Finix JS

v1.0.0

Finix.js integration for accepting card and ACH bank payments on the web. Embed the hosted payment form, tokenize card/bank data in the browser, and claim to...

0· 68·0 current·0 all-time

Install

OpenClaw Prompt Flow

Install with OpenClaw

Best for remote or guided setup. Copy the exact prompt, then paste it into OpenClaw for jbryant/finix-js.

Previewing Install & Setup.
Prompt PreviewInstall & Setup
Install the skill "Finix JS" (jbryant/finix-js) from ClawHub.
Skill page: https://clawhub.ai/jbryant/finix-js
Keep the work scoped to this skill only.
After install, inspect the skill metadata and help me finish setup.
Required env vars: FINIX_USERNAME, FINIX_PASSWORD, FINIX_APPLICATION_ID, FINIX_MERCHANT_ID
Use only the metadata you can verify from ClawHub; do not invent missing requirements.
Ask before making any broader environment changes.

Command Line

CLI Commands

Use the direct CLI path if you want to install manually and keep every step visible.

OpenClaw CLI

Bare skill slug

openclaw skills install finix-js

ClawHub CLI

Package manager switcher

npx clawhub@latest install finix-js
Security Scan
Capability signals
CryptoCan make purchasesRequires sensitive credentials
These labels describe what authority the skill may exercise. They are separate from suspicious or malicious moderation verdicts.
VirusTotalVirusTotal
Benign
View report →
OpenClawOpenClaw
Benign
high confidence
Purpose & Capability
The name/description and the SKILL.md all describe a Finix.js web payments integration. The four required env vars (FINIX_USERNAME, FINIX_PASSWORD, FINIX_APPLICATION_ID, FINIX_MERCHANT_ID) are relevant to this purpose. Minor inconsistency: no primary credential is declared even though API username/password are the highest-sensitivity credentials in the list — this is odd but not a functional mismatch.
Instruction Scope
The runtime instructions focus on embedding the hosted payment form, client-side tokenization, and the expected server-side token-exchange flow. The document explicitly warns that API credentials are server-side only and does not instruct the agent to read unrelated system files or secrets.
Install Mechanism
This is an instruction-only skill with no install spec and no code files; nothing is written to disk by an installer, which minimizes install-time risk.
Credentials
All requested env vars are directly relevant to Finix integration. However, FINIX_USERNAME and FINIX_PASSWORD are full API credentials that can perform server-side actions (creating payment instruments, transfers/authorizations). Supplying them to the agent grants it the ability to act as your server for payments — a high-sensitivity capability that should be tightly controlled (use sandbox creds, rotate, limit scope).
Persistence & Privilege
always is false and the skill is user-invocable; it does not request persistent platform-level privileges or modifications to other skills' configurations.
Assessment
This skill appears to do what it claims, but it requests high-impact credentials. Before installing: (1) prefer providing sandbox/test FINIX_USERNAME/FINIX_PASSWORD and test Application/Merchant IDs, not production creds; (2) confirm the skill owner/source (unknown origin here) and prefer official Finix-provided integrations; (3) ensure the agent's runtime environment stores these secrets securely and is restricted; (4) rotate credentials after testing and audit any API activity (webhooks, logs) for unexpected charges; (5) verify the skill's behavior in a isolated environment first and ensure the SKILL.md's server-side guidance is followed so credentials never go to the browser.

Like a lobster shell, security has layers — review code before you run it.

Runtime requirements

💳 Clawdis
EnvFINIX_USERNAME, FINIX_PASSWORD, FINIX_APPLICATION_ID, FINIX_MERCHANT_ID
latestvk97eherwnabs2fyhk99fak8pds853q4z
68downloads
0stars
1versions
Updated 1w ago
v1.0.0
MIT-0

Finix.js Integration Skill

This document teaches how to integrate Finix.js into a customer website to accept card and bank (ACH) payments via Finix. It covers the Finix APIs involved, the account/application setup required, and the steps to embed and use the library.


1. What Finix.js Is

