> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dodopayments.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Hak Fitur Bendera

> Mengatur fitur dalam aplikasi Anda sendiri berdasarkan pembelian. Hak fitur bendera memberikan kemampuan boolean secara instan saat pembayaran dan mencabutnya secara otomatis saat pembatalan.

<Info>
  Hak fitur bendera mengubah Dodo Payments menjadi penyimpanan fitur-bendera yang sadar-billing. Lampirkan bendera seperti `advanced_reports` ke produk, dan setiap pelanggan yang membayar mendapatkan hak yang dapat diuji aplikasi Anda melalui API atau disinkronkan dengan webhooks. Tanpa platform eksternal, tanpa OAuth, tanpa langkah pengiriman — hak itu sendiri adalah kemampuan.
</Info>

## Apa yang disampaikan

Tidak ada yang meninggalkan Dodo Payments — hak itu **adalah** yang disampaikan:

* Pada saat pembelian, hak dibuat dan langsung bergerak ke `delivered`. Tidak ada fase `pending`, tidak ada aksi pelanggan, dan tidak ada cara untuk pengiriman gagal.
* Hak membawa payload bertipe `feature`: `{ "feature_type": "boolean", "feature_id": "advanced_reports" }`. Aplikasi Anda membaca `feature_id` untuk memutuskan apa yang akan dibuka.
* Pembatalan, pengembalian dana, atau pencabutan manual memindahkan hak ke `revoked`, dan aplikasi Anda melihat bendera itu menghilang.

Penggunaan umum termasuk pembatasan fitur berdasarkan rencana (Pro membuka analitik), kemampuan tambahan (peningkatan "akses API"), dan program akses awal yang dijual sebagai pembelian satu kali.

<Note>
  `feature_id` adalah pengidentifikasi yang dipilih oleh pedagang, tidak unik di seluruh hak. Dua hak dapat memberikan `feature_id` yang sama — misalnya, rencana Pro bulanan dan tahunan keduanya memberikan `advanced_reports`.
</Note>

## Membuat fitur bendera

