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

# Checkout integration - Integration steps

> The complete process for integrating payments using the Waffo hosted Checkout.

Checkout is the simplest integration method—redirect users to a Waffo-hosted payment page without handling sensitive card information.

> [!NOTE]
>
> This page covers both **server-side** responsibilities (create the order, receive Webhook callbacks) and **frontend** responsibilities (parse orderAction, redirect, iframe / window\.open / WebView embedding). Read the parts relevant to your role.

## Workflow

```mermaid theme={null}
sequenceDiagram
    participant M as Merchant server
    participant W as Waffo
    participant U as User

    M->>W: 1. Create order (API)
    W-->>M: 2. Return orderAction
    M->>U: 3. Redirect user
    U->>W: 4. User completes payment
    W-->>M: 5. Webhook notification
    W->>U: 6. Redirect back to merchant
```

**1. Create an order on your server**

Call the payment creation API to obtain `orderAction` from the response. `orderAction` is a JSON string.

**2. Redirect the user**

Parse `orderAction`, then use `webUrl` or `deeplinkUrl` based on `actionType`. The user will complete the payment on the Waffo Checkout page.

**3. Handle the result**

* **Webhook** (recommended): Waffo sends the payment result notification to `notifyUrl`
* **Redirect**: Successful payments redirect to `successRedirectUrl`; payment pre-verification failures or failed payments redirect to `failedRedirectUrl`; user cancellation without continuing redirects to `cancelRedirectUrl`
* **Active query**: Call the payment query API

> [!WARNING]
>
> A redirect only indicates the user returned to your website—it does not mean the payment succeeded. Always rely on the Webhook or the active query result.

### Get the Checkout URL

`orderAction` is a JSON string. Parse it first, then choose the URL based on `actionType`:

Different values for `payMethodType` and `payMethodName` affect whether the user enters the Waffo payment-method selection page first. See [Cashier integration - Customization options](/docs/en/developer-docs/integration/checkout/customization.md).

```typescript theme={null}
const orderAction = JSON.parse(response.data.orderAction);
const checkoutUrl = orderAction.actionType === 'DEEPLINK'
  ? orderAction.deeplinkUrl
  : orderAction.webUrl;
```

### Payment method prerequisites

* **WeChat Pay**: If you need WeChat Pay in production, submit the production domain to Waffo in advance. Waffo will file it with the payment channel.

***

## iframe Embedding Considerations

If you embed the Waffo Checkout inside an iframe, pay attention to the following:

### Required Configuration

* **`allow="payment"` attribute**: The iframe tag must include `allow="payment"`, otherwise payment methods such as Google Pay will not work on Android devices.
* **Referrer Policy**: Must be set to `strict-origin-when-cross-origin`, otherwise WeChat Pay will fail.

```html theme={null}
<head>
  <meta name="referrer" content="strict-origin-when-cross-origin">
</head>

<iframe src="https://cashier.waffo.com/..." allow="payment"></iframe>
```

### 3DS challenges and CSP

A 3DS challenge may load the issuer's Access Control Server (ACS) in a nested iframe. The ACS origin can vary by issuer and transaction, so do not try to enumerate every ACS origin. Configure your [Content Security Policy (CSP)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP) for your integration mode:

| Integration mode | CSP configuration |
| - | - |
| Waffo Checkout in an iframe | The Merchant page only needs to allow the exact, approved Waffo Checkout origin for each environment in `frame-src`. Waffo Checkout handles the nested 3DS challenge, so the Merchant page does not need ACS origins in `frame-src`. |

> [!WARNING]
>
> Do not allow every `https:` source in `frame-src`, and do not disable CSP to accommodate dynamic ACS origins. If you cannot safely support an iframe challenge, use a top-level redirect or `window.open` instead.

For a blank 3DS page, capture the CSP violation's `effectiveDirective`, blocked URL, the document that delivered the policy, integration mode, order ID, and event time from the browser Console and Network panels. You can validate a policy with `Content-Security-Policy-Report-Only` before enforcing it. Do not decide which origins to allow based only on a provider name appearing on the page.

### Responsive Width

Do not use a fixed width (e.g. `width: 700px`), as content will be clipped on different devices. Use a responsive layout instead:

```css theme={null}
.checkout-container {
  overflow: scroll;
  height: calc(100vh - 40px);
}
```

### Known Limitations

