> ## 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.

# Bun Adaptor

> Pelajari cara mengintegrasikan <CardGroup cols={2}> dengan proyek server Bun Anda menggunakan Bun Adaptor kami. Mencakup checkout, portal pelanggan, webhook, dan penyiapan environment yang aman.

<CardGroup cols={2}>
  <Card title="Checkout Handler" icon="cart-shopping" href="#checkout-route-handler">
    Integrasikan checkout Dodo Payments ke server Bun Anda.
  </Card>

  <Card title="Customer Portal" icon="user" href="#customer-portal-route-handler">
    Izinkan pelanggan mengelola langganan dan detail mereka.
  </Card>

  <Card title="Webhooks" icon="bell" href="#webhook-route-handler">
    Terima dan proses event webhook Dodo Payments.
  </Card>
</CardGroup>

## Instalasi

<Steps>
  <Step title="Install the package">
    Jalankan perintah berikut di root proyek Anda:

    ```bash theme={null}
    bun add @dodopayments/bun
    ```
  </Step>

  <Step title="Set up environment variables">
    Buat file <code>.env</code> di root proyek Anda:

    ```env expandable theme={null}
    DODO_PAYMENTS_API_KEY=your-api-key
    DODO_PAYMENTS_WEBHOOK_KEY=your-webhook-secret
    DODO_PAYMENTS_ENVIRONMENT="test_mode" or "live_mode"
    DODO_PAYMENTS_RETURN_URL=your-return-url
    ```

    <Warning>
      Jangan pernah commit file <code>.env</code> atau secrets Anda ke version control.
    </Warning>
  </Step>
</Steps>

## Contoh Route Handler

<Info>
  Semua contoh mengasumsikan Anda menggunakan server native Bun dengan <code>Bun.serve()</code>.
</Info>

