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

# Express 어댑터

> Dodo Payments를 Express App Router 프로젝트에 통합하는 방법을 배우세요. 체크아웃, 고객 포털, 웹훅 및 보안 환경 설정을 다룹니다.

<CardGroup cols={2}>
  <Card title="Checkout Handler" icon="cart-shopping" href="#checkout-route-handler">
    Express 앱에 Dodo Payments 체크아웃을 통합합니다.
  </Card>

  <Card title="Customer Portal" icon="user" href="#customer-portal-route-handler">
    고객이 구독 및 세부 정보를 관리할 수 있도록 허용합니다.
  </Card>

  <Card title="Webhooks" icon="bell" href="#webhook-route-handler">
    Dodo Payments 웹훅 이벤트를 수신하고 처리합니다.
  </Card>
</CardGroup>

## 설치

<Steps>
  <Step title="Install the package">
    프로젝트 루트에서 다음 명령어를 실행하세요:

    ```bash theme={null}
    npm install @dodopayments/express
    ```
  </Step>

  <Step title="Set up environment variables">
    프로젝트 루트에 <code>.env</code> 파일을 생성하세요:

    ```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>
      절대로 <code>.env</code> 파일이나 비밀 정보를 버전 관리에 커밋하지 마세요.
    </Warning>
  </Step>
</Steps>

## 라우트 핸들러 예제

<Tabs>
  <Tab title="Checkout Handler">
    <Info>
      이 핸들러를 사용하여 Express 앱에 Dodo Payments 체크아웃을 통합하세요. 정적(GET), 동적(POST), 세션(POST) 결제 흐름을 모두 지원합니다.
    </Info>

    <CodeGroup>
      ```typescript Express Route Handler expandable theme={null}
      import { checkoutHandler } from '@dodopayments/express';

      app.get('/api/checkout', checkoutHandler({
          bearerToken: process.env.DODO_PAYMENTS_API_KEY,
          returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
          environment: process.env.DODO_PAYMENTS_ENVIRONMENT,
          type: "static"
      }))

      app.post('/api/checkout', checkoutHandler({
          bearerToken: process.env.DODO_PAYMENTS_API_KEY,
          returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
          environment: process.env.DODO_PAYMENTS_ENVIRONMENT,
          type: "dynamic"
      }))

      app.post('/api/checkout', checkoutHandler({
          bearerToken: process.env.DODO_PAYMENTS_API_KEY,
          returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
          environment: process.env.DODO_PAYMENTS_ENVIRONMENT,
          type: "session"
      }))
      ```
    </CodeGroup>

    <CodeGroup>
      ```curl Static Checkout Curl example expandable theme={null}
      curl --request GET \
      --url 'https://example.com/api/checkout?productId=pdt_fqJhl7pxKWiLhwQR042rh' \
      --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_QMDuvLkbVzCRWRQjLNcs",
        "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_QMDuvLkbVzCRWRQjLNcs",
          "quantity": 1
        }
      ],
      "customer": {
        "email": "test@example.com",
        "name": "test"
      },
      "return_url": "https://example.com/success"
      }'
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Customer Portal Handler">
    <Info>
      이 핸들러를 사용하여 고객이 Dodo Payments 고객 포털을 통해 구독 및 세부 정보를 관리하도록 허용하세요.
    </Info>

    <CodeGroup>
      ```typescript Express Route Handler expandable theme={null}
      import { CustomerPortal } from "@dodopayments/express";

      app.get('/api/customer-portal', CustomerPortal({
          bearerToken: process.env.DODO_PAYMENTS_API_KEY,
          environment: process.env.DODO_PAYMENTS_ENVIRONMENT,
      }))
      ```
    </CodeGroup>

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

  <Tab title="Webhook Handler">
    <Info>
      이 핸들러를 사용하여 Express 앱에서 Dodo Payments 웹훅 이벤트를 안전하게 수신하고 처리하세요.
    </Info>

    <CodeGroup>
      ```typescript Express Route Handler expandable theme={null}
      import { Webhooks } from "@dodopayments/express";

      app.post('/api/webhook',Webhooks({
          webhookKey: process.env.DODO_PAYMENTS_WEBHOOK_KEY,
          onPayload: async (payload) => {
              // handle the payload
          },
          // ... other event handlers for granular control
      }))
      ```
    </CodeGroup>
  </Tab>
</Tabs>

## 체크아웃 라우트 핸들러

<Info>
  Dodo Payments는 웹사이트에 결제를 통합하기 위한 세 가지 유형의 결제 흐름을 지원하며, 이 어댑터는 모든 결제 흐름을 지원합니다.
</Info>

