Vezmo App

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.

Note: because Elements renders the payment fields inside your page, it loads a third-party tokenization library into your site (the industry-standard, PCI-isolated card field). That request is visible in your browser's network tab. If you need a checkout that keeps the entire payment surface inside a Vezmo-owned origin, use the embedded checkout (iframe) or the Vezmo-hosted checkout instead.

How it works

  1. On your server, create a payment session with your VezmoPay API key and pass the returned clientToken to the page.
  2. 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.

Server
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 — required
  • country — required (ISO code, e.g. US)
  • postalCode — required (ZIP / postal code)
  • email — required for bank/ACH; recommended otherwise
  • line1, 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.

Browser
<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.

Address element
<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 automatically

Split 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.

Split card fields
<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).

Session payload
// 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.

Saved cards
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.