Skip to main content

Drop-in SDK: TensorRail.js

Cards are available on card rails

Card acceptance switches on when a card acquirer is enabled for your account — ask your account manager; no rail enabled today takes cards. Build on this page once they confirm one.

The card shape does not change when it does. The only card shape the API accepts is an opaque token minted by the processor's own PCI-compliant secure fields, and a raw card number is refused in every mode, sandbox included, with ERR_2029 — by design, no card number reaches TensorRail.

Meanwhile the rails you already have take payments through Payment links, Hosted checkout or the Direct API. Your dashboard shows the methods enabled on your account.

Where the bundle is served from

The Drop-in SDK bundle is served from https://js.tensorrail.com/v1/TensorRail.js and talks to the TensorRail API at api.tensorrail.com. See the SDK Overview for details. If you'd rather not write frontend payment code at all, the Hosted Checkout is the fastest path.

TensorRail.js renders a secure, pre-styled payment form on your checkout page. Customers enter card data directly into TensorRail's hosted secure fields, so it never touches your servers.

All API calls use the TensorRail API at api.tensorrail.com with the api-key: header. See Authentication. To look up payment history, see the Reporting API.

When to use the Drop-in SDK

  • You want a pre-built, styled payment form embedded in your page.
  • You want to keep card data off your servers.
  • You want to support multiple payment methods with one integration.

How it works

Your backend creates the payment with the secret key; the browser mounts the element with the scoped client_secret; the customer pays inside the iframe; confirmPayment finalizes; the webhook delivers the authoritative result.

End-to-end: the client_secret flow

  1. Backend calls POST /payments with your secret key (rail_<type>_test_… / rail_<type>_live_…), an integer amount in minor units (e.g. 5000 = $50.00 USD), and currency. Pass return_url for redirect flows and an Idempotency-Key (a UUID) on every create.
  2. The API returns a payment_id (e.g. pay_…), a status, and a client_secret scoped to that one payment.
  3. Frontend loads TensorRail.js with your publishable key, then passes only the client_secret into elements({ clientSecret }), never the secret key.
  4. The customer pays in the iframe. confirmPayment finalizes and redirects to return_url. Always verify the result server-side (the Reporting API or a webhook).
  5. The webhook delivers the final status to your backend; verify the TensorRail-Signature signature (HMAC-SHA512) before fulfilling the order.

Step-by-step

1. Create a payment (backend)

Call POST /payments with your secret key. This returns the client_secret your frontend needs.

ParamRequiredNotes
amountYesInteger, minor units (cents). 5000 = $50.00. Never send decimals.
currencyYesISO 4217 code, e.g. USD.
return_urlRecommendedWhere the customer lands after confirmPayment.
Idempotency-Key (header)RecommendedA UUID; makes retries safe. See Idempotency.
curl -X POST https://api.tensorrail.com/payments \
-H "api-key: rail_full_test_xxx" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
-d '{
"amount": 5000,
"currency": "USD",
"return_url": "https://yoursite.com/payment/complete"
}'

Responses also return amount in minor units.

Response (includes client_secret):

{
"payment_id": "pay_N5cPeGw6uS2QIMjnsjVF",
"client_secret": "<client_secret>",
"status": "requires_payment_method"
}

Pass client_secret to your frontend. Never expose your secret API key to the frontend.

2. Include TensorRail.js (frontend)

Load the SDK from the TensorRail CDN:

<script src="https://js.tensorrail.com/v1/TensorRail.js"></script>

There is one bundle and one host. For test mode, pass a rail_open_test_ publishable key: the key selects the mode, and the same js.tensorrail.com/v1/TensorRail.js serves both test and live. The runtime's own assets and the payment iframe are fetched from js.tensorrail.com, so your Content-Security-Policy must allow that host.

Using a bundler (React, Vue, Vite, webpack)? Install the npm loader instead of a script tag: npm install @tensorrail/sdk, then const tensorrail = await loadTensorRail("rail_open_test_…"). It injects the same CDN runtime and returns the initialized instance. See the SDK Overview.

3. Initialize and mount the payment form

Your publishable key lives in the Dashboard under Developers → API Keys, prefixed rail_open_<mode>_ (e.g. rail_open_test_…). Use a test-mode publishable key (rail_open_test_…) for testing and a live key (rail_open_live_…) for production traffic.

<div id="payment-form"></div>
<!-- UI label can show dollars; the payment was created with amount: 5000 (minor units) -->
<button id="pay-button">Pay $50.00</button>

<script>
// Backend API base URL for the SDK to call:
// - Sandbox development: https://api.tensorrail.com (with rail_full_test_ keys)
// - Production: https://api.tensorrail.com (with rail_full_live_ keys)
const tensorrail = window.TensorRail("your_publishable_key", {
customBackendUrl: "https://api.tensorrail.com",
});
const elements = tensorrail.elements({ clientSecret: clientSecret });
const paymentElement = elements.create("payment");
paymentElement.mount("#payment-form");

// Handle submission
document.getElementById("pay-button").addEventListener("click", async () => {
const { error, paymentIntent } = await tensorrail.confirmPayment({
elements,
confirmParams: {
return_url: "https://yoursite.com/payment/complete",
},
});

if (error) {
console.error(error.message);
}
// Customer is redirected to return_url on success
});
</script>

Set customBackendUrl to https://api.tensorrail.com while testing (rail_<type>_test_… keys). For live traffic, use https://api.tensorrail.com and your live publishable key.

4. Handle the return URL

After payment, the customer lands on your return_url with query parameters:

https://yoursite.com/payment/complete?payment_id=pay_N5cPeGw6uS2QIMjnsjVF&status=succeeded

Never trust the redirect alone. Verify status server-side before fulfilling: process the webhook or look up payment_id via the Reporting API.

Wallets (Apple Pay / Google Pay)

Digital-wallet payment sheets are rendered in the browser. This is a publishable-key (client-side) flow, not a server-to-server call. TensorRail.js requests the wallet session tokens for you when you mount the payment element, so in most integrations you never call the endpoint directly.

When you need to drive a custom wallet button yourself, request the session tokens from the browser with the payment's client_secret and your publishable key (rail_open_*):

POST /payments/session_tokens (client-side, publishable key)

FieldRequiredDescription
payment_idYesThe payment to create wallet session tokens for.
client_secretYes (browser)The payment's client_secret, when called from the browser with a publishable key.
walletsNoRestrict to specific wallets (e.g. ["apple_pay"]). Defaults to all enabled wallets.
// In the browser, with the payment's client_secret and your publishable key.
const res = await fetch("https://api.tensorrail.com/payments/session_tokens", {
method: "POST",
headers: {
"api-key": "rail_open_test_xxx",
"Content-Type": "application/json",
},
body: JSON.stringify({
payment_id: "pay_N5cPeGw6uS2QIMjnsjVF",
client_secret: clientSecret,
wallets: ["apple_pay", "google_pay"],
}),
});
const { session_token } = await res.json();
// Feed session_token into the Apple Pay / Google Pay sheet.

The response carries a per-wallet session_token array your wallet button uses to render its sheet. Because this runs client-side with a publishable key, it never handles a secret key; keep secret keys on your backend.

Errors

confirmPayment returns { error } on a problem; your backend create call returns a { "error": { "code": "ERR_NNNN" } } body. Common cases:

CodeMeaningAction
ERR_2001 / ERR_2002Missing or malformed field on createFix the request payload before retrying.
ERR_2004Unsupported currency — raised on settlement, not on payment createSettle in a currency your account holds a balance in.
ERR_3018Duplicate resource: a payment_id was reused for a different paymentUse a fresh payment_id per distinct payment.
ERR_4094Payment declinedShow error.message; do not fulfill the order.
ERR_5099Internal errorRetry with backoff; reconcile via webhook.

See Error catalog for the full ERR_* reference.

Edge cases

SituationWhat happensWhat you should do
Card declinedconfirmPayment may return { error } or the payment moves to failedShow the message from error.message or unified error fields; do not fulfill the order.
3DS / authentication (requires_customer_action)Status indicates customer action; redirect may be requiredFollow next_action / redirect flow; after return, confirm status via webhook or the Reporting API.
Network or SDK errorfetch or SDK fails before a definitive statusRetry idempotently only when safe; show a generic error; reconcile later via webhooks.
Webhook arrives before redirectFinal status may be known server-side firstAlways treat the webhook (verified with TensorRail-Signature, HMAC-SHA512) as the source of truth for fulfillment.

Never rely on client-side success alone. Note: a webhook payload's top-level keys are merchant_id (a string), event_id, event_type, content, and timestamp; the payment object is nested at content.object. See Webhooks for the full shape.

Customization

Theme TensorRail.js to match your brand:

const elements = tensorrail.elements({
clientSecret: clientSecret,
appearance: {
theme: "flat",
variables: {
colorPrimary: "#4F7CFF",
fontFamily: "Inter, sans-serif",
borderRadius: "8px",
},
},
});

Supported payment methods

The Drop-in SDK renders the methods enabled for your account automatically, matched to the customer's market and the payment's currency, so you do not hard-code a method list and do not re-integrate to add one. The SDK asks the platform which methods apply to this payment at render time, so you never enumerate them yourself. The set enabled on your account is visible in your dashboard, and your account manager can extend it — see Corridors and coverage. (The unauthenticated GET /v1/capabilities catalogue lists what the platform supports overall; it is not per-account.)

Security

  • Card data goes straight into TensorRail's hosted secure fields; it never reaches your servers.
  • The client_secret is safe on the frontend: it can only confirm the specific payment it was created for.
  • Always verify status server-side (API or webhook) before fulfilling orders.

Complete example

Save as checkout.html and serve over HTTPS (or a local static server). Replace your_publishable_key with your Dashboard publishable key (rail_open_test_… in test mode) and client_secret with the value your backend got from POST /payments. Your server must call TensorRail with the secret key; never put it in HTML.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>TensorRail Checkout</title>
<script src="https://js.tensorrail.com/v1/TensorRail.js"></script>
</head>
<body>
<div id="payment-form"></div>
<button id="pay-btn" type="button">Pay $50.00</button>
<script>
// 1) Backend: POST https://api.tensorrail.com/payments
// with header "api-key: rail_..." + Idempotency-Key → { client_secret, payment_id, ... }
const clientSecret = "REPLACE_WITH_client_secret_FROM_YOUR_BACKEND";

const tensorrail = window.TensorRail("your_publishable_key", {
customBackendUrl: "https://api.tensorrail.com",
});
const elements = tensorrail.elements({ clientSecret: clientSecret });
const paymentElement = elements.create("payment");
paymentElement.mount("#payment-form");

document.getElementById("pay-btn").addEventListener("click", async () => {
const { error } = await tensorrail.confirmPayment({
elements,
confirmParams: { return_url: "https://yoursite.com/success" },
});
if (error) console.error("Payment failed:", error.message);
});
</script>
</body>
</html>