* **정적 결제 링크:** 빠르고 코드 없이 결제를 수집할 수 있는 즉시 공유 가능한 URL입니다.
* **동적 결제 링크:** API 또는 SDK를 사용하여 사용자 정의 세부정보로 결제 링크를 프로그래밍 방식으로 생성합니다.
* **체크아웃 세션:** 미리 구성된 제품 장바구니 및 고객 세부정보로 안전하고 사용자 정의 가능한 체크아웃 경험을 생성합니다.

<AccordionGroup>
  <Accordion title="Static Checkout (GET)">
    ### 지원되는 쿼리 매개변수

    <ParamField query="productId" type="string" required>
      제품 식별자(예: <code>?productId=pdt\_nZuwz45WAs64n3l07zpQR</code>).
    </ParamField>

    <ParamField query="quantity" type="integer">
      제품 수량.
    </ParamField>

    <ParamField query="fullName" type="string">
      고객의 전체 이름.
    </ParamField>

    <ParamField query="firstName" type="string">
      고객의 이름.
    </ParamField>

    <ParamField query="lastName" type="string">
      고객의 성.
    </ParamField>

    <ParamField query="email" type="string">
      고객의 이메일 주소.
    </ParamField>

    <ParamField query="country" type="string">
      고객의 국가.
    </ParamField>

    <ParamField query="addressLine" type="string">
      고객의 주소 라인.
    </ParamField>

    <ParamField query="city" type="string">
      고객의 도시.
    </ParamField>

    <ParamField query="state" type="string">
      고객의 주/도.
    </ParamField>

    <ParamField query="zipCode" type="string">
      고객의 우편번호.
    </ParamField>

    <ParamField query="disableFullName" type="boolean">
      전체 이름 필드를 비활성화합니다.
    </ParamField>

    <ParamField query="disableFirstName" type="boolean">
      이름 필드를 비활성화합니다.
    </ParamField>

    <ParamField query="disableLastName" type="boolean">
      성 필드를 비활성화합니다.
    </ParamField>

    <ParamField query="disableEmail" type="boolean">
      이메일 필드를 비활성화합니다.
    </ParamField>

    <ParamField query="disableCountry" type="boolean">
      국가 필드를 비활성화합니다.
    </ParamField>

    <ParamField query="disableAddressLine" type="boolean">
      주소 라인 필드를 비활성화합니다.
    </ParamField>

    <ParamField query="disableCity" type="boolean">
      도시 필드를 비활성화합니다.
    </ParamField>

    <ParamField query="disableState" type="boolean">
      주 필드를 비활성화합니다.
    </ParamField>

    <ParamField query="disableZipCode" type="boolean">
      우편번호 필드를 비활성화합니다.
    </ParamField>

    <ParamField query="paymentCurrency" type="string">
      결제 통화를 지정합니다(예: <code>USD</code>).
    </ParamField>

    <ParamField query="showCurrencySelector" type="boolean">
      통화 선택기를 표시합니다.
    </ParamField>

    <ParamField query="paymentAmount" type="integer">
      결제 금액을 지정합니다(예: <code>1000</code>은 \$10.00입니다).
    </ParamField>

    <ParamField query="showDiscounts" type="boolean">
      할인 필드를 표시합니다.
    </ParamField>

    <ParamField query="metadata_*" type="string">
      <code>metadata\_</code>로 시작하는 모든 쿼리 매개변수는 메타데이터로 전달됩니다.
    </ParamField>

    <Warning>
      productId가 없으면 핸들러는 400 응답을 반환합니다. 잘못된 쿼리 매개변수도 400 응답을 발생시킵니다.
    </Warning>

    ### 응답 형식

    정적 체크아웃은 체크아웃 URL이 포함된 JSON 응답을 반환합니다:

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

  <Accordion title="Dynamic Checkout (POST)">
    * POST 요청의 JSON 본문으로 파라미터를 전송하세요.
    * 일회성 결제와 정기 결제 모두를 지원합니다.
    * 지원되는 POST 본문 필드 전체 목록은 다음을 참조하세요:
      * [Request body for a One Time Payment Product](https://docs.dodopayments.com/api-reference/payments/post-payments)
      * [Request body for a Subscription Product](https://docs.dodopayments.com/api-reference/subscriptions/post-subscriptions)

    ### 응답 형식

    동적 체크아웃은 체크아웃 URL이 포함된 JSON 응답을 반환합니다:

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

  <Accordion title="Checkout Sessions (POST)">
    체크아웃 세션은 일회성 구매 및 구독 모두에 대해 완전한 커스터마이징 제어를 제공하면서 전체 결제 흐름을 처리하는 보다 안전한 호스티드 체크아웃 환경을 제공합니다.

    자세한 내용과 지원되는 필드의 전체 목록은 [체크아웃 세션 통합 가이드](https://docs.dodopayments.com/developer-resources/checkout-session)를 참조하세요.

    ### 응답 형식

    체크아웃 세션은 체크아웃 URL이 포함된 JSON 응답을 반환합니다:

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

## 고객 포털 라우트 핸들러

고객 포털 라우트 핸들러는 Dodo Payments 고객 포털을 Express 애플리케이션에 원활하게 통합할 수 있도록 합니다.

### 쿼리 매개변수

<ParamField query="customer_id" type="string" required>
  포털 세션을 위한 고객 ID(예: <code>?customer\_id=cus\_123</code>).
</ParamField>

<ParamField query="send_email" type="boolean">
  <code>true</code>로 설정하면 고객에게 포털 링크를 이메일로 전송합니다.
</ParamField>

<Warning>
  customer\_id가 없으면 400을 반환합니다.
</Warning>

## 웹훅 라우트 핸들러

* **메서드:** POST 요청만 지원됩니다. 다른 메서드는 405를 반환합니다.
* **서명 검증:** <code>webhookKey</code>를 사용하여 웹훅 서명을 검증합니다. 검증 실패 시 401을 반환합니다.
* **페이로드 검증:** Zod로 검증됩니다. 잘못된 페이로드에 대해 400을 반환합니다.
* **오류 처리:**
  * 401: 잘못된 서명
  * 400: 잘못된 페이로드
  * 500: 검증 중 내부 오류
* **이벤트 라우팅:** 페이로드 유형에 따라 적절한 이벤트 핸들러를 호출합니다.

### 지원되는 웹훅 이벤트 핸들러

<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>;
  onLicenseKeyCreated?: (payload: WebhookPayload) => Promise<void>;
  ```
</CodeGroup>

***

## LLM에 대한 프롬프트

```
당신은 Express.js 개발자 보조 전문가입니다. 당신의 임무는 사용자가 기존 Express.js 프로젝트에 @dodopayments/express 어댑터를 통합하는 과정을 안내하는 것입니다.

@dodopayments/express 어댑터는 Dodo Payments의 체크아웃, 고객 포털 및 웹훅 기능을 위한 라우트 핸들러를 제공하며, Express 앱에 직접 연결되도록 설계되었습니다.

먼저 필요한 패키지를 설치하세요. 사용자의 프로젝트에 적합한 패키지 관리자를 사용하세요 (npm, yarn 또는 bun):

npm install @dodopayments/express

---

응답을 다음과 같이 구조화해야 합니다:

1. 사용자가 통합하고자 하는 기능을 물어보세요.

"@dodopayments/express 어댑터의 어떤 부분을 프로젝트에 통합하고 싶으신가요? 다음 중 하나 또는 여러 개를 선택할 수 있습니다:

- 체크아웃 라우트 핸들러 (제품 체크아웃 처리용)
- 고객 포털 라우트 핸들러 (고객 구독/세부 정보 관리용)
- 웹훅 라우트 핸들러 (Dodo Payments 웹훅 이벤트 수신용)
- 모두 (세 가지 모두 통합)"

---

2. 사용자의 선택에 따라 각 선택된 기능에 대한 자세한 통합 단계를 제공합니다.

---

**체크아웃 라우트 핸들러가 선택된 경우:**

**목적**: 이 핸들러는 다양한 유형의 체크아웃 흐름을 관리합니다. 모든 체크아웃 유형(정적, 동적 및 세션)은 프로그래밍 방식으로 처리할 수 있는 체크아웃 URL이 포함된 JSON 응답을 반환합니다.

**통합**:
정적 (GET), 동적 (POST) 및 체크아웃 세션 (POST)에 대한 라우트를 Express 앱에 생성하세요.

import { checkoutHandler } from '@dodopayments/express';

app.get('/api/checkout', checkoutHandler({
  bearerToken: process.env.DODO_PAYMENTS_API_KEY,
  returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
  environment: process.env.DODO_PAYMENTS_ENVIRONMENT,
  type: "static"
}));

app.post('/api/checkout', checkoutHandler({
  bearerToken: process.env.DODO_PAYMENTS_API_KEY,
  returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
  environment: process.env.DODO_PAYMENTS_ENVIRONMENT,
  type: "dynamic"
}));

// 체크아웃 세션의 경우
app.post('/api/checkout', checkoutHandler({
  bearerToken: process.env.DODO_PAYMENTS_API_KEY,
  returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
  environment: process.env.DODO_PAYMENTS_ENVIRONMENT,
  type: "session"
}));

구성 옵션:

    bearerToken: Dodo Payments API 키 (DODO_PAYMENTS_API_KEY 환경 변수에 저장하는 것이 권장됩니다).

    returnUrl (선택 사항): 체크아웃 성공 후 사용자를 리디렉션할 URL입니다.

    environment: "test_mode" 또는 "live_mode"

    type: "static" (GET), "dynamic" (POST), 또는 "session" (POST)

GET (정적 체크아웃)은 쿼리 매개변수를 기대합니다:

    productId (필수)

    수량, 고객 필드 (fullName, email 등) 및 메타데이터 (metadata_*)는 선택 사항입니다.

    반환: {"checkout_url": "https://checkout.dodopayments.com/..."}

POST (동적 체크아웃)은 결제 세부정보가 포함된 JSON 본문을 기대합니다 (일회성 또는 구독). 전체 POST 스키마에 대한 문서는 다음을 참조하세요:

    일회성 결제: https://docs.dodopayments.com/api-reference/payments/post-payments

    구독: https://docs.dodopayments.com/api-reference/subscriptions/post-subscriptions

    반환: {"checkout_url": "https://checkout.dodopayments.com/..."}

POST (체크아웃 세션) - (권장) 보다 사용자 정의 가능한 체크아웃 경험. JSON으로 체크아웃_url을 반환합니다: 매개변수는 JSON 본문으로 전송됩니다. 일회성 및 정기 결제를 모두 지원합니다. 반환: {"checkout_url": "https://checkout.dodopayments.com/session/..."}. 지원되는 필드의 전체 목록은 다음을 참조하세요:

  체크아웃 세션 통합 가이드: https://docs.dodopayments.com/developer-resources/checkout-session

고객 포털 라우트 핸들러가 선택된 경우:

목적: 이 라우트는 고객이 Dodo Payments 포털을 통해 구독을 관리할 수 있도록 합니다.

통합:

import { CustomerPortal } from "@dodopayments/express";

app.get('/api/customer-portal', CustomerPortal({
  bearerToken: process.env.DODO_PAYMENTS_API_KEY,
  environment: process.env.DODO_PAYMENTS_ENVIRONMENT,
}));

쿼리 매개변수:

    customer_id (필수): 예: ?customer_id=cus_123

    send_email (선택 사항): true인 경우 고객에게 포털 링크가 이메일로 전송됩니다.

customer_id가 누락된 경우 400을 반환합니다.

웹훅 라우트 핸들러가 선택된 경우:

목적: Dodo Payments로부터 수신한 웹훅 이벤트를 처리하여 앱에서 이벤트를 트리거합니다.

통합:

import { Webhooks } from "@dodopayments/express";

app.post('/api/webhook', Webhooks({
  webhookKey: process.env.DODO_PAYMENTS_WEBHOOK_KEY,
  onPayload: async (payload) => {
    // 일반 페이로드 처리
  },
  // 아래 각 이벤트 유형에 대한 세부 핸들러를 제공할 수도 있습니다.
}));

기능:

    POST 메서드만 허용됩니다 — 다른 메서드는 405를 반환합니다.

    서명 검증은 webhookKey를 사용하여 수행됩니다. 잘못된 경우 401을 반환합니다.

    Zod 기반 페이로드 검증. 잘못된 스키마인 경우 400을 반환합니다.

    모든 핸들러는 비동기 함수입니다.

지원되는 웹훅 이벤트 핸들러:

다음 핸들러 중 하나를 전달할 수 있습니다:

    onPayload

    onPaymentSucceeded

    onPaymentFailed

    onPaymentProcessing

    onPaymentCancelled

    onRefundSucceeded

    onRefundFailed

    onDisputeOpened, onDisputeExpired, onDisputeAccepted, onDisputeCancelled, onDisputeChallenged, onDisputeWon, onDisputeLost

    onSubscriptionActive, onSubscriptionOnHold, onSubscriptionRenewed, onSubscriptionPlanChanged, onSubscriptionCancelled, onSubscriptionFailed, onSubscriptionExpired

    onLicenseKeyCreated

환경 변수 설정:

프로젝트에 다음 환경 변수를 정의해야 합니다:

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

코드 내에서 다음과 같이 사용하세요:

process.env.DODO_PAYMENTS_API_KEY
process.env.DODO_PAYMENTS_WEBHOOK_SECRET

보안 주의: 비밀 정보를 버전 관리에 커밋하지 마세요. 로컬에서는 .env 파일을 사용하고 배포 환경에서는 비밀 관리자를 사용하세요 (예: AWS, Vercel, Heroku 등).

코드 내부에서 다음과 같이 사용하세요:

process.env.DODO_PAYMENTS_API_KEY
process.env.DODO_PAYMENTS_WEBHOOK_SECRET

보안 주의: 비밀 정보를 버전 관리에 커밋하지 마세요. 로컬에서는 .env 파일을 사용하고 배포 환경에서는 비밀 관리자(e.g., AWS, Vercel, Heroku 등)를 사용하세요.
```
