NodeRailsCRYPTO PAYMENT INFRASTRUCTURE
DocumentationAPI Reference
Dashboard

Payment Intents

Payment intents are the core payment object. Every checkout session creates a payment intent under the hood when the customer pays. You can also create payment intents directly for more control over the payment flow.

💡

Payment intent lifecycle

A payment intent moves through these states: CREATED → AUTHORIZED → CAPTURED → SETTLED. After capture and before the settlement clock, a refund can go to PARTIALLY_REFUNDED (leftover still settles at the original timelock) or REFUNDED (nothing left). It can also be CANCELLED (before capture). If a dispute is raised, it moves to DISPUTED and then DISPUTE_RESOLVED or DISPUTE_LOST.

Create a payment intent

Create payment intenttypescript
const intent = await noderails.paymentIntents.create({
  amount: '100.00',
  currency: 'USD',
  captureMode: 'AUTOMATIC',
  allowedChains: [1, 137, 42161, 103],   // Ethereum, Polygon, Arbitrum, Solana mainnet
  allowedTokens: ['USDC', 'USDT', 'SOL'],
  externalId: 'order_456',                 // Your internal order ID
  metadata: { plan: 'enterprise' },
});

console.log(intent.id);     // Payment intent ID
console.log(intent.status); // "CREATED"

Retrieve a payment intent

Check a payment intent's status at any time. Useful for polling or verifying the state after receiving a webhook.

Check payment statustypescript
const intent = await noderails.paymentIntents.retrieve('payment-intent-id');

console.log(intent.status);           // Current status
console.log(intent.amount);           // Fiat amount
console.log(intent.cryptoAmount);     // Original captured crypto amount
console.log(intent.settleAmount);     // Leftover still in escrow
console.log(intent.captureTxHash);    // On-chain capture transaction hash
console.log(intent.authorizationChainId);  // Chain the payment was made on
console.log(intent.authorizationTokenKey); // Token used (e.g., "USDC-8453", "SOL-103")
console.log(intent.externalId);       // Your order reference

List payment intents

List and filtertypescript
// List all captured payments
const captured = await noderails.paymentIntents.list({
  status: 'CAPTURED',
  page: 1,
  pageSize: 50,
});

for (const intent of captured.data) {
  console.log(intent.id, intent.amount, intent.cryptoAmount);
}

Cancel a payment intent

Cancel a payment that hasn't been captured yet. This releases the authorized funds back to the customer.

Canceltypescript
const cancelled = await noderails.paymentIntents.cancel('payment-intent-id');
console.log(cancelled.status); // "CANCELLED"

Refund a payment

Refund leftover escrow to the customer's wallet. Allowed on CAPTURED or PARTIALLY_REFUNDED before the settlement timelock. Omit amount and percent to refund all leftover. Partial refunds are EVM only.

Refundtypescript
const refunded = await noderails.paymentIntents.refund('payment-intent-id', {
  reason: 'Customer requested refund',
  percent: 50, // or amount: '5000000' in escrow token units
});
console.log(refunded.status); // PENDING until the chain confirms
console.log(refunded.amount, refunded.leftoverAfter);
⚠️

Refund timing

Refunds are processed on-chain and may take a few minutes to complete depending on the network. A leftover refund sets status to PARTIALLY_REFUNDED andpayment.refunded has fullyRefunded: false. A full leftover refund sets REFUNDED and fullyRefunded: true.

Webhooks

Listen for these events to track payment intent state changes:

EventDescription
payment.authorizedCustomer gave approval to pull money from their wallet
payment.capturedFunds taken from wallet, locked in escrow, and confirmed on-chain
payment.settledFunds released to your merchant wallet
payment.refundedRefund completed on-chain
payment.disputedCustomer raised a dispute

Methods reference

MethodDescription
create(params)Create a new payment intent
retrieve(id)Retrieve a payment intent by ID
list(params?)List payment intents with optional filters
cancel(id)Cancel an authorized payment
refund(id, params)Refund leftover (full, percent, or amount). Partial is EVM only.

TypeScript types

Type importstypescript
import type {
  PaymentIntent,
  PaymentIntentCreateParams,
  PaymentIntentListParams,
  PaymentIntentRefund,
  PaymentIntentRefundParams,
  PaymentWebhookPayload,
} from '@noderails/sdk';

Response body reference

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

create() response

Returns all scalar fields only (no relations):

PaymentIntent (create)

