# Instructions for AI agents - Payments integration notes

Notes for AI agents building card payment integrations with the Web JS SDK. These supplement [Instructions for AI agents](https://www.airwallex.com/docs/developer-tools/ai-agent-instructions.md), which covers general, money and test-card guidance that is not repeated here.

## Authentication

- Access tokens are short-lived. Read `expires_at` from the login response and refresh against that value rather than assuming a fixed lifetime.
- Authenticate server-side only. Never place an API key in frontend code.
- Demo is `https://api-demo.airwallex.com`, production is `https://api.airwallex.com`.

## Amounts

[Instructions for AI agents](https://www.airwallex.com/docs/developer-tools/ai-agent-instructions.md) states that money fields are not in minor units. The practical consequences for card payments:

- Send `12.50` for USD $12.50, not `1250`. Airwallex is not cents-based, so multiplying by 100 overcharges the shopper a hundredfold. Developers arriving from platforms that do use cents reach for that conversion out of habit.
- Zero-decimal currencies such as JPY, KRW and VND have no minor unit. Send the whole number as it is, for example `1000` for ¥1000, and do not multiply those either.

## Customers

- Create a Customer once, at user registration, and keep the returned `id`. Save-card flows need one; guest checkout does not.

## PaymentIntents

- Include `customer_id` whenever the shopper may save a card or already has saved cards.
- To save a card without taking payment, set `amount: 0`. The card is validated by a zero-amount authorization and no funds are captured.
- `return_url` is required for alternative payment methods that redirect, such as PayPal and iDEAL.
- For the Hosted Payment Page the redirect destination is `successUrl` in `redirectToCheckout()`, not `return_url`.
- Pass `id` (as `intent_id`) and `client_secret` to the frontend.
- Derive the amount from trusted server-side order data. Never charge an amount supplied by the client.
- Treat client-side completion as provisional. A `success` event, or a redirect back to your site, means the shopper finished the form rather than that the payment settled. Confirm with the `payment_intent.succeeded` webhook or by retrieving the PaymentIntent before granting value.

## Saved payment methods

- List saved cards with `type=card&status=CREATED`. Do not show `DISABLED` methods to the shopper.
- The Hosted Payment Page and Drop-in element display saved cards automatically when `customer_id` is present, so you generally do not need this API for them.
- The same physical card can appear as several PaymentMethods. Group them by `card.fingerprint` when presenting a saved-card picker, keep every underlying `payment_method.id` on your side, and resolve back to the exact one the shopper selected, together with the `payment_consent_id` for merchant-initiated flows. Where `card.fingerprint` is absent, leave the entries separate rather than collapsing them.
- Do not apply that grouping to `GET /pa/config/payment_method_types`. That endpoint can repeat a method name across `transaction_mode` values, so query it with `active=true` and the `transaction_mode` you intend to use.

## Customer-initiated and merchant-initiated payments

See [Save and reuse payment details](https://www.airwallex.com/docs/payments/integration-options/web-checkout/save-and-reuse-payment-details.md) for the full consent model.

|                                  | Customer-initiated (CIT)      | Merchant-initiated (MIT)              |
| -------------------------------- | ----------------------------- | ------------------------------------- |
| Who triggers it                  | The shopper, in session       | The merchant, with no shopper session |
| `next_triggered_by`              | `customer`                    | `merchant`                            |
| CVC on subsequent payments       | Required                      | Not required                          |
| How subsequent payments are made | Frontend, saved card plus CVC | Server-side API call                  |
| Typical use                      | Returning shopper checkout    | Subscriptions, top-ups, installments  |

`merchant_trigger_reason` values:

| Value          | Use for                                   | Notes                                                              |
| -------------- | ----------------------------------------- | ------------------------------------------------------------------ |
| `scheduled`    | Subscriptions and recurring billing       | Fixed intervals, for example monthly or yearly                     |
| `unscheduled`  | Balance top-ups, one-off merchant charges | No fixed schedule, shopper not in session                          |
| `installments` | Buy now pay later, split payments         | One total collected over several payments; requires `terms_of_use` |

- Subsequent merchant-initiated payments are purely server-side. Confirm the PaymentIntent against the stored `payment_consent_id` with `triggered_by: merchant`; no frontend element is involved.
- A new agreement may still require 3DS authentication under PSD2 SCA, even though later merchant-initiated charges do not.
- Customer-initiated subsequent payments confirm through the CVC element, not the card number element.

## Split Card Element

- Pass the PaymentIntent to `createElement('cardNumber', ...)` as a nested `intent` object, for example `intent: { id, client_secret }`. This element has no top-level `intent_id` or `client_secret` options and passing them there is silently ignored; those two names belong to `confirm()`.
- The element type for card expiry is `expiry`, not `expiryDate`.
- A mounted element instance stays bound to the container it was mounted into, and mounting the same instance again has no effect. To move a field, destroy the instance and create a new one.

## Endpoints used by these flows

| Purpose                 | Endpoint                                                                              | When to call it                                                                          |
| ----------------------- | ------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| Create a Customer       | `POST /api/v1/pa/customers/create`                                                    | Once, at user registration                                                               |
| Create a PaymentIntent  | `POST /api/v1/pa/payment_intents/create`                                              | Before each payment                                                                      |
| List saved cards        | `GET /api/v1/pa/payment_methods` with `customer_id`, `type=card` and `status=CREATED` | When a returning shopper reaches the payment page                                        |
| Confirm a PaymentIntent | `POST /api/v1/pa/payment_intents/{id}/confirm`                                        | Customer-initiated confirm from the frontend, or a merchant-initiated charge server-side |

## Beyond these notes

For anything not covered here, use the [Airwallex Developer MCP](https://www.airwallex.com/docs/developer-tools/ai/developer-connector.md) to search the API documentation and to call sandbox APIs while building.