Skip to main content

Standard flow: Create payment first

Rootline offers multiple flavours of the Checkout Components. Depending on your needs, you may opt for the Standard or Deferred flow. Continue reading this page if you have read Choose your integration and decided that the Standard option suits your needs best.

Overview

The Standard implementation is the fastest and simplest way to accept payments. Your backend creates a payment with Rootline first, then your frontend initializes the SDK with the payment_session_secret to render the payment form. This approach is ideal when the payment amount is known before the customer reaches checkout.

These are the steps you need to follow:

  1. Your backend sends a POST /payments request to Rootline to create the payment and receive a payment_session_secret;
  2. Your backend passes the payment_session_secret to your frontend;
  3. Your frontend initializes Rootline.js with the payment_session_secret;
  4. Using the SDK, you render form(s) to the customer;
  5. Your customer enters their payment details and submits the form;
  6. Your frontend calls components.submit() to validate the input, then rootline.confirmPayment() to complete the payment;
  7. The SDK handles 3DS authentication if required and redirects the customer to your return_url.

And here is a high-level overview of those steps:

Implementation

The following sections explain each step in detail. For complete, working code you can copy into your project, see Sample implementation.

Create a payment on your backend

Make a POST /payments request to Rootline's Payments API with create_payment_session_secret set to true. The response will include a payment_session_secret that you pass to your frontend.

POST /payments
curl 'https://payment-api.rootline.com/v1/payments' \
--request POST \
--header 'content-type: application/json' \
--header 'x-api-key: [paste-your-api-key]' \
--header 'rootline-version: 2024-04-23' \
--data '{
"account_id": "[platform-account-id]",
"reference": "your-reference",
"amount": {
"currency": "EUR",
"quantity": "10.00"
},
"create_payment_session_secret": true,
"return_url": "https://your-website.com/[PAYMENT_ID]"
}'

Prepare the frontend

Add rootline.js to your website. You can use the latest version from our CDN.

<script src="https://js.rootline.com/js/rootline.js"></script>
warning

Make sure to always load the SDK from our servers. Do not copy the SDK to your code base.

Create the form

Create a form and DOM container(s) on your checkout page where you want the payment component(s) to be rendered and give them descriptive IDs.

<form id="payment-form">
<div id="wallet-buttons"></div>
<div id="card-form"></div>
<!-- Or use: <div id="payment-methods-list"></div> for all payment methods -->
<button id="pay-button" type="button">Pay</button>
</form>

Initialize the Rootline SDK and components

Initialize the Rootline SDK with the payment_session_secret received from your backend. We recommend doing this after the DOM has loaded.

const rootline = new Rootline();
const components = rootline.components("checkout", {
paymentSessionSecret: "PAYMENT_SESSION_SECRET_FROM_BACKEND"
});

Mount the component

The SDK provides several components you can mount depending on your needs:

Card Form — Secure input fields for card payments

const cardForm = components.create("card-form");
cardForm.mount("#card-form");

Wallet Button — Apple Pay and Google Pay buttons

const walletButtons = components.create("wallet-buttons");
walletButtons.mount("#wallet-buttons");

Payment Methods List — A list of all available payment methods (cards, wallets, and alternative payment methods)

const paymentMethodsList = components.create("payment-methods-list");
paymentMethodsList.mount("#payment-methods-list");

You can mount multiple components on the same page. For example, you might want to show the Wallet Buttons prominently at the top and the Card Form below:

const walletButtons = components.create("wallet-buttons");
walletButtons.mount("#wallet-buttons");

const cardForm = components.create("card-form");
cardForm.mount("#card-form");

Configure payment methods (Payment Methods List only)

When using the payment-methods-list, you can control which payment methods are displayed and in what order. See Customize for details on ordering, excluding, and filtering payment methods.

Complete the payment

The final step is to call components.submit() to validate the payment details, then rootline.confirmPayment() to process the payment. How you trigger these calls depends on which payment method the customer uses.

Card payments

For card payments, you control the flow via your own pay button. When clicked, call submit() to validate, then confirmPayment() to complete:

const payButton = document.getElementById("pay-button");

payButton.addEventListener("click", async function (e) {
e.preventDefault();

const { error } = await components.submit();

if (error) {
console.error("Submit failed:", error.code, error.message);
return;
}

payButton.disabled = true;

const { error: confirmError } = await rootline.confirmPayment({
components
});

if (confirmError) {
console.error("Confirmation failed:", confirmError.code, confirmError.message);
payButton.disabled = false;
}
});

Wallet payments (Apple Pay, Google Pay)

Wallet Buttons trigger their own click events. Listen for the click event using on(), then call submit() and confirmPayment():

