Create customer with payment instrument
This tutorial walks through the three calls that turn a new customer plus a raw card into an attached payment instrument you can charge later.
To choose between FramePay and the server-side tokenization endpoint, see When to use FramePay vs server-side. For the token lifecycle, see Token lifecycle.
1. Create the customer
Specify customer fields in a Customers API call. Minimal required set:
firstNamelastNameemailphoneNumberprimaryAddress.customerAddress(a nested object containingaddress,country,city,region, andzip)
For the full customer model, see Customers.
Address field names differ across endpoints:
The customer endpoint uses
primaryAddress.customerAddress.{address, zip}as a nested structure. The tokenization production endpoint uses a flat format:billingAddress.{address, postalCode}. Note that while theaddressfield name matches, the tokenization endpoint requirespostalCodeinstead ofzip. If you collect a single billing form on your site, map it to the correct field names per endpoint since they are not interchangeable.
Example:
curl --location \
"https://staging-api.payments.ai/v1/public-api/organizations/${ORGANIZATION_ID}/customers" \
--header 'Content-Type: application/json' \
--header "Authorization: ApiKey ${API_KEY}" \
--data-raw '{
"firstName": "John",
"lastName": "Doe",
"email": "[email protected]",
"phoneNumber": "123-456-7890",
"primaryAddress": {
"customerAddress": {
"address": "36 Craven St",
"city": "London",
"region": "London",
"country": "GB",
"zip": "WC2N 5NF"
}
}
}'
The response contains the customerId you will use in the attach step.
2. Create the payment instrument token
Pick one of two paths:
- FramePay (recommended for web): Raw card data never reaches your server, keeping your PCI scope at SAQ A.
- Server-side tokenization endpoint: Your server posts the card data directly, which requires SAQ D scope. See Tokenization endpoint.
The rest of this tutorial uses FramePay. The output of either path is a token you pass to step 3.
Prerequisites
organizationId: Provided after merchant registration.publishableKey: Obtain it withGET /v1/organizations/{organizationId}/public-keys/publishable. See the public-keys endpoint.websiteId: Provided after merchant registration.
Create a token with FramePay
Mount the FramePay card field, then call Framepay.createToken to tokenize what the customer typed:
<!doctype html>
<html>
<head>
<link href="https://framepay.payments.ai/framepay.css" rel="stylesheet" />
<script src="https://framepay.payments.ai/framepay.js"></script>
</head>
<body>
<form id="payment-form">
<input id="first-name" placeholder="First Name" />
<input id="last-name" placeholder="Last Name" />
<div id="mounting-point"></div>
</form>
<button id="submit-button">Submit</button>
<script src="index.js"></script>
</body>
</html>
Framepay.initialize({
publishableKey: `${publishableKey}`,
transactionData: {
currency: 'USD',
amount: 10,
},
});
Framepay.on('ready', function () {
const card = Framepay.card.mount('#mounting-point');
});
const submitButton = document.getElementById('submit-button');
submitButton.addEventListener('click', async function () {
try {
const form = document.getElementById('payment-form');
const firstName = document.getElementById('first-name').value;
const lastName = document.getElementById('last-name').value;
const paymentToken = await Framepay.createToken(form, {
billingAddress: { firstName, lastName },
});
console.log(paymentToken);
} catch (error) {
console.log('❌ Create token error:', error);
}
});
If FramePay initialization fails, run the page through a local server, for example with serve:
npx serve
The token you pass to step 3 is the id field of the object returned by Framepay.createToken and logged to the console.
Coinbase (cryptocurrency)
To obtain a Coinbase token, set method: 'cryptocurrency' on Framepay.createToken. The credit-card mount is not required for this method.
Framepay.initialize({
publishableKey: `${publishableKey}`,
transactionData: {
currency: 'USD',
amount: 10,
},
});
// Credit card element is no longer required
// Framepay.on('ready', function () {
// const card = Framepay.card.mount('#mounting-point');
// });
const submitButton = document.getElementById('submit-button');
submitButton.addEventListener('click', async function () {
try {
const form = document.getElementById('payment-form');
const firstName = document.getElementById('first-name').value;
const lastName = document.getElementById('last-name').value;
const paymentToken = await Framepay.createToken(form, {
billingAddress: { firstName, lastName },
method: 'cryptocurrency',
});
console.log(paymentToken);
} catch (error) {
console.log('❌ Create token error:', error);
}
});
3. Attach the token to the customer
Pass the token from step 2 to the attach endpoint. This creates the payment instrument on the customer and consumes the token. See Attach a token to a customer for the full sequence and status transitions.
curl --location \
"https://staging-api.payments.ai/v1/public-api/organizations/${ORGANIZATION_ID}/customers/${CUSTOMER_ID}/payment-instruments" \
--header 'Content-Type: application/json' \
--header "Authorization: ApiKey ${API_KEY}" \
--data '{ "token": "{{tokenValue}}" }'
The response contains a paymentInstrumentId. The new instrument is created in the inactive state and is activated by the customer's first hosted-form payment. See Payment instrument lifecycle.
What's next
- Start a subscription using the new
paymentInstrumentId. See Create a subscription. - Run a one-off charge. See the Transactions API.