Skip to content

Capture a card

Collect card details in your own page without the card number ever touching your servers. The Aptean Pay JS SDK renders the input fields inside an iFrame that Aptean hosts, and hands you back a token that stands in for the card.

Why it works this way

The fields the payer types into are not yours. They belong to an iFrame served from Aptean's domain, so the card number never enters your page's JavaScript context, never hits your server and never appears in your logs. What you get back is a token.

This is what keeps your PCI scope small. Do not work around it by collecting card numbers yourself and posting them to the API — that is not supported, and it moves the entire PCI burden onto you.

Build the page

1. Create an HTML file

<html>
  <head></head>
  <body>
  </body>
</html>

2. Add a container for the iFrame and a submit button

<div id="creditCardIframe"></div>
<button id="submitButton">Pay</button>

The SDK mounts the card form into the div and wires itself to the button — you do not attach your own click handler to trigger tokenisation.

3. Load the SDK

Use the URL for your environment (full list):

Environment Script URL
Staging https://stg.js.apteansharedservices.com/apteanpay-js/v1
Production https://js.apteansharedservices.com/apteanpay-js/v1
<script src="https://stg.js.apteansharedservices.com/apteanpay-js/v1"></script>

4. Initialise, mount, and handle the token

<script>
  // The SDK takes your public credentials only.
  // It does NOT take your tenant secret — never put that in a browser.
  const apteanPay = ApteanPay(
    '<your api key>',     // x-aptean-apim
    '<your product id>',  // your ERP product id
    '<your tenant id>'    // x-aptean-tenant
  );

  // Optional. See the "Styling the iFrame" guide.
  const customStyle = {
    styles: {
      base: {
        'border-radius': '4px',
        height: '56px',
        'font-size': '16px'
      }
    }
  };

  const cardComponentOptions = {
    customStyle,
    showPlaceholders: true,
    showErrorMessages: true
  };

  const components = apteanPay.components({});
  const cardComponent = components.create('card', cardComponentOptions);

  cardComponent.mount(
    'creditCardIframe', // the div id — no '#'
    '#submitButton'     // the button — with '#'
  );

  apteanPay.createTokenCallback(
    cardComponent,
    {
      name: '<cardholder name>',            // required
      addressLine1: '<address line 1>',
      addressLine2: '<address line 2>',
      addressCity: '<city>',
      addressState: '<state>',
      addressZip: '<postal code>',          // required
      addressCountry: '<ISO country code>', // required
      emailAddress: '<email>',              // required
      phoneCountryCode: '<phone country code>',
      phoneNumber: '<phone number>'
    },
    function (token, error) {
      if (error) {
        console.log(error);
        return;
      }
      // Send token.id to YOUR server. Charge it from there.
      console.log(token);
    }
  );
</script>

Warning

mount() takes the div id without a # and the button selector with one. Getting this the wrong way round is the usual reason the form renders but the button does nothing.

The required holder fields are name, addressZip, addressCountry and emailAddress. Omitting any of them fails validation.

5. Open the page

The rendered Aptean Pay card capture iFrame

Enter a test card number and click the button.

What you get back

A success looks like this — the value you want is id:

{
  "id": "183075a2-41f1-4ba1-895f-e320336c3f59",
  "created": 1746614580,
  "livemode": false,
  "type": "credit-card",
  "used": false
}

On failure you get an array of validation errors instead:

[
  {
    "type": "validation_error",
    "message": "Parameter failed UI validation.",
    "param": "card-number"
  }
]
Error message What it means
Parameter failed UI validation : card-number The card number is not valid.
Parameter failed UI validation : cvv-number The CVV is not valid.
Expected value in [PR, PS, PT, …] addressCountry is not a recognised ISO country code.
Invalid postal code for the country '{addressCountry}' addressZip does not match the format for that country.

Note

If a valid-looking test card is rejected in staging, check it is one of the sanctioned test numbers. Numbers like 4111 1111 1111 1111 are designed to decline and are the most common false alarm.

What to do with the token

The token is short-lived and single-use. It is a handle for "the card the payer just typed", not a stored card.

Send token.id to your own server, then either:

Do not try to store the raw token and reuse it days later; it will have expired.

Next: Capture bank details or Create a payment.