Finix.js is a client-side, hosted payment form. It injects an iframe into the merchant's page that collects sensitive PCI/NACHA data (card number, expiration, security code, bank account/routing numbers) and exchanges it directly with the Finix Payments API for a single-use token. The merchant's backend then uses that token to create a reusable Payment Instrument and charge the customer via Transfer or Authorization API calls. Because raw card/bank data never hits the merchant's servers, this flow keeps cards in SAQ-A PCI scope, and is the only supported pattern for Finix web integrations.


2. Prerequisites: Finix Account Setup

Before writing any code, the merchant must have:

  1. A Finix account (Dashboard access at https://finix.payments-dashboard.com/).
  2. An Application ID (APxxxxxxxxxxxxxxxxxxxxxx). This identifies the platform/application that owns the tokens. It is passed to Finix.PaymentForm(...). Application IDs are environment-scoped — sandbox and production IDs are different.
  3. A Merchant ID (MUxxxxxxxxxxxxxxxxxxxxxx). This represents the entity being paid. Needed server-side when creating Authorizations/Transfers, and client-side if using Finix.Auth for fraud session tracking.
  4. API credentials (username + password used as HTTP Basic Auth). These are server-side only — never ship them to the browser.

Environments

Environment stringPayments API host
prod / production / livehttps://finix.live-payments-api.com
sandbox / sbhttps://finix.sandbox-payments-api.com

Always build/test in sandbox first. Switching to prod is a one-line change (environment string + Application ID).


3. The End-to-End Payment Flow

  1. The merchant page loads finix.js and calls Finix.PaymentForm(...), which mounts an iframe into a container <div>.
  2. The buyer enters card or bank data into the iframe's inputs.
  3. On submit, the iframe POSTs the sensitive data directly to the Finix Payments API at /applications/:app_id/tokens and receives a single-use token (TKxxxx...).
  4. The token (plus a fraud_session_id from Finix.Auth and any checkout context) is POSTed to the merchant's backend.
  5. The backend uses server-side API credentials to create a Buyer Identity, exchange the token for a Payment Instrument, and create a Transfer or Authorization (see "Server-Side: Claiming the Token" below).

Finix.js handles steps 1–3. Steps 4–5 are the merchant's responsibility.


4. Embedding Finix.js

4a. Minimal integration

<div id="finix-form"></div>
<script src="https://js.finix.com/v/2/finix.js"></script>
<script>
  const form = Finix.PaymentForm(
    "finix-form",                 // container: id string OR an HTMLElement
    "sandbox",                    // environment
    "APgPDQrLD52TYvqazjHJJchM",   // Application ID
    {
      onSubmit: (error, response) => {
        if (error) {
          console.error(error);
          return;
        }
        const token = response.data.id; // "TKxxxx..."
        // POST token to your backend to finish the charge.
        fetch("/api/charge", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ ...paymentData, token }), // include other checkout data (amount, currency, buyer info, cart id, etc.)
        });
      },
    }
  );
</script>

The library attaches window.Finix on load and exposes:

  • Finix.PaymentForm(element, env, applicationId, options) — renders card + bank form (default is cards only; enable bank via paymentMethods: ['card', 'bank']).
  • Finix.Auth(env, merchantId, callback) — initializes Sift fraud session tracking (see "Fraud Session" below).
  • Aliases: Finix.TokenForm, Finix.CardTokenForm, Finix.BankTokenForm all delegate to PaymentForm.

element may be either an element ID (string) or an HTMLElement — the latter is required when mounting inside a Shadow DOM / web component.

4b. The form instance

PaymentForm returns an instance with:

  • form.submit((error, response) => {...}) — trigger tokenization from a custom button. See "Custom submit button" below.
  • form.theme(themeName) — swap theme at runtime.

4c. Options Reference

Pass as the 4th argument to PaymentForm. All are optional. This section is the longest because options are where most integrations get stuck — read carefully before customizing.

4c.1 Quick index

