Skip to content

rublex-company/node-payments

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@rublex/payments

Node.js SDK for the Rublex Payment Gateway — accept crypto and fiat payments through a single, terminal-scoped API.

Wraps every endpoint of the Rublex Merchant API with a thin client, plus a fluent invoice builder. Zero runtime dependencies (uses the built-in global fetch).

Requirements

  • Node.js 18+

Installation

npm install @rublex/payments
# or
pnpm add @rublex/payments
# or
yarn add @rublex/payments

Build Your Integration With an AI Assistant

Skip reading the rest of this doc. Paste this whole README.md plus the prompt below into Claude / Cursor / Copilot — the assistant interviews you, then wires the SDK into your Node app end to end.

ROLE
You are a senior Node.js engineer. Your task is to integrate the Rublex Payment
Gateway into my Node app using the official `@rublex/payments` SDK (README
provided above) and take me from zero to a production-ready integration.

SOURCE OF TRUTH
The `@rublex/payments` README provided above is your single source of truth.
  - USE the SDK. Do not hand-roll a fetch/axios client or call the REST API directly.
  - Only call methods that exist in the README's endpoint reference table
    (`crypto()`, `fiat()`, `getSupportedCurrencies()`, `getCryptoInvoice()`,
    `getFiatInvoice()`, `listFiatInvoiceGateways()`, `selectFiatGateway()`, …).
  - Method signatures are listed in the README — respect them exactly. Crypto
    takes ONE argument now (`createCryptoInvoice(data)`), no payer-choice flag.
  - Every SDK call resolves with the `{ status, message, data }` envelope —
    handle that centrally (a small wrapper module is fine).
  - Hosted invoice pages live on https://p.rublex.io and are baked into the
    `data.invoice_url` you receive. Redirect customers there as-is.
  - If something I ask for is not in the SDK or the README is silent on it,
    STOP and tell me. Never invent endpoints or response fields.

CRYPTO INVOICE — NON-NEGOTIABLE PRE-FLIGHT
Before you call `client.crypto().…createInvoice()` (or `client.createCryptoInvoice(data)`)
you MUST:
  1. Call `client.getSupportedCurrencies()` first.
  2. Pick a `currency_id` from THAT response (terminal-approved list).
  3. Pass it via `.pick(currencyId)` or as `currency_id` in the data object.
Never hard-code numeric currency IDs. The terminal rejects unapproved IDs with
HTTP 422 — surface a clear error and refetch the list on retry.

RETURN URLS — success_url AND failed_url
Every invoice flow accepts optional `success_url` / `failed_url` (builder
methods `.success(url)`, `.failed(url)`, or `.returnTo(url)` for the same URL).
Pass them from the checkout. The redirect is UX only — NOT proof of payment.
Order state must come from the webhook + a server-side status lookup.

WORK IN TWO PHASES.

──────────────────────────────────────────────
PHASE 1 — INTERVIEW ME (no code yet)
Ask the questions below in ONE grouped message, recommend where you can, then
STOP and wait.
  1. Payment types: crypto, fiat, or both?
  2. Flow: explain the trade-offs and recommend one:
       - Crypto · Pay Request          (`client.crypto().pick(id).createInvoice()`)
       - Fiat · Direct gateway         (`client.fiat().pick(gatewayId).createInvoice()`)
       - Fiat · Gateway selection      (`client.fiat().byPayer().createInvoice()` + payer endpoints)
  3. Runtime & framework: Node version, Express / Fastify / Koa / Hono / Next.js
     (App Router or Pages), NestJS, etc.
  4. ORM / DB layer if any: Prisma, Drizzle, TypeORM, Mongoose, raw SQL …
  5. TypeScript or plain JS?
  6. Where should the terminal token live? (`.env`, AWS Secrets Manager, Vault, …)
  7. Which URL should be my `callback_url`, and which should `success_url` /
     `failed_url` point to? (Same URL for both is fine — recommended.)
  8. Scope: which pieces do I need — checkout endpoint that creates an invoice
     and returns / redirects to `invoice_url`, webhook endpoint, Order/Payment
     model + migration, status reconciliation job (cron / BullMQ / Agenda),
     admin/status view, tests?
  9. Greenfield or fitting into code I'll paste?
 10. Separate staging/production terminals?

