@noderails/sdk
v0.1.0
Published
NodeRails — Crypto payments SDK for Node.js, Deno, and Bun
Maintainers
Readme
@noderails/sdk
The official Node.js SDK for the NodeRails crypto payments gateway.
Features
- Zero runtime dependencies — uses native
fetch - Dual ESM / CJS — works with
importandrequire() - Full TypeScript support — complete type definitions
- Cross-runtime — Node.js 18+, Deno, Bun
- Webhook verification — HMAC-SHA256 signature checking
- Stripe-style API — familiar resource-based interface
Installation
npm install @noderails/sdk
# or
pnpm add @noderails/sdk
# or
yarn add @noderails/sdkQuick Start
import { NodeRails } from '@noderails/sdk';
const noderails = new NodeRails({
appId: 'your-app-id',
apiKey: 'nr_live_sk_...', // secret key required
});
// Create a checkout session
const session = await noderails.checkoutSessions.create({
appId: noderails.appId,
successUrl: 'https://example.com/success',
cancelUrl: 'https://example.com/cancel',
items: [
{ name: 'Pro Plan', amount: '29.99', quantity: 1 },
],
});
console.log(session.id); // redirect user to your payment UIConfiguration
const noderails = new NodeRails({
appId: 'your-app-id', // Required — your app UUID
apiKey: 'nr_live_sk_...', // Required — secret API key
baseUrl: 'https://...', // Optional — override API base URL
timeout: 30000, // Optional — request timeout in ms (default: 30s)
});API Key Format
Keys follow the format nr_<env>_<type>_<random>:
| Key | Description |
|-----|-------------|
| nr_live_sk_... | Live secret key |
| nr_test_sk_... | Test secret key |
| nr_live_pk_... | Live public key (cannot be used with SDK) |
| nr_test_pk_... | Test public key (cannot be used with SDK) |
Note: The SDK requires a secret key (
sk). Public keys are for client-side use only.
Resources
Checkout Sessions
// Create
const session = await noderails.checkoutSessions.create({
appId: noderails.appId,
successUrl: 'https://example.com/success',
cancelUrl: 'https://example.com/cancel',
items: [{ name: 'Widget', amount: '9.99', quantity: 2 }],
});
// Retrieve
const session = await noderails.checkoutSessions.retrieve('session-id');
// List (paginated)
const { data, pagination } = await noderails.checkoutSessions.list({
page: 1,
pageSize: 20,
status: 'OPEN',
});
// Expire
const expired = await noderails.checkoutSessions.expire('session-id');Payment Intents
// Create
const intent = await noderails.paymentIntents.create({
amount: '100.00',
currency: 'USD',
captureMode: 'AUTOMATIC',
});
// Retrieve
const intent = await noderails.paymentIntents.retrieve('intent-id');
// List
const { data } = await noderails.paymentIntents.list({ status: 'CAPTURED' });
// Cancel
await noderails.paymentIntents.cancel('intent-id');
// Refund
await noderails.paymentIntents.refund('intent-id', { reason: 'Customer request' });Customers
// Create
const customer = await noderails.customers.create({
appId: noderails.appId,
email: '[email protected]',
name: 'John Doe',
});
// Retrieve (includes wallets)
const customer = await noderails.customers.retrieve('customer-id');
// Update
await noderails.customers.update('customer-id', { name: 'Jane Doe' });
// List
const { data } = await noderails.customers.list({ search: 'john' });
// Add wallet
const wallet = await noderails.customers.addWallet('customer-id', {
chainId: 1,
walletAddress: '0x...',
});
// Remove wallet
await noderails.customers.removeWallet('customer-id', 'wallet-id');Invoices
// Create (created as DRAFT)
const invoice = await noderails.invoices.create({
appId: noderails.appId,
customerAccountId: 'customer-id',
items: [{ description: 'Consulting', amount: '500.00' }],
});
// Open (DRAFT → OPEN)
await noderails.invoices.open('invoice-id');
// Send via email
await noderails.invoices.send('invoice-id');
// Void
await noderails.invoices.void('invoice-id');
// List
const { data } = await noderails.invoices.list({ status: 'OPEN' });Payment Links
// Create
const link = await noderails.paymentLinks.create({
appId: noderails.appId,
name: 'Pro Plan',
slug: 'pro-plan',
amount: '29.99',
});
// Update
await noderails.paymentLinks.update('link-id', { isActive: false });
// List
const { data } = await noderails.paymentLinks.list({ isActive: true });
// Delete
await noderails.paymentLinks.delete('link-id');Subscriptions
// Create
const sub = await noderails.subscriptions.create({
appId: noderails.appId,
customerAccountId: 'customer-id',
productPlanId: 'plan-id',
productPlanPriceId: 'price-id',
});
// Retrieve (includes invoices)
const sub = await noderails.subscriptions.retrieve('sub-id');
// Pause / Resume
await noderails.subscriptions.pause('sub-id');
await noderails.subscriptions.resume('sub-id');
// Cancel
await noderails.subscriptions.cancel('sub-id', { cancelAtPeriodEnd: true });
// Create checkout for initial payment
const checkout = await noderails.subscriptions.createCheckout('sub-id');Product Plans & Prices
// Create plan with prices
const plan = await noderails.productPlans.create({
appId: noderails.appId,
name: 'Pro',
planType: 'SUBSCRIPTION',
prices: [
{ amount: '29.99', billingInterval: 'MONTH' },
{ amount: '299.00', billingInterval: 'YEAR' },
],
});
// Add price to existing plan
const price = await noderails.productPlans.createPrice('plan-id', {
amount: '49.99',
billingInterval: 'MONTH',
nickname: 'Premium Monthly',
});
// Update price
await noderails.productPlans.updatePrice('plan-id', 'price-id', {
isDefault: true,
});
// Deactivate price
await noderails.productPlans.deletePrice('plan-id', 'price-id');Tax Rates
// Create
const tax = await noderails.taxRates.create({
displayName: 'VAT',
percentage: 20,
jurisdiction: 'EU',
});
// List (returns all, not paginated)
const rates = await noderails.taxRates.list();
// Include archived
const allRates = await noderails.taxRates.list({ includeInactive: true });
// Archive
await noderails.taxRates.delete('tax-rate-id');Webhook Endpoints
// Create (returns secret — save it!)
const webhook = await noderails.webhookEndpoints.create({
url: 'https://example.com/webhooks',
events: ['payment.captured', 'payment.settled'],
});
console.log(webhook.secret); // only returned on create
// List
const webhooks = await noderails.webhookEndpoints.list();
// Update
await noderails.webhookEndpoints.update('webhook-id', {
events: ['payment.captured', 'payment.settled', 'payment.refunded'],
});
// Rotate secret
const rotated = await noderails.webhookEndpoints.rotateSecret('webhook-id');
// Test ping
await noderails.webhookEndpoints.testPing('webhook-id');
// List deliveries (cursor-based pagination)
const { items, nextCursor } = await noderails.webhookEndpoints.listDeliveries(
'webhook-id',
{ limit: 25 },
);
// Delete
await noderails.webhookEndpoints.delete('webhook-id');Prices (Crypto)
// Get current token price
const eth = await noderails.prices.getPrice('ETH');
console.log(eth.priceUsd);
// Convert USD → token amount
const result = await noderails.prices.convert({
symbol: 'ETH',
amountUsd: 100,
});
console.log(result.tokenAmount);
// Convert token → USD
const result2 = await noderails.prices.convert({
symbol: 'ETH',
tokenAmount: 1.5,
});
console.log(result2.amountUsd);Webhook Verification
Verify incoming webhook payloads using HMAC-SHA256:
import { NodeRails } from '@noderails/sdk';
import express from 'express';
const app = express();
app.post(
'/webhooks',
express.raw({ type: 'application/json' }),
(req, res) => {
try {
const event = NodeRails.webhooks.constructEvent(
req.body, // raw body
req.headers['x-noderails-signature'] as string,
req.headers['x-noderails-timestamp'] as string,
'your-webhook-secret',
);
switch (event.event) {
case 'payment.captured':
// Handle captured payment
break;
case 'payment.settled':
// Handle settled payment
break;
}
res.sendStatus(200);
} catch (err) {
console.error('Webhook verification failed:', err);
res.sendStatus(400);
}
},
);Error Handling
All SDK errors extend NodeRailsError:
import {
NodeRails,
ApiError,
AuthenticationError,
NotFoundError,
ValidationError,
RateLimitError,
} from '@noderails/sdk';
try {
await noderails.paymentIntents.retrieve('non-existent');
} catch (err) {
if (err instanceof NotFoundError) {
console.log('Not found');
} else if (err instanceof ValidationError) {
console.log('Validation error:', err.details);
} else if (err instanceof AuthenticationError) {
console.log('Bad API key');
} else if (err instanceof RateLimitError) {
console.log('Rate limited, retry after:', err.retryAfter);
} else if (err instanceof ApiError) {
console.log(`API error ${err.status}: ${err.message}`);
}
}| Error Class | Status | Description |
|-------------|--------|-------------|
| AuthenticationError | 401 | Invalid or missing API key |
| PermissionError | 403 | Insufficient permissions |
| NotFoundError | 404 | Resource not found |
| ValidationError | 400/422 | Invalid request parameters |
| RateLimitError | 429 | Too many requests |
| ConnectionError | — | Network/connection failure |
| TimeoutError | — | Request timed out |
| SignatureVerificationError | — | Webhook signature mismatch |
Pagination
Paginated endpoints return a PaginatedResult:
const result = await noderails.paymentIntents.list({
page: 1,
pageSize: 50,
});
console.log(result.data); // PaymentIntent[]
console.log(result.pagination.total); // total count
console.log(result.pagination.hasMore); // booleanLicense
MIT