OptionTypeDefaultPurpose
paymentMethods('card' | 'bank')[]['card']Which payment method tabs to render.
showAddressbooleanfalseShow the billing address section.
showLabelsbooleantrueRender field <label> elements above inputs.
labelsRecord<field, string>{}Override default label text per field.
showPlaceholdersbooleantrueRender input placeholders.
placeholdersRecord<field, string>{}Override placeholder text per field.
hideFieldsstring[][]Hide specific fields (deep dive below).
requiredFieldsstring[][]Force normally-optional fields to be required (deep dive below).
requireSecurityCodebooleantrueRequire CVV/CVC on cards.
confirmAccountNumberbooleantrueRequire bank account number to be typed twice.
hideErrorMessagesbooleanfalseSuppress inline error text (fields still validate).
errorMessagesRecord<field, string>{}Custom inline error copy per field.
hidePotentialIssueMessagesbooleanfalseHide soft "are you sure?" warnings (e.g. unusual card length).
defaultValuesRecord<field, string>{}Prefill non-sensitive fields (deep dive below).
enableDarkModebooleanfalseRespect prefers-color-scheme: dark.
themestring'finix'Brand preset (deep dive below).
stylesStylesCfg{}Per-state CSS overrides (deep dive below).
fontsArray<{fontFamily,url,format}>[]Inject custom @font-face rules (deep dive below).
submitLabelstring'Submit'Built-in submit button label.
plaidLinkSettings{ displayName, language, countries }Enable Plaid embedded bank linking (see "Plaid Bank Linking" below).
onLoad() => voidFires when the iframe has mounted and initialized.
onUpdate(state, binInfo, hasErrors) => voidFires on every field change.
onSubmit(error, response) => voidFires after tokenization completes.

4c.2 The complete field name list

Every option that takes a field-keyed record (labels, placeholders, hideFields, requiredFields, errorMessages, defaultValues) uses the same set of canonical field names. Use these exactly — arbitrary keys are silently ignored.

SectionField nameNotes
Cardcard_holder_nameCardholder name. Optional by default.
CardnumberCard number. Always required when card is active.
Cardexpiration_dateMM/YY. Always required when card is active.
Cardsecurity_codeCVV/CVC. Required unless requireSecurityCode: false or hideFields: ['security_code'].
Bank (USA + CAN)account_holder_nameAccount holder name. Required by default when bank is active.
Bank (USA + CAN)account_numberAccount number. Required when bank is active.
Bank (USA + CAN)account_typePERSONAL_CHECKING | PERSONAL_SAVINGS | BUSINESS_CHECKING | BUSINESS_SAVINGS. Required when bank is active.
Bank (USA)bank_codeUS ABA routing number (9 digits). Required when country is USA.
Bank (CAN)transit_number5-digit transit. Required when country is CAN; replaces bank_code.
Bank (CAN)institution_number3-digit institution. Required when country is CAN; replaces bank_code.
Addressaddress_line1Street.
Addressaddress_line2Apt/suite.
Addressaddress_cityCity.
Addressaddress_regionState/province. Dropdown is auto-populated for USA/CAN.
Addressaddress_countryDropdown; dictates bank field set and postal-code format.
Addressaddress_postal_codeZIP (US: 12345 or 12345-6789) / postal code (CAN: A1A 1A1).

4c.3 paymentMethods

paymentMethods: ['card']            // card only (default)
paymentMethods: ['bank']            // ACH/bank only
paymentMethods: ['card', 'bank']    // tabs for both

When both are present, a tab bar appears and the user chooses. The active method determines which required fields are enforced. Bank fields vary by address_country — selecting CAN swaps bank_code for transit_number + institution_number.

4c.4 hideFields (deep dive)

Removes a field from the DOM and from validation. The tokenization payload simply omits it.

Fields that are safe to hide:

  • card_holder_name — optional for cards.
  • account_holder_name — hideable for bank; if hidden the default-required check is skipped for it.
  • security_code — equivalent to setting requireSecurityCode: false; produces a token without CVV, but many issuers will decline the downstream charge. Only hide if your Finix account/use-case permits card-on-file without CVV.
  • Any address_* field — all address fields are optional at the tokenization layer. To hide the entire address section at once, prefer showAddress: false (or simply omit it — false is the default).

Fields you should NOT hide (will break tokenization):

  • number, expiration_date — required to mint a card token.
  • account_number, account_type, and bank_code (USA) / transit_number+institution_number (CAN) — required to mint a bank token.

Common hide patterns:

// Show address but skip the optional second line
hideFields: ['address_line2'],

// CVV-less card-on-file
hideFields: ['security_code'],