> [!WARNING]
>
> * **Apple Pay**: An iframe integration must use the Waffo frontend SDK. Contact Waffo technical support in advance to complete domain verification and registration. Do not load `orderAction.webUrl` in a plain iframe. See [Apple Pay integration](/docs/en/developer-docs/integration/apple-pay/overview.md#option-3-load-waffo-apple-pay-checkout-in-an-iframe).
> * **JKOPAY**: Does not support iframe loading.
> * **Mobile**: Check that the iframe does not have a fixed size that causes it to render as a landscape PC page on mobile.
> * For more limitations that apply by payment method or payment method type, see [Payment method integration notes](/docs/en/developer-docs/tools-and-references/references/payment-method-integration-notes.md).

***

## `window.open` Embedding Considerations

When opening the Checkout with `window.open`:

* **Synchronous calls are fine**: Calling `window.open()` directly inside a click event handler works without issues.
* **Async calls will be blocked**: If `window.open()` is called after an `await`, the browser will block the popup. Workaround: open a blank window with `window.open()` first, then assign the URL once retrieved asynchronously; or show a confirmation dialog so the user can click to open it manually.
* **Apple mobile**: User gesture detection is strict—async opens are almost always blocked.

***

## App WebView Integration Considerations

When a merchant App loads the Checkout inside a WebView:

* **External launch capability**: Confirm that the App and WebView allow opening external Apps or external browser pages. Some wallets complete authorization through deeplinks or external pages. Blocking this will stop the payment flow.
* **Download and copy capability**: WebViews may not support file downloads, copy actions, or long-press save behavior by default. QR, OTC, and bank-transfer style payment methods may depend on these capabilities.
* **Redirect URL integrity**: When passing URLs between the native App and WebView, do not rewrite the URL or drop query parameters. Some payment methods append required parameters to the redirect URL.
* **`userTerminal`**: Pass `APP` instead of `WEB` to receive redirect links optimised for the App environment.
* **Payment method-specific limitations**: For PayPay Smart Payment, Google Pay, Apple Pay, JKOPAY, and other environment-specific limitations, see [Payment method integration notes](/docs/en/developer-docs/tools-and-references/references/payment-method-integration-notes.md).

### Handle wallet App custom schemes

The Checkout URL starts with HTTPS, but later redirects are not necessarily HTTPS. TrueMoney, PromptPay, ShopeePay, and other payment flows may navigate to a non-HTTP(S) scheme defined by the payment page, such as `airpay://`, in the final step to launch a wallet App. Use the scheme returned by the payment page at runtime.

A WebView cannot load a custom scheme as a web page. The host App must intercept non-HTTP(S) main-frame navigation, validate the target scheme, and hand the URL to the operating system to open the matching App. Leave HTTP(S) URLs in the WebView. If no installed App can handle the scheme, tell the user to install the wallet or choose another payment method.

| Platform | Integration requirement | Official references |
| - | - | - |
| Android | Distinguish HTTP(S) URLs from custom schemes in `WebViewClient.shouldOverrideUrlLoading`, then hand validated external URLs to a system `Intent` | [Build web apps in WebView](https://developer.android.com/develop/ui/views/layout/webapps/webview#handle-page-navigation), [`WebViewClient.shouldOverrideUrlLoading`](https://developer.android.com/reference/android/webkit/WebViewClient#shouldOverrideUrlLoading\(android.webkit.WebView,%20android.webkit.WebResourceRequest\)), [`Intent.ACTION_VIEW`](https://developer.android.com/reference/android/content/Intent#ACTION_VIEW) |
| iOS | Inspect navigation URLs through `WKNavigationDelegate`, cancel custom-scheme loading in WKWebView, then open the matching App with `UIApplication.open` | [`WKNavigationDelegate`](https://developer.apple.com/documentation/webkit/wknavigationdelegate), [`UIApplication.open`](https://developer.apple.com/documentation/uikit/uiapplication/open%28_%3Aoptions%3Acompletionhandler%3A%29), [`UIApplication.canOpenURL`](https://developer.apple.com/documentation/uikit/uiapplication/canopenurl%28_%3A%29) |

> [!NOTE]
>
> If your iOS App calls `canOpenURL` before opening a custom scheme, add the required schemes to `LSApplicationQueriesSchemes` in `Info.plist` as described by Apple. Add only the schemes your integration needs.

### Troubleshoot `ERR_UNKNOWN_URL_SCHEME` and `Forbidden`

| Symptom | Check first | Resolution |
| - | - | - |
| The WebView shows `net::ERR_UNKNOWN_URL_SCHEME`, or the wallet App does not open after the user taps pay | Record the last navigation URL and check whether it uses a non-HTTP(S) scheme; confirm that the WebView navigation callback receives the URL; confirm that an installed App can handle the scheme | Follow the platform guidance above and hand the custom scheme to the operating system instead of letting the WebView load it as a web page |
| The WebView shows `Error: Forbidden` | Record the complete failed URL before diagnosing the error from the page text | If the URL uses a non-HTTP(S) scheme, troubleshoot the external launch flow above. If it is still HTTPS, preserve the complete URL and query parameters, record the time and order ID, and contact Waffo technical support |

These issues normally depend on the failed URL's scheme and the host App's WebView handling. Do not diagnose them from the device model or Android version alone.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.