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.
https://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 live → prod 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"],
}); 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.
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 — approved51$0.51 — declined1$0.01 — call issuerGoing 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:
- Finix.js reference
- Tokenization forms
- Finix.js v1 → v2 migration — if you're still on the v1
CardTokenForm
Next steps
- Accept an online payment — the backend charge call
- API reference — every endpoint and field
- Onboarding & readiness — charges are gated until approved
- Full working sample — a runnable Node app