NodeRailsCRYPTO PAYMENT INFRASTRUCTURE
DocumentationAPI Reference
Dashboard

Checkout Sessions

Checkout sessions are the easiest way to accept a payment. Create a session on your server, redirect the customer to the hosted payment page, and wait for the webhook.

Step 1: Create a checkout session

Create sessiontypescript
const session = await noderails.checkoutSessions.create({
  successUrl: 'https://yoursite.com/success?session={CHECKOUT_SESSION_ID}',
  cancelUrl: 'https://yoursite.com/cancel',
  items: [
    {
      name: 'Pro Plan',
      amount: '49.99',
      currency: 'USD',
      quantity: 1,
    },
  ],
  metadata: { orderId: 'order_123' },
});

// Redirect customer to the hosted checkout page
const checkoutUrl = `https://pay.noderails.com/checkout/${session.id}`;
💡

Session URL

The checkout URL follows the pattern https://pay.noderails.com/checkout/SESSION_ID. Redirect your customer here after creating the session.

Step 2: Customer completes payment

On the hosted checkout page, the customer selects their preferred chain and token, connects their wallet, and approves the transaction. You don't need to build any of this. NodeRails handles the entire payment UI.

Once the customer pays, they're redirected to your successUrl. But don't rely on the redirect to confirm the payment. Always use webhooks.

Step 3: Listen for the webhook

NodeRails sends a payment.captured event to your webhook endpoint when funds have been taken from the customer's wallet, locked in escrow, and confirmed on-chain. This is your signal to fulfill the order.

Handle webhooktypescript
app.post('/webhooks/noderails', express.raw({ type: 'application/json' }), (req, res) => {
  const event = NodeRails.webhooks.constructEvent(
    req.body,
    req.headers['x-noderails-signature'] as string,
    req.headers['x-noderails-timestamp'] as string,
    process.env.WEBHOOK_SECRET!,
  );

  switch (event.event) {
    case 'payment.captured':
      // Payment confirmed! Fulfill the order
      const paymentIntentId = event.data.id;
      const amount = event.data.amount;
      const orderId = event.data.metadata?.orderId;
      console.log(`Payment ${paymentIntentId} captured for order ${orderId}`);
      break;

    case 'payment.settled':
      // Funds released to your merchant wallet
      console.log('Funds settled:', event.data.id);
      break;
  }

  res.sendStatus(200);
});

Step 4: Check payment status

After creating a checkout session, you can check its status at any time. When the customer completes payment, the full payment intent is included in the response, no extra API call needed.

Check session and payment statustypescript
const session = await noderails.checkoutSessions.retrieve('session-id');
console.log(session.status);          // "OPEN" | "COMPLETE" | "EXPIRED"
console.log(session.paymentIntentId); // null until customer pays

// Once payment is made, the full payment intent is included
if (session.paymentIntent) {
  console.log(session.paymentIntent.status);              // "CAPTURED"
  console.log(session.paymentIntent.amount);              // "49.99"
  console.log(session.paymentIntent.cryptoAmount);        // "50000000" (in token's smallest unit)
  console.log(session.paymentIntent.captureTxHash);       // "0x..." (on-chain tx hash)
  console.log(session.paymentIntent.authorizationChainId); // 8453 (chain used)
  console.log(session.paymentIntent.authorizationTokenKey); // "USDC-8453" (token used)
  console.log(session.paymentIntent.exchangeRate);        // "1.0001"
}
💡

No extra calls needed

When you retrieve a checkout session, the full paymentIntent object is automatically included once the customer has paid. You don't need to make a separate call to paymentIntents.retrieve().

List checkout sessions

List and filtertypescript
const result = await noderails.checkoutSessions.list({
  status: 'OPEN',
  page: 1,
  pageSize: 25,
});

console.log(result.data);              // CheckoutSession[]
console.log(result.pagination.total);  // Total matching sessions

Expire a session

Manually expire an open checkout session. This prevents the customer from completing the payment after the session is expired.

Expire sessiontypescript
const expired = await noderails.checkoutSessions.expire('session-id');
console.log(expired.status); // "EXPIRED"

Methods reference

MethodDescription
create(params)Create a new checkout session
retrieve(id)Retrieve a session by ID
list(params?)List sessions with optional filters
expire(id)Expire an open session

TypeScript types

Type importstypescript
import type {
  CheckoutSession,
  CheckoutSessionCreateParams,
  CheckoutSessionListParams,
} from '@noderails/sdk';

Response body reference

All responses are wrapped in { success: true, data: ... }. The fields below describe what's inside data.

create() response

CheckoutSession (create)

idstringUnique session ID (UUID)
appIdstringYour app ID
customerAccountIdstring | nullLinked customer, if provided
paymentIntentIdnullAlways null at creation
modestring"PAYMENT" or "SUBSCRIPTION"
statusstring"OPEN" at creation
sourceTypestring"API" when created via SDK
sourceIdnullNot set at creation
amountstring | nullTotal amount (null until computed)
currencystringCurrency code, default "USD"
subtotalstring | nullPre-tax total
taxAmountstring | nullTax portion
taxDescriptionstring | nullTax label, e.g. "VAT 20%"
allowedChainsstring | number[]"ALL" or array of chain IDs
allowedTokensstring | string[]"ALL" or array of token keys
successUrlstringRedirect URL after payment
cancelUrlstringRedirect URL if cancelled
requireBillingDetailsbooleanWhether billing details are required
metadataobjectYour metadata key-value pairs
expiresAtstringISO 8601 expiration timestamp
completedAtnullSet when session completes
createdAtstringISO 8601 creation timestamp
updatedAtstringISO 8601 last update timestamp
itemsCheckoutSessionItem[]Line items (see below)

CheckoutSessionItem (nested in items[])

idstringItem ID (UUID)
checkoutSessionIdstringParent session ID
productPlanIdstring | nullLinked product plan, if any
productPlanPriceIdstring | nullLinked price, if any
namestringItem name
descriptionstring | nullItem description
amountstring | nullItem amount (Decimal as string)
currencystringItem currency
quantitynumberItem quantity
isPriceOptionbooleanWhether this is a price selection
createdAtstringISO 8601 timestamp

retrieve() response

Returns all fields from create() above, plus these additional nested objects:

Additional fields on retrieve

appAppFull app object (id, name, environment, etc.)
paymentIntentPaymentIntent | nullFull payment intent once customer pays (all PI fields)
items[].productPlanProductPlan | nullFull product plan on each item, if linked
items[].productPlanPriceProductPlanPrice | nullFull price on each item, if linked

list() response

Returns an array of sessions. Each session has the same shape as create()(with items but without app, paymentIntent, or nested plan/price on items). The response includes pagination:

Paginated response shapejson
{
  "success": true,
  "data": [ /* CheckoutSession[] */ ],
  "pagination": {
    "total": 42,
    "page": 1,
    "pageSize": 20,
    "totalPages": 3
  }
}

expire() response

Same shape as create() (session + items). The status field will be "EXPIRED".