<Tabs>
  <Tab title="Checkout Handler">
    <Info>
      Gunakan handler ini untuk mengintegrasikan checkout Dodo Payments ke server Bun Anda. Mendukung alur pembayaran statis (GET), dinamis (POST), dan sesi (POST).
    </Info>

    <CodeGroup>
      ```typescript Bun Server Handler expandable theme={null}
      import { Checkout } from '@dodopayments/bun';

      const staticCheckoutHandler = Checkout({
          bearerToken: process.env.DODO_PAYMENTS_API_KEY,
          returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
          environment: process.env.DODO_PAYMENTS_ENVIRONMENT,
          type: "static"
      });

      const sessionCheckoutHandler = Checkout({
          bearerToken: process.env.DODO_PAYMENTS_API_KEY,
          returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
          environment: process.env.DODO_PAYMENTS_ENVIRONMENT,
          type: "session"
      });

      const dynamicCheckoutHandler = Checkout({
          bearerToken: process.env.DODO_PAYMENTS_API_KEY,
          returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
          environment: process.env.DODO_PAYMENTS_ENVIRONMENT,
          type: "dynamic"
      });

      Bun.serve({
        port: 3000,
        fetch(request) {
          const url = new URL(request.url);
          
          if (url.pathname === "/api/checkout") {
            if (request.method === "GET") {
              return staticCheckoutHandler(request);
            }
            if (request.method === "POST") {
              return sessionCheckoutHandler(request);
              // or return dynamicCheckoutHandler(request);
            }
          }
          
          return new Response("Not Found", { status: 404 });
        },
      });
      ```
    </CodeGroup>

    <CodeGroup>
      ```curl Static Checkout Curl example expandable theme={null}
      curl --request GET \
      --url 'https://example.com/api/checkout?productId=pdt_xxx' \
      --header 'User-Agent: insomnia/11.2.0' \
      --cookie mode=test
      ```
    </CodeGroup>

    <CodeGroup>
      ```curl Dynamic Checkout Curl example expandable theme={null}
      curl --request POST \
      --url https://example.com/api/checkout \
      --header 'Content-Type: application/json' \
      --header 'User-Agent: insomnia/11.2.0' \
      --cookie mode=test \
      --data '{
      "billing": {
        "city": "Texas",
        "country": "US",
        "state": "Texas",
        "street": "56, hhh",
        "zipcode": "560000"
      },
      "customer": {
        "email": "test@example.com",
        	"name": "test"
      },
      "metadata": {},
      "payment_link": true,
        "product_id": "pdt_xxx",
        "quantity": 1,
        "billing_currency": "USD",
        "discount_codes": ["IKHZ23M9GQ"],
        "return_url": "https://example.com",
        "trial_period_days": 10
      }'
      ```
    </CodeGroup>

    <CodeGroup>
      ```curl Checkout Session Curl example expandable theme={null}
      curl --request POST \
      --url https://example.com/api/checkout \
      --header 'Content-Type: application/json' \
      --header 'User-Agent: insomnia/11.2.0' \
      --cookie mode=test \
      --data '{
      "product_cart": [
        {
          "product_id": "pdt_xxx",
          "quantity": 1
        }
      ],
      "customer": {
        "email": "test@example.com",
        "name": "test"
      },
      "return_url": "https://example.com/success"
      }'
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Customer Portal Handler">
    <Info>
      Gunakan handler ini agar pelanggan dapat mengelola langganan dan detail mereka melalui customer portal Dodo Payments.
    </Info>

    <CodeGroup>
      ```typescript Bun Server Handler expandable theme={null}
      import { CustomerPortal } from "@dodopayments/bun";

      const customerPortalHandler = CustomerPortal({
        bearerToken: process.env.DODO_PAYMENTS_API_KEY,
        environment: process.env.DODO_PAYMENTS_ENVIRONMENT,
      });

      Bun.serve({
        port: 3000,
        fetch(request) {
          const url = new URL(request.url);
          
          if (url.pathname === "/api/customer-portal" && request.method === "GET") {
            return customerPortalHandler(request);
          }
          
          return new Response("Not Found", { status: 404 });
        },
      });
      ```
    </CodeGroup>

    <CodeGroup>
      ```curl Customer Portal Curl example expandable theme={null}
      curl --request GET \
      --url 'https://example.com/api/customer-portal?customer_id=cus_123&send_email=true' \
      --header 'User-Agent: insomnia/11.2.0' \
      --cookie mode=test
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Webhook Handler">
    <Info>
      Gunakan handler ini untuk menerima dan memproses event webhook Dodo Payments dengan aman di server Bun Anda.
    </Info>

    <CodeGroup>
      ```typescript Bun Server Handler expandable theme={null}
      import { Webhooks } from "@dodopayments/bun";

      const webhookHandler = Webhooks({
        webhookKey: process.env.DODO_PAYMENTS_WEBHOOK_KEY,
        onPayload: async (payload) => {
          // handle the payload
        },
        // ... other event handlers for granular control
      });

      Bun.serve({
        port: 3000,
        fetch(request) {
          const url = new URL(request.url);
          
          if (url.pathname === "/api/webhook/dodo-payments" && request.method === "POST") {
            return webhookHandler(request);
          }
          
          return new Response("Not Found", { status: 404 });
        },
      });
      ```
    </CodeGroup>
  </Tab>
</Tabs>

## Checkout Route Handler

<Info>
  Dodo Payments mendukung tiga jenis alur pembayaran untuk mengintegrasikan pembayaran ke situs web Anda; adaptor ini mendukung semua jenis alur pembayaran.
</Info>

* **Payment Links Statis:** URL yang dapat langsung dibagikan untuk pengumpulan pembayaran cepat tanpa kode.
* **Payment Links Dinamis:** Buat payment links secara terprogram dengan detail khusus menggunakan API atau SDK.
* **Checkout Sessions:** Buat pengalaman checkout yang aman dan dapat disesuaikan dengan keranjang produk serta detail pelanggan yang telah dikonfigurasi sebelumnya.