──────────────────────────────────────────────
PHASE 2 — BUILD IT (after I confirm)
Deliverables, idiomatic for the stack chosen in Phase 1:
  a. SDK wrapper module — a thin `rublexClient.{js,ts}` that constructs
     `RublexPayments.fromEnv()` once and exports it. Plus a `paymentsService`
     that centralises envelope handling and logging. NO direct HTTP calls.
  b. Invoice creation
     - Crypto: call `getSupportedCurrencies()` (cached briefly with TTL), pick
       the right `currency_id`, create the invoice via the SDK with `amount`,
       `callback`, `success`, `failed`, persist `invoice_number` on the Order.
     - Fiat: same pattern with `pick(gatewayId)` or `byPayer()`.
     - Return / redirect the caller to `data.invoice_url` as-is.
  c. Webhook endpoint
     - Responds `200 OK` within 10s (defer heavy work to a queue).
     - Treats the body as UNTRUSTED: re-fetch via `getCryptoInvoice()` /
       `getFiatInvoice()` before marking paid.
     - Idempotent — guard by Order status before transitioning.
     - Maps PENDING / PARTIAL / PAID / EXPIRED / CANCELLED → order state.
  d. Return-URL endpoint
     - Reads `invoice_number` from the query string, looks up MY order, renders
       status from the local record. Never trust the redirect alone.
  e. Config + errors
     - Use `RublexPayments.fromEnv()` so env vars match the SDK defaults.
     - Centralised error handling: 400, 401, 403, 404, 422, 429, 5xx. Retry
       with exponential backoff on 429 / 5xx (a queued job, not in-process).
     - On 422 from crypto, refetch the supported list and bubble a clear error.
  f. Security
     - Token only in env / secrets manager, never in client code or VCS.
     - Log `invoice_number` next to my internal order id.
     - HTTPS on every URL (callback / success / failed).
  g. Optional but recommended: a small cron job that pulls open invoices and
     reconciles their status — defends against missed webhooks.

OUTPUT FORMAT
  - Full file tree first, then each file in its own code block.
  - Setup steps + env vars + how to run.
  - Finally, ask whether I want automated tests (Vitest / Jest / node:test)
    or any adjustments.

Begin with PHASE 1 now.

Tip: If you use TypeScript, also tell the assistant to import the SDK's index.d.ts types so your service layer is fully typed.

Configuration

The SDK needs your terminal token (a 60-char secret from Stores → Terminals in the merchant panel). Pass it explicitly or via environment variables.

RUBLEX_PAYMENTS_API_KEY=<your-60-char-terminal-token>
RUBLEX_PAYMENTS_CALLBACK_URL=https://your-site.com/rublex/callback
# Override only if Rublex tells you to:
# RUBLEX_PAYMENTS_URL=https://api.pay.rublex.io/terminals/v1/
const { RublexPayments } = require('@rublex/payments');

// Explicit
const rublex = new RublexPayments({
  apiKey: process.env.RUBLEX_PAYMENTS_API_KEY,
  callbackUrl: 'https://your-site.com/rublex/callback',
});

// Or pick up everything from the environment
const rublex = RublexPayments.fromEnv();

Treat the terminal token like a password. Keep it server-side only — never ship it to a browser or mobile app.

Invoice creation

Crypto pre-flight is mandatory. Before creating a crypto invoice you MUST call getSupportedCurrencies() and use one of the returned id values as currency_id. The terminal rejects IDs it has not approved with HTTP 422. Do not hard-code IDs.

Two equivalent styles — pick whichever fits your code.

Fluent builder

// 1) Look up which currencies this terminal supports.
const supported = await rublex.getSupportedCurrencies();
const currencyId = supported.data[0].id;

