Skip to main content

Express Checkout

Express checkout lets you collect shipping and billing addresses directly from Apple Pay, Google Pay, or PayPal wallet UIs — no separate address form needed.

Available on PaymentRequest (Apple Pay / Google Pay) and PayPal components.

Before you begin

Express checkout with shipping

With shipping, the wallet-authorized amount may differ from the initial amount when shipping costs change. This flow uses the accountId approach — no pre-created payment needed. The payment is created server-side after the customer confirms in the wallet UI.

1. Include monei.js (Client-side)

Add the script tag to the head of your HTML file.

checkout.html
<head>
<script src="https://js.monei.com/v3/monei.js"></script>
</head>
<body>
<!-- Use "payment_request" for Apple Pay / Google Pay, "paypal_container" for PayPal -->
<div id="payment_request"></div>
<div id="paypal_container"></div>
<script src="client.js"></script>
</body>

2. Initialize Component with express checkout (Client-side)

client.js
const paymentRequest = monei.PaymentRequest({
accountId: 'your_account_id',
sessionId: 'unique_session_id',
amount: 1099, // base amount in minor units (before shipping)
currency: 'EUR',
requestShipping: true,
requestBilling: true,
shippingOptions: [
{id: 'standard', label: 'Standard Shipping', amount: 500},
{id: 'express', label: 'Express Shipping', amount: 1200, description: '1-2 business days'}
],
onShippingAddressChange(address) {
// Called when the customer changes their shipping address.
// address contains: country, city, state, zip (no street-level data mid-flow).
// Return updated shipping options and/or amount based on the address.
if (address.country !== 'ES') {
return {
shippingOptions: [{id: 'intl', label: 'International', amount: 2000}],
amount: 3099 // product (1099) + intl shipping (2000)
};
}
return {shippingOptions: [{id: 'standard', label: 'Standard', amount: 500}], amount: 1599};
},
onShippingOptionChange(option) {
// Called when the customer selects a different shipping option.
// Return updated total amount.
return {amount: 1099 + option.amount};
},
async onSubmit(result) {
// Called when the customer approves the payment in the wallet UI.
// Create the payment server-side (see Step 3).
await fetch('/create-payment', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
amount: result.finalAmount,
currency: 'EUR',
sessionId: 'unique_session_id',
paymentToken: result.token,
billingDetails: result.billingDetails,
shippingDetails: result.shippingDetails
})
});
},
onError(error) {
console.error(error);
}
});

paymentRequest.render('#payment_request');

Key options:

  • accountId string: Your MONEI account ID.
  • sessionId string: A unique session identifier. Must match the sessionId passed when creating the payment server-side.
  • amount number: Initial amount in the smallest currency unit (e.g., 1099 = 10.99 EUR).
  • requestShipping boolean: Enable shipping address collection in the wallet UI.
  • requestBilling boolean: Enable billing address collection (PaymentRequest only — PayPal always returns billing info).
  • shippingOptions ShippingOption[]: Available shipping options.

See PaymentRequest options and PayPal options for the full list.

3. Create Payment (Server-side)

When the customer confirms in the wallet UI, your onSubmit handler sends the result to your server. Create the payment passing the paymentToken — this confirms the payment automatically.

important

You must pass the same sessionId used to initialize the component on the frontend. This ensures the token-generating customer matches the paying customer.

POST https://api.monei.com/v1/payments
curl --request POST 'https://api.monei.com/v1/payments' \
--header 'Authorization: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data-raw '{
"amount": 1599,
"currency": "EUR",
"orderId": "14379133960355",
"description": "Test Shop - #14379133960355",
"sessionId": "unique_session_id",
"paymentToken": "token_from_client",
"callbackUrl": "https://example.com/checkout/callback",
"customer": {
"email": "email@example.com"
},
"billingDetails": {},
"shippingDetails": {}
}'

Check all available request parameters.

4. Process Webhook Notification (Server-side)

MONEI sends the final payment status via an asynchronous HTTP POST request to the callbackUrl you provided in Step 3. The request body contains the full Payment object in JSON format.

This webhook is the only reliable way to confirm the definitive payment outcome.

  1. Verify the MONEI-Signature header to confirm the webhook genuinely came from MONEI. See the Verify Signatures guide for implementation details.
  2. Return a 200 OK HTTP status code immediately to acknowledge receipt.

If MONEI doesn't receive a 200 OK, it will retry sending the webhook.

Billing-only checkout

If you only need the billing address (no shipping), the flow is simpler — use paymentId instead of accountId and confirm client-side with confirmPayment. Follow the same steps as Build a custom checkout, but add requestBilling: true to collect the billing address from the wallet:

client.js
const paymentRequest = monei.PaymentRequest({
paymentId: '{{payment_id}}',
requestBilling: true,
async onSubmit(result) {
await monei.confirmPayment({
paymentId: '{{payment_id}}',
paymentToken: result.token,
billingDetails: result.billingDetails
});
},
onError(error) {
console.error(error);
}
});

paymentRequest.render('#payment_request');

Type reference

See the full type definitions in the Reference:

Before you go live