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

# Card binding and token management

> Securely convert users’ bank card information into a token so future payments don’t require entering the card number again.

Securely convert users’ bank card information into a token, so there is no need to enter the card number again for subsequent payments.

<Info>
  This flow spans both the **server-side** (calling the Generate / Inquiry / Remove APIs) and the **frontend** (submitting card information via `@waffo/payment-sdk`).
</Info>

## Core flow

```text theme={null}
1. Merchant backend → Call Generate API → Get tokenSessionId
2. Merchant frontend → Pass plaintext card data to @waffo/payment-sdk → The SDK encrypts and submits it
3. Binding result → Get a tokenId with the initial UNVERIFIED status (3DS verification may be required)
4. Card verification → Complete a successful CIT → Token status becomes VERIFIED
5. Subsequent payments → Pass tokenId as paymentInfo.userPaymentAccessToken in order/create
```

## Card binding flow

<Steps>
  <Step title="Merchant backend calls the Generate API">
    Call [POST /api/v1/tokenization/generate](/docs/api-reference/waffo-tokenization-api/tokenization-generate), passing parameters such as `tokenRequestId`, `merchantUserId`, and `tokenType: "CARD"`. On success, it returns `tokenSessionId`.
  </Step>

  <Step title="Frontend submits card information">
    Use the `tokenizationSubmit` method of `@waffo/payment-sdk` to encrypt the card data and submit it to the Waffo server:

    ```typescript theme={null}
    import WaffoSDK from '@waffo/payment-sdk';

    const sdk = new WaffoSDK('your-client-api-key', {
      env: 'prod',
      locale: 'en'
    });

    const result = await sdk.tokenizationSubmit('tokenSessionId', {
      tokenData: {
        pan: '4111111111111111',
        name: 'John Doe',
        expiry: '12/2028',    // MM/YYYY
        cvv: '123'            // optional
      },
      billingAddress: {        // optional
        countryCode: 'USA',
        region: 'CA',
        city: 'San Francisco',
        postalCode: '94102',
        address: '123 Main St'
      }
    });
    ```

    The merchant frontend passes plaintext card data to the SDK, which encrypts the data before transmission. With the frontend SDK binding flow, the merchant does not need PCI DSS certification as long as its backend neither retains nor transmits plaintext card data.
  </Step>

  <Step title="Handle the card binding result">
    ```typescript theme={null}
    if (result.success) {
      const { tokenRequestId, validateUrl } = result.data;

      if (validateUrl) {
        // Redirect the user for 3DS verification
        window.location.href = validateUrl;
      } else {
        // Card binding succeeded; wait for the Webhook to obtain tokenId
      }
    }
    ```

    If the Generate request includes `notifyUrl`, Waffo sends `TOKENIZATION_NOTIFICATION` when card binding completes, the Token status changes, or the card summary is updated. Process notifications idempotently using `result.tokenId`, and store the latest `tokenStatus` and Token data. The notification is not limited to the initial binding result.
  </Step>
</Steps>

## Token status

After card binding succeeds, the Token starts in `UNVERIFIED`. You must complete a successful CIT (cardholder-initiated transaction) before its status changes to `VERIFIED`. The verification transaction can be a separate [`ONE_TIME_PAYMENT`](/docs/api-reference/order-create/create-new-order) for `0` or `0.01`, or a normal CIT payment.

```mermaid theme={null}
stateDiagram-v2
    [*] --> UNVERIFIED: Card binding succeeds
    UNVERIFIED --> VERIFIED: CIT succeeds
    UNVERIFIED --> EXPIRED: Card expires
    VERIFIED --> EXPIRED: Card expires
    UNVERIFIED --> SUSPENDED: Waffo suspends use
    VERIFIED --> SUSPENDED: Waffo suspends use
```

* `UNVERIFIED`: The Token exists but has not completed a successful CIT. You cannot use it for scheduled or unscheduled MIT; Waffo returns `A0045`.
* `VERIFIED`: The Token has completed a successful CIT and can be used for subsequent payments.
* `EXPIRED`: The Token status becomes `EXPIRED` when the card reaches its expiry date.
* `SUSPENDED`: Waffo has suspended use of the Token. The public contract does not define the exact trigger conditions.

## Pay with a token

After obtaining `tokenId`, pass it as `paymentInfo.userPaymentAccessToken` when creating an order to replace the card number:

```typescript theme={null}
const response = await waffo.order().create({
  paymentRequestId: '...',
  merchantOrderId: 'ORDER_001',
  orderCurrency: 'HKD',
  orderAmount: '100.00',
  notifyUrl: 'https://your-site.com/webhook',
  userInfo: {
    userId: 'USER_001',  // must match the merchantUserId passed to the Generate API
  },
  paymentInfo: {
    productName: 'ONE_TIME_PAYMENT',
    userPaymentAccessToken: 'tok_xxxxxxxxxxxx',  // use the token instead of the card number
  },
  // ... other parameters
});
```

You can use `tokenId` only with Waffo's [`ONE_TIME_PAYMENT`](/docs/api-reference/order-create/create-new-order) product. Do not pass it to Waffo's `SUBSCRIPTION` product.

If you manage your own recurring billing schedule, you can repeatedly create `ONE_TIME_PAYMENT` orders after the Token becomes `VERIFIED`. MIT orders must also set `paymentInfo.merchantInitiatedMode`.

## Token API usage

| Action             | API                                                                                               | Description                                                                                |
| ------------------ | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| Start card binding | [POST /api/v1/tokenization/generate](/docs/api-reference/waffo-tokenization-api/tokenization-generate) | Create a card binding session and obtain the `tokenSessionId` required by the frontend SDK |
| Inquiry            | [POST /api/v1/tokenization/inquiry](/docs/api-reference/waffo-tokenization-api/tokenization-inquiry)   | Retrieve one Token by `tokenId`, or retrieve a user's Token list by `merchantUserId`       |
| Remove             | [POST /api/v1/tokenization/remove](/docs/api-reference/waffo-tokenization-api/tokenization-remove)     | Remove one Token by `tokenId`, or remove all Tokens for a user by `merchantUserId`         |

## Security mechanisms

* The merchant frontend passes plaintext card data to the SDK, which encrypts it before transmission; the merchant backend does not handle the plaintext card number
* All API requests and responses use SHA256WithRSA signature verification
* Supports 3DS verification to enhance payment security