<AccordionGroup>
  <Accordion title="Static Checkout (GET)">
    ### Query Parameters yang Didukung

    <ParamField query="productId" type="string" required>
      Identifier produk (misalnya, <code>?productId=pdt\_xxx</code>).
    </ParamField>

    <ParamField query="quantity" type="integer">
      Jumlah produk.
    </ParamField>

    <ParamField query="fullName" type="string">
      Nama lengkap pelanggan.
    </ParamField>

    <ParamField query="firstName" type="string">
      Nama depan pelanggan.
    </ParamField>

    <ParamField query="lastName" type="string">
      Nama belakang pelanggan.
    </ParamField>

    <ParamField query="email" type="string">
      Alamat email pelanggan.
    </ParamField>

    <ParamField query="country" type="string">
      Negara pelanggan.
    </ParamField>

    <ParamField query="addressLine" type="string">
      Baris alamat pelanggan.
    </ParamField>

    <ParamField query="city" type="string">
      Kota pelanggan.
    </ParamField>

    <ParamField query="state" type="string">
      Negara bagian/provinsi pelanggan.
    </ParamField>

    <ParamField query="zipCode" type="string">
      Kode pos pelanggan.
    </ParamField>

    <ParamField query="disableFullName" type="boolean">
      Nonaktifkan field nama lengkap.
    </ParamField>

    <ParamField query="disableFirstName" type="boolean">
      Nonaktifkan field nama depan.
    </ParamField>

    <ParamField query="disableLastName" type="boolean">
      Nonaktifkan field nama belakang.
    </ParamField>

    <ParamField query="disableEmail" type="boolean">
      Nonaktifkan field email.
    </ParamField>

    <ParamField query="disableCountry" type="boolean">
      Nonaktifkan field negara.
    </ParamField>

    <ParamField query="disableAddressLine" type="boolean">
      Nonaktifkan field baris alamat.
    </ParamField>

    <ParamField query="disableCity" type="boolean">
      Nonaktifkan field kota.
    </ParamField>

    <ParamField query="disableState" type="boolean">
      Nonaktifkan field negara bagian.
    </ParamField>

    <ParamField query="disableZipCode" type="boolean">
      Nonaktifkan field kode pos.
    </ParamField>

    <ParamField query="paymentCurrency" type="string">
      Tentukan mata uang pembayaran (misalnya, <code>USD</code>).
    </ParamField>

    <ParamField query="showCurrencySelector" type="boolean">
      Tampilkan pemilih mata uang.
    </ParamField>

    <ParamField query="paymentAmount" type="integer">
      Tentukan jumlah pembayaran (misalnya, <code>1000</code> untuk \$10.00).
    </ParamField>

    <ParamField query="showDiscounts" type="boolean">
      Tampilkan field diskon.
    </ParamField>

    <ParamField query="metadata_*" type="string">
      Query parameter apa pun yang diawali <code>metadata\_</code> akan diteruskan sebagai metadata.
    </ParamField>

    <Warning>
      Jika <code>productId</code> tidak ada, handler mengembalikan respons 400. Query parameters yang tidak valid juga menghasilkan respons 400.
    </Warning>

    ### Format Respons

    Checkout statis mengembalikan respons JSON dengan URL checkout:

    ```json theme={null}
    {
      "checkout_url": "https://checkout.dodopayments.com/..."
    }
    ```
  </Accordion>

  <Accordion title="Dynamic Checkout (POST)">
    * Kirim parameter sebagai body JSON dalam request POST.
    * Mendukung pembayaran satu kali dan berulang.
    * Untuk daftar lengkap field body POST yang didukung, lihat:
      * [Request body untuk One Time Payment Product](https://docs.dodopayments.com/api-reference/payments/post-payments)
      * [Request body untuk Subscription Product](https://docs.dodopayments.com/api-reference/subscriptions/post-subscriptions)

    ### Format Respons

    Checkout dinamis mengembalikan respons JSON dengan URL checkout:

    ```json theme={null}
    {
      "checkout_url": "https://checkout.dodopayments.com/..."
    }
    ```
  </Accordion>

  <Accordion title="Checkout Sessions (POST)">
    Checkout sessions menyediakan pengalaman checkout hosted yang lebih aman dan menangani alur pembayaran lengkap untuk pembelian satu kali maupun langganan dengan kontrol kustomisasi penuh.

    Lihat [Panduan Integrasi Checkout Sessions](https://docs.dodopayments.com/developer-resources/checkout-session) untuk detail lebih lanjut dan daftar lengkap field yang didukung.

    ### Format Respons

    Checkout sessions mengembalikan respons JSON dengan URL checkout:

    ```json theme={null}
    {
      "checkout_url": "https://checkout.dodopayments.com/session/..."
    }
    ```
  </Accordion>
</AccordionGroup>

## Customer Portal Route Handler

Customer Portal Route Handler memungkinkan Anda mengintegrasikan customer portal Dodo Payments ke aplikasi server Bun Anda dengan lancar.

### Query Parameters

<ParamField query="customer_id" type="string" required>
  ID pelanggan untuk sesi portal (misalnya, <code>?customer\_id=cus\_123</code>).
</ParamField>

<ParamField query="send_email" type="boolean">
  Jika diatur ke <code>true</code>, email berisi tautan portal akan dikirim kepada pelanggan.
</ParamField>

<Warning>
  Mengembalikan 400 jika <code>customer\_id</code> tidak ada.
</Warning>

## Webhook Route Handler

* **Method:** Hanya request POST yang didukung. Method lain mengembalikan 405.
* **Signature Verification:** Memverifikasi signature webhook menggunakan <code>webhookKey</code>. Mengembalikan 401 jika verifikasi gagal.
* **Payload Validation:** Divalidasi dengan Zod. Mengembalikan 400 untuk payload yang tidak valid.
* **Error Handling:**
  * 401: Signature tidak valid
  * 400: Payload tidak valid
  * 500: Error internal selama verifikasi
* **Event Routing:** Memanggil event handler yang sesuai berdasarkan tipe payload.

### Event Handler Webhook yang Didukung

<CodeGroup>
  ```typescript Typescript expandable theme={null}
  onPayload?: (payload: WebhookPayload) => Promise<void>;
  onPaymentSucceeded?: (payload: WebhookPayload) => Promise<void>;
  onPaymentFailed?: (payload: WebhookPayload) => Promise<void>;
  onPaymentProcessing?: (payload: WebhookPayload) => Promise<void>;
  onPaymentCancelled?: (payload: WebhookPayload) => Promise<void>;
  onRefundSucceeded?: (payload: WebhookPayload) => Promise<void>;
  onRefundFailed?: (payload: WebhookPayload) => Promise<void>;
  onDisputeOpened?: (payload: WebhookPayload) => Promise<void>;
  onDisputeExpired?: (payload: WebhookPayload) => Promise<void>;
  onDisputeAccepted?: (payload: WebhookPayload) => Promise<void>;
  onDisputeCancelled?: (payload: WebhookPayload) => Promise<void>;
  onDisputeChallenged?: (payload: WebhookPayload) => Promise<void>;
  onDisputeWon?: (payload: WebhookPayload) => Promise<void>;
  onDisputeLost?: (payload: WebhookPayload) => Promise<void>;
  onSubscriptionActive?: (payload: WebhookPayload) => Promise<void>;
  onSubscriptionOnHold?: (payload: WebhookPayload) => Promise<void>;
  onSubscriptionRenewed?: (payload: WebhookPayload) => Promise<void>;
  onSubscriptionPlanChanged?: (payload: WebhookPayload) => Promise<void>;
  onSubscriptionCancelled?: (payload: WebhookPayload) => Promise<void>;
  onSubscriptionFailed?: (payload: WebhookPayload) => Promise<void>;
  onSubscriptionExpired?: (payload: WebhookPayload) => Promise<void>;
  onSubscriptionUpdated?: (payload: WebhookPayload) => Promise<void>;
  onLicenseKeyCreated?: (payload: WebhookPayload) => Promise<void>;
  onAbandonedCheckoutDetected?: (payload: WebhookPayload) => Promise<void>;
  onAbandonedCheckoutRecovered?: (payload: WebhookPayload) => Promise<void>;
  onDunningStarted?: (payload: WebhookPayload) => Promise<void>;
  onDunningRecovered?: (payload: WebhookPayload) => Promise<void>;
  onCreditAdded?: (payload: WebhookPayload) => Promise<void>;
  onCreditDeducted?: (payload: WebhookPayload) => Promise<void>;
  onCreditExpired?: (payload: WebhookPayload) => Promise<void>;
  onCreditRolledOver?: (payload: WebhookPayload) => Promise<void>;
  onCreditRolloverForfeited?: (payload: WebhookPayload) => Promise<void>;
  onCreditOverageCharged?: (payload: WebhookPayload) => Promise<void>;
  onCreditManualAdjustment?: (payload: WebhookPayload) => Promise<void>;
  onCreditBalanceLow?: (payload: WebhookPayload) => Promise<void>;
  ```
</CodeGroup>

***

## Prompt untuk LLM

````

You are an expert Bun developer assistant. Your task is to guide a user through integrating the @dodopayments/bun adapter into their existing Bun project.

The @dodopayments/bun adapter provides handlers for Dodo Payments' Checkout, Customer Portal, and Webhook functionalities, designed for Bun's native server with Bun.serve().

First, install the necessary package:

bun add @dodopayments/bun

Here's how you should structure your response:

    Ask the user which functionalities they want to integrate.

"Which parts of the @dodopayments/bun adapter would you like to integrate into your project? You can choose one or more of the following:
1. Checkout (static, dynamic, or session-based)
2. Customer Portal
3. Webhooks"

    Based on the user's selection, provide step-by-step integration instructions.

For each selected functionality, show:

    The environment variables required
    The exact code to add to their server file
    Where to place the code in their Bun.serve() configuration

Provide complete, working examples that the user can copy and paste directly into their project.

    Environment Variables Setup

Always include instructions for setting up the .env file:

DODO_PAYMENTS_API_KEY=your-api-key
DODO_PAYMENTS_WEBHOOK_KEY=your-webhook-secret
DODO_PAYMENTS_ENVIRONMENT="test_mode" or "live_mode"
DODO_PAYMENTS_RETURN_URL=your-return-url

Remind the user to never commit their .env file to version control.

    For Checkout Integration

If the user selects Checkout, ask which type they need:

    Static checkout (GET requests with query parameters)
    Dynamic checkout (POST requests with JSON body)
    Checkout sessions (POST requests with product cart)

Provide the appropriate handler code for their selection.

    For Customer Portal Integration

Provide a complete example showing how to integrate the customer portal handler into their Bun server.

    For Webhook Integration

Provide a complete example showing how to integrate the webhook handler with available event handlers for granular control.

    Full Example

If the user wants to integrate all three functionalities, provide a complete Bun server example that combines all handlers.

```typescript
import { Checkout, CustomerPortal, Webhooks } from "@dodopayments/bun";

const staticCheckoutHandler = Checkout({
  bearerToken: process.env.DODO_PAYMENTS_API_KEY,
  returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
  environment: process.env.DODO_PAYMENTS_ENVIRONMENT,
  type: "static",
});

const sessionCheckoutHandler = Checkout({
  bearerToken: process.env.DODO_PAYMENTS_API_KEY,
  returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
  environment: process.env.DODO_PAYMENTS_ENVIRONMENT,
  type: "session",
});

const customerPortalHandler = CustomerPortal({
  bearerToken: process.env.DODO_PAYMENTS_API_KEY,
  environment: process.env.DODO_PAYMENTS_ENVIRONMENT,
});

const webhookHandler = Webhooks({
  webhookKey: process.env.DODO_PAYMENTS_WEBHOOK_KEY,
  onPaymentSucceeded: async (payload) => {
    console.log("Payment succeeded:", payload);
    // Your business logic here
  },
  onSubscriptionActive: async (payload) => {
    console.log("Subscription activated:", payload);
    // Your business logic here
  },
});

Bun.serve({
  port: 3000,
  fetch(request) {
    const url = new URL(request.url);
    
    // Checkout routes
    if (url.pathname === "/api/checkout") {
      if (request.method === "GET") {
        return staticCheckoutHandler(request);
      }
      if (request.method === "POST") {
        return sessionCheckoutHandler(request);
      }
    }
    
    // Customer portal route
    if (url.pathname === "/api/customer-portal" && request.method === "GET") {
      return customerPortalHandler(request);
    }
    
    // Webhook route
    if (url.pathname === "/api/webhook/dodo-payments" && request.method === "POST") {
      return webhookHandler(request);
    }
    
    return new Response("Not Found", { status: 404 });
  },
});

console.log("Server running on http://localhost:3000");

Additional Guidance:
• Explain that the handlers are Bun-native and work seamlessly with Bun.serve()
• Highlight the use of Web Standard Request and Response objects
• Mention that error handling is built-in and returns appropriate HTTP status codes
• Provide links to the Dodo Payments documentation for more details
````