// 2) Crypto · merchant-fixed coin
const invoice = await rublex.crypto()
  .amount(0.5)
  .pick(currencyId)                                       // from /currencies/supported
  .callback('https://your-site.com/rublex/callback')
  .returnTo('https://your-site.com/checkout/return')      // success + failure
  .createInvoice();

// Fiat · direct gateway
const fiat = await rublex.fiat()
  .amount(19.99)
  .pick(4)                                                // gateway_id from /fiat/gateways
  .lockRate()                                             // fixed FX rate
  .success('https://your-site.com/checkout/success')
  .failed('https://your-site.com/checkout/cancelled')
  .customer({ email: 'buyer@example.com', firstName: 'Ada' })
  .createInvoice();

// Fiat · gateway selection (payer picks the gateway on the hosted page)
const fiatPick = await rublex.fiat()
  .amount(19.99)
  .byPayer()
  .lockRate(false)
  .returnTo('https://your-site.com/checkout/return')
  .createInvoice();

Direct methods

const { data: supported } = await rublex.getSupportedCurrencies();

await rublex.createCryptoInvoice({
  amount: 0.5,
  currency_id: supported[0].id,
  success_url: 'https://your-site.com/checkout/return',
  failed_url:  'https://your-site.com/checkout/return',
});

await rublex.createFiatInvoice({
  amount: 19.99,
  gateway_id: 4,
  success_url: 'https://your-site.com/checkout/return',
  failed_url:  'https://your-site.com/checkout/return',
});

await rublex.createFiatInvoice({
  amount: 19.99,
  success_url: 'https://your-site.com/checkout/return',
  failed_url:  'https://your-site.com/checkout/return',
}, true);                                                 // gateway selection

Redirect the customer to response.data.invoice_url to complete payment.

success_url / failed_url are UX, not proof of payment. Always reconcile against the webhook or getCryptoInvoice() / getFiatInvoice().

Endpoint reference

Group Method Endpoint
Terminal getInformation() GET /info
Catalog getCurrencies(page?, perPage?) GET /currencies
Catalog getSupportedCurrencies(page?, perPage?) GET /currencies/supported
Catalog getFiatGateways() GET /fiat/gateways
Catalog getFiatCurrencies() GET /fiat/currencies
Crypto createCryptoInvoice(data) POST /pay-request
Crypto getCryptoInvoice(invoiceNumber) GET /invoices
Crypto listCryptoInvoices(params?) GET /invoices
Crypto listPayRequests(params?) GET /pay-requests
Fiat createFiatInvoice(data, payerChoice?) POST /fiat/pay-request-direct or /fiat/pay-request-selection
Fiat getFiatInvoice(invoiceNumber) GET /fiat/invoices
Fiat listFiatInvoices(params?) GET /fiat/invoices
Payer listFiatInvoiceGateways(invoiceNumber) GET /fiat/invoices/{n}/gateways
Payer selectFiatGateway(invoiceNumber, data) POST /fiat/invoices/{n}/select-gateway

Every method resolves with the gateway's shared envelope:

{ status: 'SUCCESS' | 'ERROR', message: string, data: { /* payload */ } }

Webhooks

Set a callback_url (per-invoice or globally via callbackUrl). On status change Rublex POSTs JSON to it:

{ "invoice_number": "BpXo8T60vIN9D7NCcs66rOnZVipBLUah", "status": "PAID", "amount": "0.50000000", "paid_amount": "0.50000000", "currency": "USDT (TRC20)" }

Required behaviour:

  1. Respond 200 OK within 10 seconds.
  2. Treat the callback as untrusted — re-fetch the invoice via getCryptoInvoice / getFiatInvoice before marking the order paid.
  3. Be idempotent — the same callback may be retried.

Errors

  • RublexConfigError — missing API key / bad configuration.
  • RublexRequestError — network failure or non-JSON response (has .status, .body).

HTTP-level errors (4xx/5xx) are not thrown — the parsed envelope is returned so you can inspect status === 'ERROR' and the message.

License

MIT © Rublex Team. See LICENSE.md.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors