Guide

Tokenize cards with Finix.js

Pay-Pi processes payments through Finix. To keep raw card data off your servers (and shrink your PCI scope), you collect the buyer's card in the browser with Finix.js, which returns a single-use token. You then send only that token to your backend, which charges it with POST /v1/payments. This page shows the browser side end to end.

Always load Finix.js from Finix's CDNhttps://js.finix.com/v/2/finix.js. Never self-host or bundle it; doing so pulls raw card data into your page's origin and expands your PCI scope. The applicationId and environment used below are public, browser-safe values. Your secret lc_live_… key is never used in the browser.

1. Get your public Finix config

Finix.js needs two public values: your application id (AP…) and the environment. Both come from GET /v1/meta — no auth required — so you can read them at runtime instead of hardcoding. They're also shown in your dashboard's Developer / API access panel.

curl https://connect.pay-pi.com/v1/meta
{
  "service": "connect.pay-pi.com",
  "processor": "DUMMY_V1",
  "finixEnvironment": "sandbox",
  "finixApplicationId": "APxxxxxxxxxxxxxxxxxxxxxx",
  "finixApiVersion": "…",
  "currency": "USD"
}
finixApplicationIdYour Finix application id (AP…). Public — safe in the browser.
finixEnvironmentsandbox or live. Map liveprod for Finix.js (below).

2. Load Finix.js and add a container

Load the v2 script and drop an empty element where the hosted fields will mount:

<script src="https://js.finix.com/v/2/finix.js"></script>
<div id="finix-form"></div>

3. Mount the card form

Create a PaymentForm, passing the element id, the environment, your application id, and the payment methods to collect. Finix.js v2 environments are "sandbox" or "prod" — map the "live" value from /v1/meta to "prod".

// 1. Read the public config once (or inject it from your server-rendered page).
const meta = await fetch("https://connect.pay-pi.com/v1/meta").then((r) => r.json());

// Finix.js v2 environments are "sandbox" or "prod".
// /v1/meta reports "sandbox" | "live" — map "live" -> "prod".
const finixEnv = meta.finixEnvironment === "live" ? "prod" : "sandbox";

// 2. Mount the hosted card fields.
const form = Finix.PaymentForm("finix-form", finixEnv, meta.finixApplicationId, {
  paymentMethods: ["card"],
});
Bank accounts (ACH) tokenize the same way — pass paymentMethods: ["bankAccount"] instead of ["card"]. Everything else is identical; the backend charge in step 5 just receives a bank token. Pay-Pi detects the rail and applies ACH pricing.

4. Get the token

Tokenizing returns a single-use token id at response.data.id (prefixed TK…, valid ~30 minutes). There are two ways to submit:

Option A — automatic (simplest)

Provide an onSubmit callback and Finix.js renders its own submit button and handles the submit for you:

Finix.PaymentForm("finix-form", finixEnv, meta.finixApplicationId, {
  paymentMethods: ["card"],
  onSubmit: (error, response) => {
    if (error) return showError(error);
    const finixToken = response?.data?.id;   // e.g. "TKxxxxxxxxxxxxxxxx"
    postToMyBackend({ finixToken, firstName, lastName, email });
  },
});

Option B — manual (your own button)

Omit onSubmit and call form.submit(callback) from your own button when you're ready — useful when tokenization is one step in a larger checkout:

// Your own "Pay" button calls submit(); the token is on response.data.id.
payButton.addEventListener("click", () => {
  form.submit((error, response) => {
    if (error) return showError(error);
    const finixToken = response.data.id;      // single-use, ~30 min TTL
    postToMyBackend({ finixToken, firstName, lastName, email });
  });
});

5. Charge the token from your backend

Send the finixToken to your server, which calls POST /v1/payments with your secret X-Api-Key and a required Idempotency-Key. When you pass a token, buyer (first/last name; email optional) is required. The browser must never call Pay-Pi directly with your secret key.

curl -X POST https://connect.pay-pi.com/v1/payments \
  -H "X-Api-Key: lc_live_..." \
  -H "Idempotency-Key: order-1001" \
  -H "Content-Type: application/json" \
  -d '{
    "amountCents": 4999,
    "currency": "USD",
    "finixToken": "TKxxxxxxxxxxxxxxxx",
    "buyer": { "firstName": "Ada", "lastName": "Lovelace", "email": "ada@example.com" }
  }'

See Accept an online payment for the full request/response shape, and the API reference for every field.

Want a complete, runnable example? The paypi-sample-checkout repo is a zero-dependency Node app that does exactly this — Finix.js tokenization + a backend POST /v1/payments + refund. Clone it, add your sandbox key, and run node server.mjs.

Test values (sandbox)

On the sandbox, drive the outcome with the charge amount (use any Finix test card in the form):

4999$49.99 — approved
51$0.51 — declined
1$0.01 — call issuer
Prefer to click, not curl? Signed-in merchants can run a full tokenize → charge → refund against their own sandbox from the dashboard's Developer area — no code required. See Test your integration.

Going live

Because you read the environment and application id from /v1/meta, no frontend change is needed at go-live: the same call returns finixEnvironment: "live" (which you map to "prod") and your live application id automatically.

Reference

Finix's own documentation for the browser SDK:

Next steps