walletButtons.on("click", async (event) => {
// event.paymentMethod is "applepay" or "googlepay"
const { error } = await components.submit();

if (error) {
console.error("Submit failed:", error.code, error.message);
return;
}

const { error: confirmError } = await rootline.confirmPayment({
components
});

if (confirmError) {
console.error("Confirmation failed:", confirmError.code, confirmError.message);
}
});

The click event payload contains:

  • paymentMethod — The wallet clicked ("applepay" or "googlepay")
tip

When using both Wallet Buttons and card components, implement both handlers. See Sample implementation with Wallet Buttons for a complete example.

Handle the return URL

After a successful payment (or 3DS authentication), the SDK redirects the customer to your return_url. Rootline automatically replaces [PAYMENT_ID] in your return URL with the actual payment ID.

On your return page, verify the payment status by calling GET /payments/{payment_id} from your server. See Read payment statuses for details on interpreting the response.

Sample implementation

Complete, working code you can copy into your project. This example uses the Card Form, but you can substitute it with the Wallet Buttons or Payment Methods List as shown above. This example assumes your backend has already created a payment and passed the payment_session_secret to the frontend.

<!DOCTYPE html>
<html>
<head>
<title>Checkout</title>
<script src="https://js.rootline.com/js/rootline.js"></script>
</head>
<body>
<form id="payment-form">
<div id="card-form"></div>
<button id="pay-button" type="button">Pay</button>
</form>

<script>
document.addEventListener("DOMContentLoaded", () => {
// 1. Initialize the SDK
const rootline = new Rootline();

// 2. Create components with the payment session secret from your backend
const components = rootline.components("checkout", {
paymentSessionSecret: "PAYMENT_SESSION_SECRET_FROM_BACKEND"
});

// 3. Create and mount the component (card-form, wallet-buttons, or payment-methods-list)
const cardForm = components.create("card-form");
cardForm.mount("#card-form");

// 4. Handle button click
const payButton = document.getElementById("pay-button");

payButton.addEventListener("click", async function (e) {
e.preventDefault();

// 5. Validate and submit the payment details
const { error } = await components.submit();

if (error) {
console.error("Submit failed:", error.code, error.message);
return;
}

// 6. Disable button to prevent duplicate submissions
payButton.disabled = true;

// 7. Confirm the payment — the SDK handles 3DS if required
const { error: confirmError } = await rootline.confirmPayment({
components
});

if (confirmError) {
console.error("Confirmation failed:", confirmError.code, confirmError.message);
payButton.disabled = false;
}
// On success, the SDK redirects to your return_url
});
});
</script>
</body>
</html>

Sample implementation with Wallet Buttons

This example shows how to handle both wallet payments (Apple Pay, Google Pay) and card payments on the same checkout page. Wallet Buttons require event handling since they trigger their own click events.

<!DOCTYPE html>
<html>
<head>
<title>Checkout</title>
<script src="https://js.rootline.com/js/rootline.js"></script>
</head>
<body>
<form id="payment-form">
<div id="wallet-buttons"></div>
<hr />
<div id="card-form"></div>
<button id="pay-button" type="button">Pay with Card</button>
</form>

<script>
document.addEventListener("DOMContentLoaded", () => {
// 1. Initialize the SDK
const rootline = new Rootline();

// 2. Create components with the payment session secret from your backend
const components = rootline.components("checkout", {
paymentSessionSecret: "PAYMENT_SESSION_SECRET_FROM_BACKEND"
});

// 3. Create and mount the wallet button
const walletButtons = components.create("wallet-buttons");
walletButtons.mount("#wallet-buttons");

// 4. Handle wallet button clicks (Apple Pay, Google Pay)
walletButtons.on("click", async (event) => {
console.log("Wallet clicked:", event.paymentMethod);

const { error } = await components.submit();

if (error) {
console.error("Submit failed:", error.code, error.message);
return;
}

const { error: confirmError } = await rootline.confirmPayment({
components
});

if (confirmError) {
console.error("Confirmation failed:", confirmError.code, confirmError.message);
}
// On success, the SDK redirects to your return_url
});

// 5. Create and mount the card component
const cardForm = components.create("card-form");
cardForm.mount("#card-form");

// 6. Handle card payment via pay button
const payButton = document.getElementById("pay-button");

payButton.addEventListener("click", async function (e) {
e.preventDefault();

const { error } = await components.submit();

if (error) {
console.error("Submit failed:", error.code, error.message);
return;
}

payButton.disabled = true;

const { error: confirmError } = await rootline.confirmPayment({
components
});

if (confirmError) {
console.error("Confirmation failed:", confirmError.code, confirmError.message);
payButton.disabled = false;
}
// On success, the SDK redirects to your return_url
});
});
</script>
</body>
</html>