NodeRailsCRYPTO PAYMENT INFRASTRUCTURE
DocumentationAPI Reference
Dashboard

Headless Checkout

Charge from your own UI. The secret-key SDK lists tokens, returns the exact approve or sign payload, and captures after the customer acts in their wallet.

⚠️

Secret key on your server

Call headlessCheckout from your backend only. The browser talks to your API, then the customer's wallet. Do not put sk in the frontend.

Flow

  1. 1
    create or createFromSubscription
    Open a session. Do not send the customer to pay.noderails.com.
  2. 2
    paymentOptions
    Show accepted chains and tokens. Each token includes a spot quote for display.
  3. 3
    prepare
    Pass the selected token and wallet. You get cryptoAmount and a userAction.
  4. 4
    confirm
    After they sign or approve, authorize. ERC-20 is captured here.
  5. 5
    submitUserTx
    Only if confirm returns AWAITING_USER_TX (native, Solana, or Sui one-time).

One-time payment

headlessCheckout.create through confirmtypescript
const session = await noderails.headlessCheckout.create({
  successUrl: 'https://yoursite.com/ok',
  cancelUrl: 'https://yoursite.com/cancel',
  items: [{ name: 'Order 123', amount: '49.99', currency: 'USD', quantity: 1 }],
});

const options = await noderails.headlessCheckout.paymentOptions(session.id);
const token = options.acceptedTokens[0];

const prepared = await noderails.headlessCheckout.prepare(session.id, {
  tokenKey: token.tokenKey,
  chainId: token.chainId,
  walletAddress: customerWallet,
});

// Your UI: signTypedData(prepared.userAction.typedData)
// or send prepared.userAction (approve tx)

const result = await noderails.headlessCheckout.confirm(session.id, {
  walletAddress: customerWallet,
  chainId: token.chainId,
  tokenKey: token.tokenKey,
  customerEmail: 'buyer@example.com',
  cryptoAmount: prepared.cryptoAmount,
  exchangeRate: prepared.exchangeRate,
  quoteId: prepared.quoteId ?? undefined,
  permitSignature, // or approvalTxHash
});

if (result.status === 'AWAITING_USER_TX') {
  // Customer sends result.captureData, then:
  await noderails.headlessCheckout.submitUserTx(session.id, {
    intentId: result.intentId,
    txHash,
  });
}

What prepare returns

Always: cryptoAmount (this charge, raw units), exchangeRate, optional quoteId, and userAction. For subscriptions,authAmount is the standing approve or permit (~one year).chargeAmount stays one period.

userAction.typeYour UIThen confirm with
permitsignTypedData(userAction.typedData)permitSignature (amount, deadline, v, r, s)
approveSend to / data on the tokenapprovalTxHash
native_capture / solana_capture / sui_captureConfirm first. Capture calldata is on the confirm result.No extra sig. Then submitUserTx.
sui_wallet_setupFund / set up the Sui subscription walletThen confirm. Capture is pulled after setup.

Subscription first charge

Create the subscription, then open a headless session with createFromSubscription. Native tokens are not listed. Later renewals run on the worker. Listen for subscription.renewed and payment.captured. Do not call confirm every cycle.

headlessCheckout.createFromSubscriptiontypescript
const sub = await noderails.subscriptions.create({
  customerAccountId: customer.id,
  productPlanId: plan.id,
  productPlanPriceId: price.id,
});

const session = await noderails.headlessCheckout.createFromSubscription(sub.id);
const options = await noderails.headlessCheckout.paymentOptions(session.id);
const prepared = await noderails.headlessCheckout.prepare(session.id, {
  tokenKey: 'USDC-8453',
  chainId: 8453,
  walletAddress: customerWallet,
});
// prepared.authAmount is the standing allowance
// prepared.chargeAmount is this period

await noderails.headlessCheckout.confirm(session.id, {
  walletAddress: customerWallet,
  chainId: 8453,
  tokenKey: 'USDC-8453',
  customerEmail: customer.email,
  cryptoAmount: prepared.cryptoAmount,
  exchangeRate: prepared.exchangeRate,
  quoteId: prepared.quoteId ?? undefined,
  permitSignature,
  customerName: 'Ada',
  billingAddress: '1 Market St',
  billingCity: 'San Francisco',
  billingState: 'CA',
  billingCountry: 'US',
  billingPostalCode: '94105',
});
💡

Billing details

Subscription sessions require billing fields on confirm. One-time sessions only need them when requireBillingDetails is true.

After payment

Fulfill on payment.captured. Do not treat the success URL as confirmation.

Methods

MethodDescription
create(params)Create a one-time headless session
createFromSubscription(id)Create a session for the first subscription charge
paymentOptions(id)Tokens, chains, and spot quotes
prepare(id, params)Exact amount and wallet userAction
confirm(id, params)Authorize and capture when we can pull
submitUserTx(id, params)Report the capture hash after AWAITING_USER_TX