Skip to main content
Build a card payment form on your own checkout page with lite elements.

Overview

The Web SDK lets you collect card details on your own checkout page while keeping card data off your systems. The SDK renders each card field inside a secure iframe hosted by lite. Your page controls where the fields appear and how the form behaves. Card numbers, expiry dates, and CVV values are entered directly into lite-hosted fields and never pass through your servers. This keeps your integration eligible for the simplest level of PCI DSS validation. Use the Web SDK when you want full control over your checkout layout. If you prefer a complete payment page hosted by lite, use Checkout or Payment Links instead.

How it works

  1. Your server creates a checkout session with the lite API and receives a session secret.
  2. Your page initializes the SDK with the session secret and mounts the card elements.
  3. The customer enters their card details into the secure fields.
  4. You call lite.pay(). The SDK encrypts the card data inside lite-hosted iframes, tokenizes it, and submits the payment. If the issuer requires 3D Secure, the SDK handles the authentication flow automatically.
  5. Your server confirms the final payment result.

Before you begin

You need:
  • A lite account with a configured channel. See Channel Configuration.
  • Valid API keys.

Build your payment form

1. Load the SDK

Install the SDK from npm:
npm install `@litesa/js-sdk`
Then initialize it with the session secret from your server:
import { lite, PaymentOperationStatus } from '@litesa/js-sdk';

const lite = await lite.init({ clientSecret: sessionSecret });
lite.init fetches the checkout session and resolves when the SDK is ready. The session is available at lite.session, including the amount, currency, and enabled payment methods.

2. Create the elements

Create an elements group, then create one element for each card field:
const elements = lite.elements();

const cardNumberElement = elements.create('cardNumber');
const expiryElement = elements.create('expiry');
const cvvElement = elements.create('cvv');
const cardholderNameElement = elements.create('cardholderName');
cardholderName is optional. The SDK collects values only from the fields you mount.

3. Mount the elements

Add a container for each field to your page:
<div id="cardNumber"></div>
<div id="expiry"></div>
<div id="cvv"></div>
<div id="cardholderName"></div>
Then mount each element into its container. mount returns a promise and clears any existing content in the container:
await cardNumberElement.mount('#cardNumber');
await expiryElement.mount('#expiry');
await cvvElement.mount('#cvv');
await cardholderNameElement.mount('#cardholderName');

4. Track field validity

Each card field emits a change event whenever the customer’s input is re-validated. The payload contains a single valid flag:
cardNumberElement.on('change', ({ valid }) => {
  // enable or disable your pay button
});
The fields validate as the customer types. The card number must pass a Luhn check, the expiry must be a valid MM/YY date that is not in the past, and the CVV must be 3 or 4 digits. Track validity across all mounted fields and enable your pay button only when every field is valid.

5. Submit the payment

When the customer clicks your pay button, call pay and handle the result:
payButton.addEventListener('click', async () => {
  try {
    const result = await lite.pay({ payment_method: 'card' });

    switch (result.status) {
      case PaymentOperationStatus.SUCCESS:
        // show your success state
        break;
      case PaymentOperationStatus.FAILURE:
        // show result.error and let the customer retry
        break;
      default:
        // payment is still processing; confirm from your server
        break;
    }
  } catch (e) {
    // invalid options or SDK errors surface as thrown errors
  }
});
The SDK collects the card details from the mounted fields, encrypts them, tokenizes the card, and submits the payment. Your page never receives the card data. Calling pay while a payment is already in flight returns the same promise, so double clicks do not create duplicate payment attempts. 3D Secure. If the payment requires authentication, the SDK opens the 3D Secure challenge in a modal automatically and resumes when the customer completes it. You do not need to handle the redirect yourself. The pay promise resolves after authentication finishes. To save the card for future payments, pass store_for_future:
const result = await lite.pay({ payment_method: 'card', store_for_future: true });

6. Handle the result

pay resolves with a result object:
{
  status: PaymentOperationStatus;  // 'success' | 'failure' | 'processing'
  payment?: PaymentResponse;       // present on success
  error?: string;                  // present on failure
}
StatusMeaning
successThe payment was authorized or captured. result.payment.id contains the payment ID.
failureThe payment failed, was voided, or was rejected. result.error describes the failure.
processingThe payment has not reached a final status yet. Confirm the outcome from your server.

7. Confirm the payment on your server

A success result tells your page that the payment succeeded in the customer’s browser. Do not fulfill an order based on this alone. Customers can close the tab before your code runs, and client-side code can fail. Always confirm the final payment state from your server by retrieving the session before you fulfill the order.

Pay with a saved card

If the customer has cards stored with lite, retrieve them after initialization:
const instruments = lite.getStoredInstruments('card');
Each stored instrument includes an id and display details (scheme, last_4, expiry_month, expiry_year) you can render in your own UI. To charge a saved card, pass its ID to pay instead of a payment method:
const result = await lite.pay({ instrument_id: instrument.id });

Style the fields

Card fields accept styling options when you create them. Choose a variant, then override individual properties:
const cardNumberElement = elements.create('cardNumber', {
  variant: 'stylized',
  style: {
    fontSize: '16px',
    padding: '12px',
    borderRadius: '8px',
    ':focus': { borderColor: '#0570de' },
    '::placeholder': { color: '#a0a0a0' },
  },
});
VariantBehavior
plainResets the field styling. You control the appearance through your wrapper DOM and your own style values.
stylizedStarts from the SDK’s default appearance and applies your overrides on top.
Omit the options to use the default themed appearance. Supported base properties: color, backgroundColor, fontFamily, fontSize, fontWeight, fontStyle, letterSpacing, lineHeight, textAlign, padding, borderColor, borderWidth, borderStyle, borderRadius. Pseudo-state blocks are supported for :focus, :hover, :invalid, and ::placeholder, each with a subset of these properties. Unsupported values are ignored.

Clean up

Call destroy on an element to remove it from the page, and lite.destroy() to tear down the SDK instance, for example when the customer navigates away from your checkout in a single-page app:
cardNumberElement.destroy();
lite.destroy();

Test your integration

Use your test API keys and lite test cards to verify your form end to end. See Testing.