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¶
2. Add a container for the iFrame and a submit 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 |
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¶
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:
- Charge it now — Create a payment, passing it as
paymentMethodId. - Keep it for later — Saved payment methods converts it into a reusable payment method.
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.