Before you start
You need:- Cards enabled on your project. Ask your account manager to enable them. Until they’re enabled, card registration, quotes, and transactions fail with
422 CardNotEnabledForProject. - An individual customer with structured names. Cards are registered under the customer’s
firstNameandlastName. A customer that is not an individual is rejected with422 AccountTypeRequirementsNotMet. - A secret API key and secret (
sk_sdbx_…orsk_live_…) for your server. See Get started. - A publishable key (
pk_sdbx_…orpk_live_…) for the browser. See Get a publishable key. - Access to the browser SDK. The SDK packages are in early access and are not available from the public npm registry. Contact us to request access.
Get a publishable key
A publishable key identifies your project in the browser. Unlike your secret key, it is safe to ship in page code.- In the OMS Dashboard, open API Keys.
- Create a Publishable key for the environment you’re building in, sandbox or production.
- Add the origin of each page that shows the card form.
A key only works in the environment it was created for, so a sandbox publishable key pairs with a sandbox secret key.
Register a card
The SDK renders the secure card fields, collects the fraud-screening signals, and submits everything in one step.1. Install the packages
@polygonlabs/oms-sdk exports framework-neutral custom elements from its root and React bindings from @polygonlabs/oms-sdk/react, which requires React 18 or newer. @polygonlabs/oms-risk-sdk is the companion fraud-signal collector.
2. Add a customer token route to your server
The SDK calls this route at least twice on every submit: once to start the fraud-screening session and once to authorize the card write. The route receives{ customerId } from the browser. It must:
- Authenticate your own signed-in user, with your usual session and CSRF protections.
- Refuse the request if
customerIdisn’t the OMS customer ID stored on that user’s record. An ID sent by the browser is not proof that the user owns it. - Create a token for that customer with your project access token (see Get started) and return the
{ token, expiresAt }response unchanged.
Idempotency-Key.
3. Create the OMS client and render the form
Create oneOMS object per page and wrap your app in OMSProvider:
DebitCardForm includes the secure card fields, billing address inputs, inline validation, a status message, and submit and clear buttons. Fraud-signal collection starts when OMSProvider mounts. One page serves one customer; signing in as someone else needs a page reload.
To build your own layout, render DebitCard with CardFields, BillingAddressFields, and CardFeedback, and call submit() from the useDebitCard() hook. Without React, call defineDebitCardElements(), mount an <oms-debit-card> element containing <oms-debit-card-field> elements of type number, expiry, and cvc, and pass your OMS object to its configure() method.
4. Handle the result
onSuccess fires when OMS accepts the card and returns its ext_ ID. Accepted means stored, not yet usable: wait until the card is active before quoting.
The form shows field errors and recovery messages itself. To react in your own UI, pass onStateChange and read the next step with getDebitCardRecovery(state):
Don’t retry automatically. Each customer token carries its own idempotency key, so a retry with a fresh token is a new request and can save a second copy of the card. For
check_outcome, list the customer’s external accounts from your server, keep the entries whose type is card, and match the last four digits and expiry:
GET /external-accounts has no type filter, so filter the results yourself. If the card is there, use it. If not, let the customer try again.
Content Security Policy
If your site sets a Content Security Policy, allow the OMS API and secure card hosts for your environment, the card vault and fraud-screening hosts the SDK loads, and your own customer token route. The SDK package’s README lists the exactscript-src, frame-src, and connect-src sources. The fraud-screening collector loads remotely, so check its requests in your deployment before you enforce the policy.
Wait for the card to activate
A new card startspending. Wait for active before quoting: subscribe to externalAccount.statusChanged or poll GET /external-accounts/{id}. If it lands in failed or rejected, read failure.code and see Statuses.
Use a card on a quote
Once the card isactive, reference it as the quote source for a buy or the destination for a sell. The card side always uses asset: usd, and OMS infers the network.
amount on one side only. OMS calculates the other.
Execute the transaction
Card transactions are screened for fraud with signals from the customer’s browser. In the browser,oms.confirmCardTransaction({ quoteId, submit }) collects a fresh risk session and calls your submit callback with { quoteId, riskSession }. Send both to your server over HTTPS, then create the transaction with POST /transactions, passing the two riskSession values as top-level fields:
webSessionId returns 422 RiskSessionRequired. The quote already identifies the card, so the transaction doesn’t take a card ID.
Return one of these from submit:
{ status: 'submitted', transactionId }when OMS created the transaction. Submitted is not settled.{ status: 'rejected' }when you know nothing was submitted.{ status: 'unknown' }when the outcome is uncertain. Reconcile on your server before trying again.
Test in sandbox and on localhost
Build and test against sandbox. Moving to production is a key swap, not a code change.
Test card:
4111 1111 1111 1111, any future expiry, and any three-digit CVC.
To run the card form on your own machine:
- Add your dev server’s origin, such as
http://localhost:5173, to your sandbox publishable key. - Serve the page from that origin. Opening the HTML as a file (
file://) won’t work. - Run your customer token route locally with your sandbox secret key. Keep the secret in an environment file, not in page code.
- Open the page, type the test card into the secure fields, and submit.
- Poll
GET /external-accounts/{id}until the card isactive.
- The secure host only accepts submissions from the SDK’s secure fields. A
curlor server-side request to it is rejected. - The secure fields are iframes from another origin. Your page script can’t read or set their values, and some browser automation tools can’t type into them. End-to-end tests need a tool that can target cross-origin frames.
- Set
autoComplete="off"on test forms so the browser doesn’t fill in a saved real card. - For component tests, import
createTestRiskClient()andcreateVaultTestSession()from@polygonlabs/oms-sdk/testing. Pass the risk client as theriskoption ofcreateOMSand the session as the form’ssessionprop. Both run in memory with no network calls.
Idempotency
Server-sidePOST requests, including quotes and transactions, need an Idempotency-Key header. PATCH and token creation don’t. The SDK sets the key for card registration from the customer token, so you never set one in the browser.
- Same key and body, after the first request finished: OMS returns the original response.
- Same key while the first request is still running:
409with codeidempotencyKeyInProgress. Wait, then retry with the same key. - Same key, different body:
422with codeidempotencyKeyReuse. Use a new key for a corrected request. - No key:
400with codemissingIdempotencyKey. - After a timeout: retry with the same key until you get a definite answer.
error is a message and code is the string shown:
Related
- Debit cards: the card object, statuses, billing address rules, errors, and events
- External accounts: the shared model for cards, banks, and external wallets
- Transactions: the quote-and-execute flow
- Sandbox and testing: how the sandbox environment behaves
-
Receiving webhooks: verify and apply
externalAccount.statusChangedevents