Embedded Cards in checkout
In addition to the redirect flow detailed in the Orders guide, Card Payments also support the embedded flow. The embedded flow allows you to display the card fields directly in your checkout page, without redirecting the shopper to Montonio. This guide will walk you through the process.
Before you proceed, ensure you have the following:
- A backend application capable of making outbound REST requests
- A checkout page where you want to display the card fields
- Familiarity with creating Montonio Orders
1. Create a Session
The first step is to create a Session. This is a back-end request to the Montonio API, made when the customer gets to your checkout page.
Here’s how to create a Session with our API:
Complete code example
/** * We recommend using the jsonwebtoken package to generate * Json Web Tokens. You can install it with npm: * > npm install jsonwebtoken * More information can be found at * https://www.npmjs.com/package/jsonwebtoken */import jwt from 'jsonwebtoken';import axios from 'axios';
/** * Note: Please make sure to make these calls from your server, * and not from the client. This is to prevent your secret key * from being exposed to the public. */
// 1. Put your Access Key in the payloadconst payload = { "accessKey": "MY_ACCESS_KEY",}
// 2. Generate the tokenconst token = jwt.sign( payload, 'MY_SECRET_KEY', { algorithm: 'HS256', expiresIn: '10m' });
// console.log(token);// eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhY2Nlc3NLZXkiOiIwMzA2M2I4Yi0wMjliLTQ5NTMtYTA0ZC02ZDNkNjRhZDdkNmUiLCJleHAiOjE3NTczNDM2NDl9.4mEIutn-C2kU4rEGS5zs1jlxjebvb0WzmsIFWAdbUIw
// 3. Send the token to the APIaxios.post('https://stargate.montonio.com/api/sessions', { data: token}).then(response => { const { data } = response;
console.log(data.uuid); // 087a9fb5-7a85-4e1e-b3f7-2546faab9a97
// pass the Session UUID to the client-side});<?php/** * We recommend using Firebase's php-jwt package to generate * Json Web Tokens. You can install it with composer: * > composer require firebase/php-jwt * More information can be found at * https://github.com/firebase/php-jwt */
// If you are using composer remember you need to autoload files like this// require __DIR__ . '/vendor/autoload.php';
use \Firebase\JWT\JWT;
// 1. Create the token payload$payload = [ 'accessKey' => 'MY_ACCESS_KEY', 'exp' => time() + (10 * 60),];
// 2. Generate the token using Firebase's JWT library$token = JWT::encode($payload, 'MY_SECRET_KEY', 'HS256');
// var_dump($token);// eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhY2Nlc3NLZXkiOiIwMzA2M2I4Yi0wMjliLTQ5NTMtYTA0ZC02ZDNkNjRhZDdkNmUiLCJleHAiOjE3NTczNDM2NDl9.4mEIutn-C2kU4rEGS5zs1jlxjebvb0WzmsIFWAdbUIw
// 3. Send the token to the API$ch = curl_init();curl_setopt($ch, CURLOPT_URL, "https://stargate.montonio.com/api/sessions");curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);curl_setopt($ch, CURLOPT_POST, 1);curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([ 'data' => $token]));curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Content-Type: application/json']);$result = curl_exec($ch);$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);curl_close($ch);
// 4. Get the session data$data = json_decode($result, true);
var_dump($data['uuid']); // 087a9fb5-7a85-4e1e-b3f7-2546faab9a97
// pass the Session UUID to the client-side'''We recommend using the PyJWT package to generateJson Web Tokens. You can install it with pip:> pip3 install PyJWTMore information can be found athttps://pyjwt.readthedocs.io/en/latest/'''import jwtimport datetimeimport requests
# 1. Create the token payloadpayload = { 'accessKey': 'MY_ACCESS_KEY', 'exp': datetime.datetime.now(timezone.utc) + datetime.timedelta(minutes=10)}
# 2. Generate the tokentoken = jwt.encode(payload, 'MY_SECRET_KEY', algorithm='HS256')
# print(token)# eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhY2Nlc3NLZXkiOiIwMzA2M2I4Yi0wMjliLTQ5NTMtYTA0ZC02ZDNkNjRhZDdkNmUiLCJleHAiOjE3NTczNDM2NDl9.4mEIutn-C2kU4rEGS5zs1jlxjebvb0WzmsIFWAdbUIw
# 3. Send the token to the APIresponse = requests.post('https://stargate.montonio.com/api/sessions', json={ 'data': token})data = response.json()
print(data['uuid']) # 087a9fb5-7a85-4e1e-b3f7-2546faab9a97
# pass the Session UUID to the client-side🧪 For sandbox testing, use your Sandbox API Keys and https://sandbox-stargate.montonio.com/api as the API Base URL.
For more details about the endpoint, see the API reference.
2. Install the Montonio JS SDK
Once you have the Session UUID, you can use it to initialize the MontonioCheckout component in your checkout page using our JS SDK. But first, you need to install the SDK on your frontend.
We support installation both as a JavaScript module (ESM) and as an embedded script tag (UMD).
Option 1: ES Module
- Install the package using npm or yarn:
npm install @montonio/montonio-js- Import the library in your JavaScript code:
import { MontonioCheckout } from '@montonio/montonio-js';Option 2: Embedded script tag
To embed the classic UMD version of the library, you can include the following script tag in your HTML file:
<script src="https://js.montonio.com/1.x.x/montonio.umd.js"></script>In this case, the library and its components will be available in the global object window.Montonio:
const { MontonioCheckout } = window.Montonio;The following code examples use the async/await syntax. If you are using <script> tags (e.g. in some PHP-based frameworks), you can use <script type="module"> to use the async/await syntax. Alternatively, you can use the then and catch syntax for the same effect.
3. Initialize MontonioCheckout
Now that you have the SDK installed, you can initialize the MontonioCheckout component in your checkout page. First, create a container element in your HTML where MontonioCheckout will be rendered. You need to decide on the appropriate place in your checkout page.
<div id="montonio-checkout-container"></div>Then, initialize the MontonioCheckout component with the session UUID and the container element.
import { MontonioCheckout } from '@montonio/montonio-js'; // ES Module usage. See above for UMD imports
const checkoutOptions = { sessionUuid: 'session-uuid', // The UUID of the session created on your server in step 1 environment: 'production', // Use 'sandbox' for testing; 'production' for live payments locale: 'en', // The language of the payment gateway. Defaults to your store default language. // Available values are ('en', 'et', 'lt', 'lv', 'pl', 'ru', 'fi') // TypeScript users can use LocaleEnum.EN (or ET, LT, etc.) by importing LocaleEnum from @montonio/montonio-js onSuccess: (result) => { // Payment completed successfully // Redirect to the thank you page window.location.href = result.returnUrl; }, onError: (error) => { // Payment failed or validation error occurred console.error('Payment failed:', error); // Unlock your checkout form to allow the user to try again }};
const montonioCheckout = new MontonioCheckout(checkoutOptions);await montonioCheckout.initialize('#montonio-checkout-container'); // The CSS selector string or HTMLElement of the container to mount the Montonio Checkout componentThe MontonioCheckout.initialize() method will render the MontonioCheckout component in the specified container. You can then interact with it by calling methods on the MontonioCheckout instance.
4. Validate the card form
Once the user has filled in the card form and clicked the “Pay” button, you need to validate the form. Simply call the validateOrReject method on the MontonioCheckout instance. By default, validation errors are displayed to the user already in the payment form. Optionally, you can also catch the validation errors and display them to the user yourself.
// User clicks the "Pay" button in your checkout form// Make sure to now lock your checkout and prevent the user from making any further changes.try { montonioCheckout.validateOrReject(); // Proceed with the payment} catch (error) { // Handle validation errors}5. Create the order and submit the payment
Once the user has clicked the “Pay” button in your checkout and you have validated the form, you can create the order and submit the payment. First, you need to create a Montonio Order on your server. Follow the Create and validate an Order guide to create the order.
Make sure you include the session UUID in the order request. See the Order data structure section of the Orders guide for more details.
Once the order is created, you can call the submitPayment method on the MontonioCheckout instance.
// Submit the paymentmontonioCheckout.submitPayment();
// The onSuccess callback will be invoked when payment completes successfully// The onError callback will be invoked if payment failsThe MontonioCheckout.submitPayment() method will initiate the payment submission. In case a payment method requires additional user authentication (such as 3DS), a modal will pop up to handle the authentication.
When the payment completes (successfully or with an error), the appropriate callback you defined during initialization will be invoked:
onSuccess(result): Called when payment is successful. The result containspaymentStatus,orderToken, andreturnUrlfields.onError(error): Called when payment fails or validation errors occur.
The returnUrl is the URL you provided in the backend request to create the order. As per the Validating the payment section of our Orders guide, this URL will contain the order-token query parameter, which you can use to validate the payment. In most cases, you should redirect the user to the returnUrl in your onSuccess callback and handle the token validation on that page. More on that in section 6.
Handling errors and retries
If there’s an error and the customer retries, you can just retry the submitPayment() method. However if the total amount (or any other important detail) changed, create a new order with the same Session UUID before retrying submitPayment().
When creating a new order, you can keep the same merchantReference to maintain an easy 1-1 relationship between your Order and the Order in the Montonio Partner System. This avoids clutter in your dashboard and makes it easier to find the correct order later.
6. Handle success redirect and webhook notification
To handle a successful payment, you need build a success redirect page. This is the returnUrl you provided in the backend request to create the order.
In most cases during the card payment flow, the Montonio SDK keeps the user in your checout until the payment is completed or fails – this is why the result of the submitPayment() method will also include our orderToken and you can validate it on that page. However, it is possible that certain 3DS card flows will redirect the user to the bank. In such cases, we will redirect the shopper back to the returnUrl, as documented in the Validating the payment section of the Orders guide.
In addition to validating the payment during the user flow, you should also build a webhook notification handler – this is the notificationUrl you provided in the backend request to create the order. This is used to confirm the payment in the background, to prevent the issue where a customer closes the browser window before the order is confirmed, even though the payment was successful.
Webhooks are also detailed in the Validating the payment section of the Orders guide.
7. Test cards
You can use the following test cards to test the card payment flow in sandbox:
| Card number | Expiration date | CVC | Description |
|---|---|---|---|
| 5577 0000 5577 0004 | 03/30 | 737 | ✅ Successful payment |
| 5454 5454 5454 5454 | 03/30 | 737 | ✅ Successful payment with 3DS |
♻️ To test the redirect 3DS flow, provide the following value for billingAddress.email when creating the Montonio Order: redirect-3ds-test@montonio.com. Then, use the 3DS card mentioned above.
❌ To test a failed card payment, use the above cards and simply enter an invalid CVC. You can also use the 3DS card with a correct CVC and fail the 3DS authentication.
8. Re-initializing the checkout
If your checkout can re-render the container element after MontonioCheckout has already been initialized, you must destroy the previous instance before creating a new one. Otherwise the old iframe and its event listeners stay attached to the page, leaving multiple checkout instances competing over the same flow.
This applies whenever the initialization code can run more than once on the same page load, for example:
- Single-page apps that re-mount the checkout on route changes
- Checkouts that re-render on AJAX or fragment refreshes (e.g. WooCommerce refreshes its checkout fragments when the cart or shipping method changes)
- Conditionally rendering the card form when the shopper toggles between payment methods
Keep a reference to the current instance and call destroy() on it before re-initializing:
let montonioCheckout = null;
async function initializeMontonioCheckout(sessionUuid) { // Tear down a previous instance before creating a new one if (montonioCheckout) { try { montonioCheckout.destroy(); } catch (e) { console.error('Error destroying previous Montonio checkout instance', e); } montonioCheckout = null; }
// checkoutOptions as defined in step 3 montonioCheckout = new MontonioCheckout(checkoutOptions); await montonioCheckout.initialize('#montonio-checkout-container');}Calling destroy() unmounts the Montonio iframe and removes the SDK’s event listeners. Wrapping it in a try/catch ensures that a failed teardown of a stale instance never blocks re-initialization.