Payments
Elements — build your own checkout with composable fields
Elements lets you build your own checkout UI and drop in a secure VezmoPay payment field. The customer's card details are captured inside that field and tokenized directly to the processor — they never touch your servers, so you stay PCI SAQ‑A (the lightest level). Use this when you want full control of the layout and styling; use Secure Payments if you'd rather embed a ready-made form.
How it works
- On your server, create a payment session with your VezmoPay API key and pass the returned
clientTokento the page. - On the page, load
vezmo-elements.js, mount the payment field into your own element, and confirm.
1. Create a session (server) → clientToken
Authenticate (two-step exchange — see Authentication), then create a secure-payment session. The amount is set here, server-side (it can never be changed from the browser). Collect your customer's name and email on your own form and pass them in client.
curl -X POST https://api.vezmo.com/api/v1/merchant/secure-payments \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"amount": 49.00,
"currency": "USD",
"client": {
"name": "Ada Lovelace",
"email": "ada@example.com",
"country": "US",
"postalCode": "94103"
},
"successUrl": "https://your-store.com/return"
}'
# → { "data": { "clientToken": "vzcs_..." } }Required customer fields
Every payment must include the customer's name, country, and postal / ZIP code. An email is additionally required for bank (ACH) payments. Requests missing a required field are rejected with 400.
name— requiredcountry— required (ISO code, e.g.US)postalCode— required (ZIP / postal code)email— required for bank/ACH; recommended otherwiseline1,city,state,phone— optional but recommended (a complete address improves fraud screening)
How we use it: for a normal card payment we forward only the customer's name and address to the card network for verification (AVS / fraud) — we do not share their email or phone. Email is forwarded only for bank (ACH), saved-card, and wallet payments (bank payments require it for the debit mandate). Every field you provide is kept on your VezmoPay payment record either way.
2. Mount the payment field (browser)
Add a container, load the SDK, then mount. The field is fully themeable via the appearance option.
<div id="vezmo-payment"></div>
<button id="pay">Pay</button>
<script src="https://api.vezmo.com/api/v1/vezmo-elements.js"></script>
<script>
const vezmo = VezmoElements();
vezmo.checkout(CLIENT_TOKEN, {
layout: "tabs", // "tabs" | "accordion"
appearance: {
variables: { colorPrimary: "#503af2", borderRadius: "12px" }
}
}).then(function (session) {
session.mount("#vezmo-payment");
document.getElementById("pay").addEventListener("click", function () {
session.confirm({ returnUrl: "https://your-store.com/return" })
.then(function (r) {
if (r.error) showError(r.error.message);
else if (r.status === "succeeded") showSuccess();
else if (r.status === "processing") showPending(); // e.g. ACH
});
});
});
</script>Payment methods
The payment field automatically offers the methods enabled for the session — cards, wallets (Apple Pay / Google Pay), and bank transfer (ACH) when the amount is in USD and the account supports it (session.bankSupported). You don't need to configure anything — bank transfer simply appears as an option inside the field when it's eligible, and an ACH payment resolves with status: "processing" while it settles (1–2 business days), then emits payment.succeeded via webhook.
Collect a billing address (optional)
Add an Address Element in its own container to collect the customer's billing address. With the all-in-one field it's submitted automatically on confirm(); with split card fields (below) it's attached as the card's billing details.
<div id="vezmo-address"></div>
<div id="vezmo-payment"></div>
// after checkout(...).then(function (session) { ... }):
session.mountAddress("#vezmo-address", { mode: "billing" });
session.mount("#vezmo-payment");
// confirm() now includes the address automaticallySplit card fields (ultra-custom layout)
For full control of the layout, mount Card Number, Expiry and CVC into three of your own elements. This mode is card-only (no wallets or bank — use the all-in-one field for those) and is mutually exclusive with mount(). 3‑D Secure is still handled automatically.
<div id="card-number"></div>
<div id="card-expiry"></div>
<div id="card-cvc"></div>
<div id="vezmo-address"></div> <!-- optional billing address -->
<button id="pay">Pay</button>
<script src="https://api.vezmo.com/api/v1/vezmo-elements.js"></script>
<script>
VezmoElements().checkout(CLIENT_TOKEN).then(function (session) {
session.mountCardFields({
number: "#card-number",
expiry: "#card-expiry",
cvc: "#card-cvc"
});
session.mountAddress("#vezmo-address", { mode: "billing" }); // optional
document.getElementById("pay").addEventListener("click", function () {
// billing details come from the Address Element (and/or billingDetails below)
session.confirm({
returnUrl: "https://your-store.com/return",
billingDetails: { email: "ada@example.com" }
}).then(function (r) {
if (r.error) showError(r.error.message);
else if (r.status === "succeeded") showSuccess();
});
});
});
</script>3-D Secure
confirm() handles 3‑D Secure automatically (frictionless or a challenge). Some challenges redirect the customer, so always pass a returnUrl; cards usually complete inline. Regulatory SCA (EU and others) always applies regardless of your account settings.
Result & webhooks
confirm() resolves with { status, error?, paymentIntentId? } where status is succeeded, processing (e.g. ACH settling), or failed. The payment is settled by Vezmo server-side and emits the same webhooks as every other VezmoPay charge — payment.succeeded, payment.refunded, and the dispute events. See Webhooks. Never rely on the browser result alone for fulfilment — confirm via the webhook.
Theming (appearance)
Pass an appearance object to match your brand: theme (light/night), variables (colorPrimary, borderRadius, fontFamily, and more), and fine-grained rules. The session default follows the theme you set when creating it.
Saved cards (returning customers)
A returning customer can pay with a card they saved on a previous order — no re-typing. Cards are saved to the VezmoPay network, recognized by the customer's email, and every reuse is gated by a 6-digit code emailed to the saved address (so a leaked email can never charge a saved card).
1. Let a customer save their card
When you create the session, set saveCard: true only after the customer ticks a “save my card” box (and pass their email in client). A successful card payment then saves the card for reuse. The checkout payload echoes session.canSaveCard.
Saving a card always triggers 3‑D Secure — even if 3DS is otherwise off for your account. A card kept on file is established with strong customer authentication; later reuse is charged off-session (no further 3DS prompt).
// server, at session create:
{ "amount": 49.00, "currency": "USD",
"client": { "name": "Ada Lovelace", "email": "ada@example.com",
"country": "US", "postalCode": "94103" },
"saveCard": true }2. Offer saved cards on your next checkout
For a new session with the same customer email, list their reusable cards, let them pick one, verify the emailed code, and charge — all without mounting a card field.
VezmoElements().checkout(CLIENT_TOKEN).then(async function (session) {
const cards = await session.savedMethods();
// [{ savedCardId, brand, last4, expMonth, expYear, maskedEmail }]
if (cards.length) {
const card = cards[0];
// send the code to the saved email, then verify what the customer types
await session.requestSavedOtp(card.savedCardId); // → { maskedEmail }
await session.verifySavedOtp(card.savedCardId, code);
const r = await session.paySaved(card.savedCardId); // → { status }
if (r.status === "succeeded") showSuccess();
else if (r.status === "failed") showError(r.error.message);
} else {
session.mount("#vezmo-payment"); // no saved card → normal flow
}
});Saved cards are per-merchant: a customer sees a saved card at a given store only after they've paid that store once. The list is masked (brand + last 4 only) and contains no processor identifiers.
Test cards
4242 4242 4242 4242- success
4000 0000 0000 3220- 3‑D Secure required
4000 0000 0000 0002- declined
4000 0000 0000 0259- disputed (fraudulent)
Going live
- Add your production domain to your Trusted Origins (Developer Settings).
- Your account must be approved to collect payments.
- Use your live API key; the SDK loads the correct (live) keys automatically.