// Drop name fields (name captured elsewhere in your checkout)
hideFields: ['card_holder_name', 'account_holder_name'],

If the user hides a field that was also listed in requiredFields, the hide wins — the field does not render and is not validated.

4c.5 requiredFields (deep dive)

What it does: promotes a normally-optional field to required. It does not add fields that aren't already rendering — you still need showAddress: true (or no matching hideFields entry) for address fields to appear.

Fields that are already required by default (no need to list them):

  • Card: number, expiration_date, security_code (unless requireSecurityCode: false).
  • Bank (USA): account_holder_name, account_number, bank_code, account_type.
  • Bank (CAN): account_holder_name, account_number, transit_number, institution_number, account_type.

Fields you can add to requiredFields:

  • card_holder_name
  • account_holder_name (redundant for bank — already required by default)
  • name — legacy alias that maps to card_holder_name or account_holder_name depending on the active payment method. Prefer the explicit names in new code.
  • address_line1, address_line2, address_city, address_region, address_country, address_postal_code

Anything else is silently dropped (only the list above is honored).

Interactions to be aware of:

  • requireSecurityCode: false + requiredFields: ['security_code'] — the requiredFields entry is not honored for security_code. Use requireSecurityCode as the switch.
  • hideFields + requiredFields for the same field — hide wins.
  • confirmAccountNumber: true (default) implicitly requires account_number_confirmation when bank is active; you don't list it manually.

Typical billing-address collection for AVS/fraud checks:

AVS itself only evaluates address_line1 + address_postal_code, but most merchants collect the full address for records and fraud scoring:

showAddress: true,
requiredFields: [
  'address_line1',
  'address_city',
  'address_region',
  'address_postal_code',
  // address_country is a dropdown with a default, so it's not strictly needed here
],

For a minimal AVS-only setup:

showAddress: true,
hideFields: ['address_line2', 'address_city', 'address_region', 'address_country'],
requiredFields: ['address_line1', 'address_postal_code'],

4c.6 defaultValues (deep dive)

Prefills non-sensitive fields so the buyer doesn't retype data your checkout already has.

Allowed fields:

  • card_holder_name, account_holder_name
  • address_line1, address_line2, address_city, address_region, address_country, address_postal_code

Never prefill (the iframe ignores these even if passed):

  • number, expiration_date, security_code — would break PCI scope.
  • account_number, bank_code, transit_number, institution_number — would break NACHA scope.

Format requirements:

  • address_country → ISO-3 alpha codes ('USA', 'CAN', 'GBR', …). The dropdown's list comes from countryNamesOptions and is ACH-restricted to USA/CAN when bank is the active method.
  • address_region → two-letter state/province abbreviation for US ('CA', 'NY') and CAN ('ON', 'BC'). Other countries: free text.
  • address_postal_code → already-formatted string ('94105', '94105-1234', 'M5V 3L9'). The formatter will re-normalize input, but passing the canonical form avoids confusing the buyer.

Example:

defaultValues: {
  card_holder_name: 'Ada Lovelace',
  address_line1: '50 Beale St',
  address_line2: 'Suite 600',
  address_city: 'San Francisco',
  address_region: 'CA',
  address_country: 'USA',
  address_postal_code: '94105',
},

4c.7 theme vs styles

theme is a preset that changes the whole form's look (colors, borders, button). styles is a targeted override on top of whichever theme is active. Use theme for brand-alignment, then nudge with styles.

Built-in themes: finix (default), amethyst, sapphire, topaz, ruby, emerald, midnight, elevated. Switch at runtime via form.theme('ruby').

4c.8 styles (deep dive)

styles is an object with two top-level branches — default (always applied) and dark (applied only when enableDarkMode is on and the user/browser is in dark mode; merged over default).

Under each branch, each target element exposes a set of states. States are also applied additively in this order: defaultsuccess (if valid) → error (if invalid & errors shown) → focused (if focused).

Full shape:

styles: {
  default: {
    form: {
      default: { /* container around entire form */ },
    },
    section: {
      default: { /* each section wrapper (card/bank/address) */ },
    },
    sectionHeader: {
      default: { /* section heading text */ },
      focused: { /* when a field inside is focused */ },
    },
    input: {
      default: { /* all inputs */ },
      success: { /* input with valid value */ },
      error:   { /* input with validation error and showErrors=true */ },
      focused: { /* input while focused */ },
    },
    submitButton: {
      default:  { /* built-in submit button */ },
      disabled: { /* when the form has errors */ },
    },
  },
  dark: {
    // same tree — these get merged on top of `default` when dark is active
    input: { default: { /* ... */ } },
    // ...
  },
},

Values are camelCased CSS strings (same as React/inline style objects), not CSS declarations. Use string values everywhere, including numeric-looking ones:

input: {
  default: {
    border: '1px solid #7D90A5',
    borderRadius: '12px',
    padding: '12px 14px',
    fontSize: '16px',
    fontFamily: '"Effra", sans-serif',
  },
  focused: {
    border: '1px solid #3B82F6',
    boxShadow: '0 0 0 3px rgba(59,130,246,0.2)',
  },
  error: {
    border: '1px solid #DC2626',
    color: '#DC2626',
  },
  success: {
    border: '1px solid #16A34A',
  },
},

State precedence example. An input that is both focused and in error ends up with the merge default → error → focused. So if both define border, focused wins. To force error styling to trump focus, re-declare border inside the error block of the dark/default tree, or move the style onto outline so they don't collide.

Dark mode notes. dark only applies when you set enableDarkMode: true. Everything inside dark is a patch over default — you don't have to redeclare unchanged rules.

What you can't do. You can't inject arbitrary CSS, pseudo-classes (:hover), or selectors. If you need hover styles or animation, do it by adjusting default/focused or filing a feature request.

4c.9 fonts

Injects @font-face rules into the iframe so the CSS fontFamily values in styles actually resolve. Each font object:

fonts: [
  {
    fontFamily: 'Effra',
    url: 'https://cdn.example.com/fonts/effra.woff2',
    format: 'woff2',
  },
],

Constraints enforced by the iframe:

  • url must be served over https:// — anything else is rejected with a console warning.
  • format must be one of woff, woff2, truetype, opentype, embedded-opentype, svg.
  • fontFamily is sanitized (strips anything that isn't word/space/comma/hyphen) to prevent CSS injection.

After the font is loaded, reference it in styles via the sanitized family name: fontFamily: '"Effra", sans-serif'.

4c.10 Error messages

Two independent switches:

  • hideErrorMessages: true — hides inline error text. Fields still validate and the form still blocks submission; you just don't show the red copy.
  • errorMessages: { field: 'custom string' } — per-field override. Still only shown when hideErrorMessages is false. The keys match the canonical field names listed in "The complete field name list" above.

hidePotentialIssueMessages: true suppresses the soft warnings like "This Visa number is valid, but Visa typically issues 13 or 16 digit cards…" without affecting hard validation.

4c.11 Callbacks

onLoad: () => { /* iframe mounted & initialized */ },

onUpdate: (state, binInfo, hasErrors) => {
  // state     — { [fieldName]: { value, errors, isDirty, isFocused, ... } }
  // binInfo   — { cardBrand, bin } once enough digits are entered
  // hasErrors — true while any required/invalid field blocks submission
  // Typical use: enable/disable your custom submit button.
},

onSubmit: (error, response) => { /* see "The onSubmit Response" below for shape */ },

Callbacks live on the parent and are invoked in response to iframe messages — they are stripped from the options object before it crosses the postMessage boundary.

4d. Custom submit button

Omit the built-in button by providing your own and calling form.submit:

<div id="finix-form"></div>
<button id="pay-btn" disabled>Pay</button>
<script>
  const form = Finix.PaymentForm("finix-form", "sandbox", "APxxx...", {
    onUpdate: (_state, _bin, hasErrors) => {
      document.getElementById("pay-btn").disabled = hasErrors;
    },
  });

  document.getElementById("pay-btn").addEventListener("click", () => {
    form.submit((error, response) => {
      if (error) return handleError(error);
      sendTokenToBackend(response.data.id);
    });
  });
</script>

Note: do not wrap #finix-form in a <form> element whose submit you also handle — the library manages its own submission via postMessage to the iframe.


5. The onSubmit Response

onSubmit: (error, response) => {
  if (error) {
    // error.data?.message — human-readable
    // error.data?._embedded?.errors — per-field validation errors from Finix
    return;
  }
  const tokenData = response.data;
  // tokenData.id          → "TKxxxxxxxxxxxxxxxxxxxxxx" (single-use, 30 days)
  // tokenData.fingerprint → stable hash of the payment method
  // tokenData.instrument  → "PAYMENT_CARD" | "BANK_ACCOUNT"
  // tokenData.expires_at  → ISO timestamp
  const token = tokenData.id; // what you forward to your backend
};

Tokens are single-use and expire in ~30 days. The merchant's server should exchange the token for a persistent Payment Instrument immediately after receiving it (see "Server-Side: Claiming the Token" below).


6. Plaid Bank Linking (optional)

Instead of (or in addition to) manual routing + account entry, merchants can enable Plaid Link for ACH. Add plaidLinkSettings to options and make sure paymentMethods includes 'bank':

Finix.PaymentForm("finix-form", "sandbox", "APxxx...", {
  paymentMethods: ['card', 'bank'],
  plaidLinkSettings: {
    displayName: 'Acme Corp',
    language: 'en',
    countries: ['USA', 'CAN'],
  },
  onSubmit: (err, res) => { /* ... */ },
});

When the user successfully links an account via Plaid, the library posts a third_party_token payload instead of raw bank fields. The returned token is used exactly like a normal token on the server side.


7. Fraud Session (Finix.Auth)

For card and bank payments Finix recommends initializing a Sift fraud session tied to the Merchant ID. This produces a sessionKey (fraud_session_id) that must be forwarded to the backend and attached to the Authorization/Transfer request.

const auth = Finix.Auth("sandbox", "MUxxxxxxxxxxxxxxxxxxxxxx", (sessionKey) => {
  // Stash it; attach to the payload when creating an Authorization/Transfer server-side.
  window.__finixFraudSessionId = sessionKey;
});

Important: Finix.Auth takes the Merchant ID, not the Application ID. The supported environment strings are the same ones in the prerequisites table above (sandbox/sb and prod/production/live).


8. Server-Side: Claiming the Token and Charging the Customer

Everything above this section happens in the browser. Once the merchant's backend receives the token (TKxxxx...), it has 30 days to claim it. The backend does this in three steps:

  1. Create (or reuse) a Buyer IdentityIDxxxx...
  2. Create a Payment Instrument from the token + Identity → PIxxxx... (this claims the token)
  3. Create a Transfer (a sale/debit) — or an Authorization if you want to capture later — using the Payment Instrument as the source and the Merchant ID as merchant.

Transfers are the most common path (one-step sale). Authorizations follow the same pattern but require a separate capture call afterwards.

All requests use HTTP Basic Auth with a server-side API key pair and must include the Finix-Version header.

8a. (Optional) Create a Buyer Identity

Skip this step if you already have an Identity for this buyer — Identities are reusable and can be updated later via PUT.

You can create a fully-populated Identity, or an empty one with just identity_roles and type — both are valid. Populating it up front avoids asking the buyer for the same info later.

curl -i -X POST \
  -u USfdccsr1Z5iVbXDyYt7hjZZ:313636f3-fac2-45a7-bff7-a334b93e7bda \
  https://finix.sandbox-payments-api.com/identities \
  -H 'Content-Type: application/json' \
  -H 'Finix-Version: 2022-02-01' \
  -d '{
    "entity": {
      "phone": "7145677613",
      "first_name": "John",
      "last_name": "Smith",
      "email": "finix_example@finix.com",
      "personal_address": {
        "line1": "741 Douglass St",
        "line2": "Apartment 7",
        "city": "San Mateo",
        "region": "CA",
        "postal_code": "94114",
        "country": "USA"
      }
    },
    "identity_roles": ["BUYER"],
    "tags": { "key": "value" },
    "type": "PERSONAL"
  }'

Response contains "id": "IDxxxx..." — keep it for step 8b.

8b. Create a Payment Instrument (claims the token)

This is the step that claims the token. The token becomes a persistent, reusable Payment Instrument tied to the buyer's Identity. Once claimed, the original TK... is consumed; future charges use the returned PI....