idstringUnique payment intent ID (UUID)
appIdstringYour app ID
customerAccountIdstring | nullLinked customer
externalIdstring | nullYour external reference ID
amountstringFiat amount (Decimal as string, e.g. "100.00")
currencystringCurrency code, e.g. "USD"
allowedChainsstring | number[]"ALL" or array of chain IDs (EVM + Solana e.g. 103)
allowedTokensstring | string[]"ALL" or array of token symbols
captureModestring"AUTOMATIC" or "MANUAL"
timelockDurationnumberEscrow timelock in seconds (default 604800 = 7 days)
disputeStartDurationnumberDispute window in seconds (default 86400 = 1 day)
statusstring"CREATED" at creation
authorizationMethodnullSet when customer authorizes
authorizationChainIdnullChain used for payment
authorizationTokenKeynullToken key used (e.g. "USDC-8453", "SOL-103")
authorizationWalletAddressnullCustomer wallet address
authorizationTxHashnullAuthorization transaction hash
authorizedAtnullTimestamp when authorized
cryptoAmountnullCrypto amount in smallest unit
cryptoTokenKeynullToken key of crypto used
cryptoTokenDecimalsnullToken decimals
exchangeRatenullUSD-to-crypto exchange rate used
captureTxHashnullCapture transaction hash
capturedAtnullTimestamp when captured
captureAttemptsnumberNumber of capture attempts (0)
timelockEndsAtnullWhen escrow timelock expires
settledAtnullTimestamp when settled
refundedAtnullTimestamp when fully refunded
refundTxHashnullRefund transaction hash
refundReasonnullReason for refund
settleAmountnullLeftover escrow token units (integer string)
platformFeeBpsnullPlatform fee in basis points
expiresAtstring | nullISO 8601 expiration timestamp
sourceTypestring | nullWhat created this intent (CHECKOUT_SESSION, INVOICE, etc.)
sourceIdstring | nullID of the source entity
successUrlstring | nullRedirect URL after payment
cancelUrlstring | nullRedirect URL if cancelled
metadataobjectYour metadata key-value pairs
idempotencyKeystring | nullIdempotency key if provided
createdAtstringISO 8601 creation timestamp
updatedAtstringISO 8601 last update timestamp

retrieve() response

Returns all scalar fields from create() above, plus nested relations:

Additional fields on retrieve

transactionsTransaction[]On-chain transactions for this intent
refundsPaymentRefund[]Refund ledger rows for this intent
disputeDispute | nullDispute details if one exists
customerAccountCustomerAccount | nullCustomer who paid

Transaction (nested in transactions[])

idstringTransaction record ID (UUID)
paymentIntentIdstring | nullLinked payment intent
mtxmTxIdstring | nullMTXM service transaction ID
txHashstring | nullOn-chain transaction hash
chainstringChain identifier
typestringAUTHORIZE, CAPTURE, SETTLE, DISPUTE, REFUND, or PAYOUT
statusstringPENDING, CONFIRMED, or FAILED
blockNumbernumber | nullBlock number when confirmed
gasUsedstring | nullGas used for the transaction
errorstring | nullError message if failed
createdAtstringISO 8601 timestamp
confirmedAtstring | nullWhen the transaction was confirmed

Dispute (nested in dispute)

idstringDispute ID (UUID)
paymentIntentIdstringLinked payment intent
reasonstringReason for the dispute
evidencestring | nullEvidence submitted
statusstringOPEN, RESOLVED_MERCHANT, or RESOLVED_PAYER
resolvedBystring | nullWho resolved the dispute
deadlinestringDispute resolution deadline
createdAtstringISO 8601 timestamp
resolvedAtstring | nullWhen it was resolved

list() response

Returns an array of payment intents. Each has all scalar fields plus transactions[]. No dispute or customerAccount in list.

Paginated response shapejson
{
  "success": true,
  "data": [ /* PaymentIntent[] with transactions */ ],
  "pagination": {
    "total": 100,
    "page": 1,
    "pageSize": 20,
    "totalPages": 5
  }
}

cancel() response

Returns all scalar fields only (no relations). The status field will be "CANCELLED".

refund() response

Returns a refund submission object (not a full PaymentIntent). Status is PENDING until the chain confirms.

Key refund fields

paymentIntentIdstringPayment intent ID
transactionIdstringRefund transaction record ID
mtxmTxIdstringMTXM transaction ID
txHashstring | nullOn-chain refund tx hash (when available)
statusstringPENDING until confirmed
amountstringRefunded escrow token units (integer string)
leftoverAfterstringsettleAmount remaining after this refund