Accept a one-click payment with Apple Pay
Learn how to use the Fintoc Express Checkout to accept one-click Apple Pay payments
Use a single integration to accept payments through one-click payment buttons. The Express Checkout component is a Fintoc SDK feature that lets your customers pay with wallet buttons — without opening the full widget. The currently supported payment method is Apple Pay.
Customers see the Apple Pay button depending on their device and browser. If Apple Pay is not available on the device, the button is hidden.
There are six steps to accept Apple Pay payments using Express Checkout:
- Set up your server to create a
Checkout Session - Set up the Fintoc SDK on your frontend
- Create and mount the Express Checkout component
- Handle the
onPaymentRequestcallback - Submit the payment to Fintoc
- Test the integration
Step 1 — Set up your server
Server-side
Express Checkout calls the onPaymentRequest callback every time the customer taps the wallet button. In that callback you must create a Checkout Session from your backend and return its session_token to the browser.
Expose an endpoint on your server that creates a Checkout Session with Fintoc's API:
Node
// server.js — Node example
app.post("/api/create-checkout", async (req, res) => {
const response = await fetch("https://api.fintoc.com/v2/checkout_sessions", {
method: "POST",
headers: {
Authorization: process.env.FINTOC_SECRET_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
amount: 1000,
currency: "CLP",
payment_methods: ["card"],
recipient_account: { id: "acc_xxx" },
// ...any other fields your flow requires
}),
});
const session = await response.json();
res.json({ session_token: session.session_token });
});Never expose your secret key in the browser. Always keep the Checkout Session creation call on your server.
Step 2 — Set up the Fintoc SDK
Client-side
Express Checkout is automatically available as a feature of the Fintoc SDK. Include the Fintoc script on your checkout page by adding it to the <head> of your HTML file. Always load the SDK directly from js.fintoc.com to receive security updates. Don't include the script in a bundle or host a copy of it yourself.
<head>
<title>Checkout</title>
<script src="https://js.fintoc.com/v1/"></script>
</head>Step 3 — Create and mount the Express Checkout
Client-side
The Express Checkout component renders the wallet button inside an iframe that securely sends the payment information to Fintoc over an HTTPS connection. The checkout page address must also start with https://, rather than http://, for your integration to work.
First, create an empty DOM node (container) with a unique ID in your payment form:
<div id="apple-pay-container">
<!-- Express Checkout will be inserted here -->
</div>
<div id="error-message">
<!-- Display an error message to your customers here -->
</div>When the form has loaded, create an instance of the widget and mount Express Checkout to the container DOM node:
const widget = window.Fintoc.create({
product: "payments",
publicKey: "pk_test_...",
country: "cl",
expressCheckout: {
container: "apple-pay-container",
amount: 1000,
currency: "CLP",
},
onPaymentRequest: async ({ payment_method_type, wallet }) => {
const res = await fetch("/api/create-checkout", { method: "POST" });
const { session_token } = await res.json();
return { sessionToken: session_token };
},
onSuccess: (data) => console.log("Payment succeeded", data),
onExit: (reason) => console.log("Widget closed", reason),
onEvent: (eventName, metadata) => console.log(eventName, metadata),
});The Apple Pay buttons can have the following styles:

Button styles: black, white and white with outline
Step 4 — Handle the onPaymentRequest callback
Client-side
onPaymentRequest is invoked every time a payment flow is initiated — both when the customer taps the wallet button and when you call widget.open() for bank transfer. In that callback you must call your server, obtain a session_token, and return it to the SDK.
The callback receives a single argument with information about the requested method:
| Field | Type | Description |
|---|---|---|
payment_method_type | string | "card" for wallet payments. "bank_transfer" when widget.open() is called. |
wallet | string (optional) | The specific wallet selected, e.g. "apple_pay". undefined for bank transfer. |
Your callback must resolve to { sessionToken: string } within 30 seconds or the SDK will emit payment_error.
You can use the
payment_method_typeandwalletfields to reuse a single endpoint for wallets and bank transfer, or to route to different endpoints if your backend logic differs.
Step 5 — Submit the payment to Fintoc
Client-side
Once the customer authorizes the wallet payment, the SDK automatically completes the flow. You don't need to call a confirmPayment method yourself.
The sequence is:
- The SDK emits
processing_express_checkout_payment. - The SDK calls your
onPaymentRequestcallback to get a freshsessionToken. - The SDK requests a payment token from Apple Pay.
- The SDK submits both tokens to the Fintoc backend, which creates the Payment Resource and charges the wallet.
- When the payment reaches
status === 'succeeded', the SDK callsonSuccess(data)with the resulting resource.
If the payment status is anything other thansucceeded, or any step in the flow fails, the SDK emitspayment_errorinstead of callingonSuccess.
Handling the loading state during payment processing
While the Express Checkout is processing a payment, you should display a loading state to give clear feedback to your customer and prevent duplicate interactions.
Fintoc Elements emits two events you can listen to in order to manage this:
• processing_express_checkout_payment: emitted when the customer has triggered the payment and Fintoc is processing it. Use this event to show your loading indicator (for example, a spinner overlay on the button or disabling other actions on the page).
• express_checkout_ready: emitted when the Express Checkout element has finished loading and is ready to be used again. Use this event to remove the loading indicator.
A typical implementation looks like this:
expressCheckout.on('processing_express_checkout_payment', () => {
// Show loading state (e.g. spinner, disable buttons)
setLoading(true);
});
expressCheckout.on('express_checkout_ready', () => {
// Hide loading state
setLoading(false);
});
Make sure to always pair both events so the loading state is properly cleared once the element is ready again, avoiding stuck UIs after a payment attempt.
Step 6 — Test the integration
Before you go live, test the integration on a device that supports Apple Pay.
Requirements for testing:
- Use your test public key and a test recipient account.
- Open your checkout on iOS or macOS.
- Register your test domain with Fintoc: Contact your sales or success to initiate the process to register your domain and be able load the Apple Pay button on your page.
Integration checklist:
- The Apple Pay button renders on page load on a supported device.
-
express_checkout_readyfires withwallets.applePay === true. - Tapping the button triggers
onPaymentRequestwithpayment_method_type: 'card',wallet: 'apple_pay'. -
processing_express_checkout_paymentfires while the payment is in progress. -
onSuccessfires with the payment resource after a successful authorization. - Cancelling the Apple Pay sheet does not emit
payment_error. - Calling
widget.open()(bank transfer fallback) invokesonPaymentRequestwithpayment_method_type: 'bank_transfer'and opens the widget.
Optional configurations
Listen to the express_checkout_ready event
After mounting, Express Checkout won't show buttons for a brief period while the SDK initializes and checks wallet availability. To animate the element when the button appears, listen to the express_checkout_ready event. Inspect the wallets value to determine which buttons, if any, to display:
// Optional: hide the element until we know if a button will show
const container = document.getElementById("apple-pay-container");
container.style.visibility = "hidden";
window.Fintoc.create({
// ...
onEvent: (eventName, metadata) => {
if (eventName === "express_checkout_ready") {
if (metadata.wallets.applePay) {
container.style.visibility = "initial";
} else {
// No buttons will show — fall back to bank transfer only
}
}
},
});Style the button
You can tune the look of the Apple Pay button through the optional fields of expressCheckout.
expressCheckout: {
container: "apple-pay-container",
amount: 1000,
currency: "CLP",
// Height in pixels. Defaults to 44. Range [40, 100].
buttonHeight: 52,
// Border radius in pixels. Defaults to 4. Range [0, 100].
buttonBorderRadius: 12,
// Specify the label shown inside the button.
// Defaults to "plain" for Apple Pay.
buttonType: { applePay: "buy" },
// Specify the color scheme of the button.
// Defaults to "black".
buttonStyle: { applePay: "white" },
// Specify the language for the Apple Pay label.
// Defaults to "es-ES".
locale: { applePay: "es-MX" },
}| Option | Type | Default | Description |
|---|---|---|---|
buttonHeight | number (px) | 44 | Height of the button. Clamped to [40, 100]. |
buttonBorderRadius | number (px) | 4 | Border radius of the button. Clamped to [0, 100]. |
buttonType.applePay | "buy", "pay", or "plain" | "plain" | Label shown inside the Apple Pay button. |
buttonStyle.applePay | "black" or "white" | "black" | Color scheme of the Apple Pay button. |
locale.applePay | "es-ES" or "es-MX" | "es-ES" | Language for the Apple Pay button label. |
Values outside the allowed range are clamped rather than rejected — e.g.buttonHeight: 200is treated as100.
The same widget object returned by Fintoc.create exposes the usual widget methods. When expressCheckout is configured, calling widget.open() automatically invokes onPaymentRequest with payment_method_type: 'bank_transfer' before opening the widget — so you don't need a second initialization.
document
.getElementById("bank-transfer-button")
.addEventListener("click", () => widget.open());Event reference
Express Checkout uses the same onEvent callback as the rest of the widget (widget events reference), plus a few events specific to this component:
| Event | Payload | When it fires |
|---|---|---|
express_checkout_ready | { wallets: { applePay?: boolean } } | Once the SDK finishes initializing. The wallets object lists the wallets that were successfully rendered. If empty, none are available (unsupported browser, domain not verified, etc.). |
processing_express_checkout_payment | — | After the user confirms the wallet payment and the SDK is exchanging tokens with your server and the Fintoc backend. |
payment_error | — | The wallet payment failed at any step: onPaymentRequest rejected or timed out, the wallet provider returned an error, or the resulting payment status is not succeeded. |
express_checkout_error | — | An internal error prevented the button from rendering (SDK failed to load, container not found, etc.). |
Full option reference
Fintoc.create(options)
| Parameter | Type | Description |
|---|---|---|
product | "payments" | Required. Must be "payments" for Express Checkout. |
publicKey | string | Required. Your publishable key. |
country | "cl" or "mx" | Destination country. Defaults to "cl". |
expressCheckout | ExpressCheckoutConfig | Required. Configuration for the wallet button. |
onPaymentRequest | (data) => Promise<{ sessionToken }> | Required. Called every time a payment flow starts. |
onSuccess | (data) => void | Called when the payment reaches the succeeded status. |
onExit | (reason?: string) => void | Called when the widget is closed. |
onEvent | (eventName, metadata?) => void | Called for every widget event. |
ExpressCheckoutConfig
| Parameter | Type | Required | Description |
|---|---|---|---|
container | string | Yes | id of the DOM element where the wallet button will be rendered. |
amount | number | Yes | Amount to charge, in the currency's minor unit (e.g. CLP without decimals, MXN in cents). |
currency | string | Yes | ISO currency code ("CLP", "MXN"). |
buttonHeight | number | No | Button height in px. Range [40, 100]. Default 44. |
buttonBorderRadius | number | No | Button border radius in px. Range [0, 100]. Default 4. |
buttonType.applePay | "buy", "pay", or "plain" | No | Apple Pay button label. Default "plain". |
buttonStyle.applePay | "black" or "white" | No | Apple Pay color scheme. Default "black". |
locale.applePay | "es-ES" or "es-MX" | No | Apple Pay button language. Default "es-ES". |
See also
- Accept a payment — standard Checkout Session integration guide.
- Web Integration — full widget integration guide.
- Listen to Widget events — complete event catalog.
- WebView Integration — integrating the widget inside a native app.
Updated about 7 hours ago