curl -i -X POST \
  -u USfdccsr1Z5iVbXDyYt7hjZZ:313636f3-fac2-45a7-bff7-a334b93e7bda \
  https://finix.sandbox-payments-api.com/payment_instruments \
  -H 'Content-Type: application/json' \
  -H 'Finix-Version: 2022-02-01' \
  -d '{
    "token": "TKiMxe323RE5Dq3wLVtG8kSW",
    "type": "TOKEN",
    "identity": "IDjvxGeXBLKH1V9YnWm1CS4n"
  }'

Example response (card):

{
  "id": "PImmCg3Po7oNi7jaZcXhfkEu",
  "application": "APgPDQrLD52TYvqazjHJJchM",
  "identity": "IDgWxBhfGYLLdkhxx2ddYf9K",
  "instrument_type": "PAYMENT_CARD",
  "brand": "MASTERCARD",
  "card_type": "DEBIT",
  "last_four": "8210",
  "expiration_month": 12,
  "expiration_year": 2029,
  "fingerprint": "FPRiCenDk2SoRng7WjQTr7RJY",
  "enabled": true,
  "currency": "USD",
  "address": { "line1": "900 Metro Center Blv", "city": "San Francisco", "region": "CA", "postal_code": "94404", "country": "USA" }
}

The id (PImmCg3Po7oNi7jaZcXhfkEu) is the source you'll use in step 8c.

8c. Create a Transfer (most common: one-step sale)

This actually moves the money. amount is in the currency's minor units (cents for USD).

curl -i -X POST \
  -u USfdccsr1Z5iVbXDyYt7hjZZ:313636f3-fac2-45a7-bff7-a334b93e7bda \
  https://finix.sandbox-payments-api.com/transfers \
  -H 'Content-Type: application/json' \
  -H 'Finix-Version: 2022-02-01' \
  -d '{
    "amount": 662154,
    "currency": "USD",
    "merchant": "MUmfEGv5bMpSJ9k5TFRUjkmm",
    "source": "PI6iQcTtJNCS8GZAVKYi5Ueb",
    "tags": { "test": "Sale" }
  }'

Fields:

  • merchant — the Merchant ID being paid (the same MU... used in Finix.Auth).
  • source — the Payment Instrument ID from step 8b.
  • amount — integer in minor units. 662154 = $6,621.54.
  • fraud_session_id — (recommended) the sessionKey returned by Finix.Auth in the browser (see "Fraud Session" above).
  • tags — arbitrary metadata for your own reporting.

8d. Authorizations (when you need to capture later)

Identical structure to Transfers, but posted to /authorizations. They place a hold on funds without capturing. Call POST /authorizations/:id/capture (or void) to finish. Use this for workflows like "authorize at checkout, capture on ship." For most use cases — e-commerce sales, one-click pay — a Transfer is what you want.

8e. What lives where

ConcernLocationCredential
Collect card/bankBrowser (iframe)Application ID (public)
Tokenize (TK...)Browser → FinixApplication ID
Create Identity, Payment Instrument, Transfer/AuthorizationServer onlyAPI username + password
Webhooks (dispute, settlement, etc.)ServerVerify signature

Never put the API username/password into finix.js or any browser-delivered bundle. The US...:... credentials shown above are public Finix sandbox test keys — safe to copy for local experiments, but replace with your own for live traffic.


9. Integration Checklist

  • Sandbox Application ID and Merchant ID obtained from Finix Dashboard.
  • finix.js script tag added; container <div> present in the DOM before the script runs.
  • PaymentForm called with correct environment + Application ID.
  • onSubmit (or custom-button form.submit) wired to a backend endpoint.
  • Backend exchanges token → Payment Instrument → Authorization/Transfer using server-side API credentials.
  • Finix.Auth initialized with the Merchant ID; sessionKey forwarded to backend and included as fraud_session_id.
  • Tested in sandbox with Finix test cards (e.g. 4111 1111 1111 1111) and test bank routing numbers before swapping to live environment + live Application ID.
  • Merchant page served over HTTPS in production (Finix enforces this for the iframe).

10. References

Comments

Loading comments...