<Steps>
  <Step title="Open Entitlements">
    Di dashboard Dodo Payments Anda, masuk ke **Entitlements** dan klik **+** untuk memulai hak baru, lalu pilih **Feature Flags**.
  </Step>

  <Step title="Name the flag">
    Berikan **Display Name** untuk dashboard Anda, **Feature ID** yang akan diperiksa aplikasi Anda (dashboard menyarankan satu dari nama), dan **Description** agar tim Anda tahu apa yang dikendalikannya.

    <Frame caption="Creating a feature flag. The Feature ID is what your application checks; Meta Data attaches limits alongside the flag.">
      <img src="https://mintcdn.com/dodopayments/oS2MTbJuY6MeBjjs/images/entitlements/feature-flags/create.png?fit=max&auto=format&n=oS2MTbJuY6MeBjjs&q=85&s=08d8fc2cfee102ff08fd150c76b5fcf9" alt="Form Fitur Bendera Baru dengan nama tampilan, ID fitur, deskripsi, dan entri kunci-nilai metadata" style={{ maxHeight: '500px', width: 'auto' }} width="1196" height="776" data-path="images/entitlements/feature-flags/create.png" />
    </Frame>
  </Step>

  <Step title="Optionally add metadata">
    Toggle **Meta Data** untuk melampirkan konfigurasi kunci-nilai — batas, nama tier, kuota — yang disampaikan ke aplikasi Anda bersamaan dengan bendera. Lihat [Lampirkan batas dengan metadata](#attach-limits-with-metadata).
  </Step>

  <Step title="Confirm">
    Klik **Confirm**. Bendera muncul di daftar hak Anda, siap dilampirkan ke produk.

    <Frame caption="The created feature flag. The right pane tracks every customer grant issued from it.">
      <img src="https://mintcdn.com/dodopayments/oS2MTbJuY6MeBjjs/images/entitlements/feature-flags/list.png?fit=max&auto=format&n=oS2MTbJuY6MeBjjs&q=85&s=02eedcbbf7ece9f67d375c984c5515b9" alt="Dasbor Entitlements menampilkan bendera fitur Advanced Reports dengan panel aktivitas haknya" style={{ maxHeight: '500px', width: 'auto' }} width="1316" height="898" data-path="images/entitlements/feature-flags/list.png" />
    </Frame>
  </Step>
</Steps>

## Lampirkan ke produk

Buka produk (atau buat satu), temukan kartu **Entitlements**, dan klik **+** untuk melampirkan hak yang ada. Pilih fitur bendera Anda dan klik **Selesai**.

<Frame caption="Attaching the feature flag to a product. One product can deliver multiple entitlements.">
  <img src="https://mintcdn.com/dodopayments/oS2MTbJuY6MeBjjs/images/entitlements/feature-flags/attach-picker.png?fit=max&auto=format&n=oS2MTbJuY6MeBjjs&q=85&s=da2137873e285cc6ce0ce234fe899ed3" alt="Panel lampiran Entitlements dengan bendera fitur Advanced Reports dipilih" style={{ maxHeight: '500px', width: 'auto' }} width="1316" height="898" data-path="images/entitlements/feature-flags/attach-picker.png" />
</Frame>

Bendera yang dilampirkan ditampilkan pada formulir produk, dan pratinjau checkout mencantumkannya di bawah **Includes**.

<Frame caption="The product now includes the feature flag. Every successful purchase or active subscription grants it.">
  <img src="https://mintcdn.com/dodopayments/oS2MTbJuY6MeBjjs/images/entitlements/feature-flags/attach-to-product.png?fit=max&auto=format&n=oS2MTbJuY6MeBjjs&q=85&s=3556181389997edddde9c396d0ce408d" alt="Formulir produk dengan bendera fitur Advanced Reports dilampirkan di kartu Entitlements" style={{ maxHeight: '500px', width: 'auto' }} width="1316" height="898" data-path="images/entitlements/feature-flags/attach-to-product.png" />
</Frame>

## Konfigurasi yang diperlukan

| Field          | Diperlukan | Deskripsi                                                                                          |
| -------------- | ---------- | -------------------------------------------------------------------------------------------------- |
| `feature_id`   | Ya         | Pengidentifikasi yang diperiksa aplikasi Anda, mis. `advanced_reports`. Tidak unik di seluruh hak. |
| `feature_type` | Ya         | Jenis kemampuan yang diberikan. Hanya `boolean` yang didukung hari ini.                            |

### Buat melalui API

<CodeGroup>
  ```typescript TypeScript theme={null} theme={null}
  import DodoPayments from 'dodopayments';

  const client = new DodoPayments({
    bearerToken: process.env['DODO_PAYMENTS_API_KEY'],
    environment: 'test_mode',
  });

  const entitlement = await client.entitlements.create({
    name: 'Advanced Reports',
    integration_type: 'feature_flag',
    integration_config: {
      feature_type: 'boolean',
      feature_id: 'advanced_reports',
    },
    metadata: {
      tier: 'pro',
      monthly_report_limit: 100,
    },
  });
  ```

  ```python Python theme={null} theme={null}
  client.entitlements.create(
      name="Advanced Reports",
      integration_type="feature_flag",
      integration_config={
          "feature_type": "boolean",
          "feature_id": "advanced_reports",
      },
      metadata={
          "tier": "pro",
          "monthly_report_limit": 100,
      },
  )
  ```

  ```go Go theme={null} theme={null}
  client.Entitlements.New(ctx, dodopayments.EntitlementNewParams{
    Name:            dodopayments.F("Advanced Reports"),
    IntegrationType: dodopayments.F(dodopayments.EntitlementIntegrationTypeFeatureFlag),
    IntegrationConfig: dodopayments.F[dodopayments.IntegrationConfigUnionParam](
      dodopayments.IntegrationConfigFeatureFlagConfigParam{
        FeatureType: dodopayments.F(dodopayments.FeatureTypeBoolean),
        FeatureID:   dodopayments.F("advanced_reports"),
      },
    ),
  })
  ```
</CodeGroup>

***

## Lampirkan batas dengan metadata

Bendera boolean menjawab "apakah pelanggan ini memiliki fitur?". Metadata menjawab "dengan konfigurasi apa?". Metadata hak menerima nilai string, integer, angka, dan boolean, dan setiap hak mengambil **snapshot beku** dari metadata hak pada saat dibuat.

Perilaku snapshot adalah yang membuat metadata aman untuk digunakan untuk batas rencana:

* Mengedit metadata dari hak nantinya hanya mempengaruhi hak **masa depan**. Pelanggan menyimpan batas yang mereka beli.
* Snapshot dikembalikan pada setiap hak sebagai bidang `metadata`-nya, jadi satu panggilan API memberi Anda bendera dan konfigurasinya.

Misalnya, bendera `advanced_reports` dengan `{ "tier": "pro", "monthly_report_limit": 100 }` memungkinkan aplikasi Anda membuka dasbor *dan* menegakkan kuota 100 laporan tanpa pencarian kedua. Jika Anda kemudian menaikkan batas menjadi 250, pelanggan yang ada tetap pada 100 hingga mereka menerima hak baru (misalnya, setelah perubahan rencana).

<Tip>
  Gunakan metadata untuk batas dan konfigurasi; gunakan `feature_id` hanya untuk identitas. Mengencode batas dalam id (`advanced_reports_100`) memaksa bendera baru untuk setiap perubahan batas dan merusak pengecekan aplikasi Anda.
</Tip>

***

## Periksa fitur pelanggan

Daftar hak fitur-bendera yang diberikan kepada pelanggan dan buat set fitur yang diaktifkan. Endpoint mengembalikan satu baris per hak di seluruh hak, dapat difilter dengan `integration_type` dan `status`.

<CodeGroup>
  ```typescript TypeScript theme={null} theme={null}
  const features = new Map<string, Record<string, unknown>>();

  for await (const grant of client.customers.listEntitlementGrants('cus_abc123', {
    integration_type: 'feature_flag',
    status: 'Delivered',
  })) {
    if (grant.feature) {
      features.set(grant.feature.feature_id, grant.metadata ?? {});
    }
  }

  if (features.has('advanced_reports')) {
    const limit = features.get('advanced_reports')?.monthly_report_limit;
    // unlock the dashboard, enforce the limit
  }
  ```

  ```python Python theme={null} theme={null}
  page = client.customers.list_entitlement_grants(
      customer_id="cus_abc123",
      integration_type="feature_flag",
      status="Delivered",
  )

  features = {
      grant.feature.feature_id: grant.metadata
      for grant in page.items
      if grant.feature
  }

  if "advanced_reports" in features:
      limit = features["advanced_reports"].get("monthly_report_limit")
  ```

  ```go Go theme={null} theme={null}
  page, _ := client.Customers.ListEntitlementGrants(
    ctx, "cus_abc123",
    dodopayments.CustomerListEntitlementGrantsParams{
      IntegrationType: dodopayments.F("feature_flag"),
      Status:          dodopayments.F("Delivered"),
    },
  )

  features := map[string]bool{}
  for _, grant := range page.Items {
    if grant.Feature.FeatureID != "" {
      features[grant.Feature.FeatureID] = true
    }
  }
  ```
</CodeGroup>

<Note>
  Payload `feature` diisi hanya pada grant `feature_flag`; ini adalah `null` untuk setiap jenis integrasi lainnya. Lihat referensi API [List Customer Grants](/api-reference/entitlements/list-customer-grants) untuk bentuk tanggapan lengkapnya.
</Note>

Memeriksa API pada setiap permintaan menambah latensi pada jalur panas Anda. Cache set fitur per pelanggan dengan waktu hidup yang sebentar (menit, bukan jam), dan batalkan cache dari handler webhook Anda saat hak berubah status — kombinasi itu menjaga pengecekan tetap cepat dan pencabutan hampir seketika.

***

## Siklus hidup

Hak fitur bendera mengikuti [siklus hidup hak](/features/entitlements/introduction#how-grants-work) standar dengan satu penyederhanaan: tidak ada langkah pengiriman, jadi hak tidak pernah duduk di `pending` dan tidak pernah berpindah ke `failed`.

| Pemicu                                                  | Efek                                                                                             |
| ------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| Pembayaran satu kali berhasil / langganan menjadi aktif | Hak dibuat dengan `status: delivered` dan `delivered_at` diset.                                  |
| Langganan ditahan, dibatalkan, atau kedaluwarsa         | Hak dicabut dengan `revocation_reason` yang cocok.                                               |
| Pengembalian dana pada pembayaran satu kali             | Hak dicabut dengan `revocation_reason: refund`.                                                  |
| Langganan pulih (misalnya, dunning berhasil)            | Hak yang dicabut dipulihkan ke `delivered` — hak yang sama `id`, bidang pencabutan dibersihkan.  |
| Pencabutan API manual                                   | Hak dicabut dengan `revocation_reason: manual`. Tidak dipulihkan secara otomatis saat pembaruan. |

Hak adalah idempotent per hak dan pelanggan: sementara pelanggan memiliki hak untuk bendera yang tidak dicabut, pembelian ulang dan pembaruan tidak menciptakan duplikat.

***

## Webhooks

Berlangganan ke [`entitlement_grant.*` events](/developer-resources/webhooks/intents/entitlement-grant) untuk menyinkronkan bendera ke dalam database Anda sendiri daripada polling:

* `entitlement_grant.created` — tiba sudah `delivered` dengan payload `feature`. Aktifkan fitur.
* `entitlement_grant.delivered` — terpicu ketika hak yang sebelumnya dicabut dipulihkan. Aktifkan kembali fitur.
* `entitlement_grant.revoked` — akses dicabut. Nonaktifkan fitur dan periksa `revocation_reason` untuk memutuskan pesan Anda.

```typescript TypeScript theme={null} theme={null}
app.post('/webhooks/dodo', async (req, res) => {
  const event = req.body;

  if (event.type.startsWith('entitlement_grant.') && event.data.feature) {
    const { customer_id, feature } = event.data;
    const enabled = event.type !== 'entitlement_grant.revoked';

    await db.customerFeatures.upsert({
      customerId: customer_id,
      featureId: feature.feature_id,
      enabled,
      config: event.data.metadata ?? {},
    });
  }

  res.sendStatus(200);
});
```

Tidak ada `entitlement_grant.failed` untuk fitur bendera — pengiriman terjadi sepenuhnya di dalam Dodo Payments dan tidak dapat gagal.

***

## Contoh: Rencana Pro membuka laporan lanjutan

1. **Buat bendera.** `feature_id: advanced_reports` dengan metadata `{ "tier": "pro", "monthly_report_limit": 100 }`.
2. **Lampirkan itu** ke produk langganan Rencana Pro Anda.
3. **Pelanggan berlangganan.** Dodo Payments membuat grant `delivered` dan menembakkan `entitlement_grant.created`; handler webhook Anda mengaktifkan `advanced_reports` untuk pelanggan dengan batas 100.
4. **Aplikasi Anda mengatur fitur.** Pada muatan dasbor, periksa set fitur yang di-cache (atau panggil `listEntitlementGrants`) dan render tab laporan hanya ketika `advanced_reports` ada.
5. **Pelanggan membatalkan.** Dodo Payments mencabut grant dan menembakkan `entitlement_grant.revoked`; handler Anda menonaktifkan fitur. Jika pelanggan kemudian pulih melalui dunning, `entitlement_grant.delivered` memulihkannya — tidak diperlukan perubahan kode.

***

## Praktik Terbaik

* **Gunakan ID fitur snake\_case yang stabil.** Kode aplikasi Anda memeriksa string ini; mengganti namanya ialah perubahan yang memecahkan di kedua sisi.
* **Satu bendera per kemampuan.** Lebih suka `advanced_reports` + `api_access` sebagai dua hak daripada satu `pro_bundle` — pencabutan dan pencampuran rencana tetap bersih.
* **Mengemudikan status dari webhooks, verifikasi dengan API.** Webhooks menjaga database Anda tetap terkini; endpoint daftar adalah sumber kebenaran untuk pekerjaan rekonsiliasi dan cache miss.
* **Anggap `revoked` sebagai mendesak.** Bendera yang dicabut berarti pelanggan tidak lagi membayar untuk fitur tersebut. Periksa pada permintaan berikutnya, bukan sesi berikutnya.
* **Letakkan batas dalam metadata, bukan dalam kode.** Mengubah kuota kemudian hanya memerlukan pengeditan hak — pelanggan baru mengambilnya secara otomatis sementara grant yang ada tetap pada snapshot yang dibeli.
