@easycryptopay/node
v0.2.1
Published
Official Node.js SDK for the EasyCryptoPay crypto payment API
Maintainers
Readme
@easycryptopay/node
Official Node.js SDK for the EasyCryptoPay crypto payment API.
Install
npm install @easycryptopay/nodeRequires Node.js 18+ (for native fetch and crypto).
Quick start
import EasyCryptoPay from "@easycryptopay/node";
const client = new EasyCryptoPay({
apiKey: process.env.ECP_API_KEY!, // ecp_live_… or ecp_test_…
});
// 1. Create a payment. POST a USD amount — the customer picks the currency
// and network on the hosted /pay page.
const payment = await client.createDeposit({
amount: 99.99,
metadata: { order_id: "order_12345" }, // echoed back on every webhook
idempotencyKey: "order_12345", // safe to retry without dupes
});
console.log(payment.paymentLink); // https://easycryptopay.xyz/pay/inv_abc
console.log(payment.expiresAt); // ISO timestamp — 7-day window
// 2. Poll status (or use webhooks — see below). Works with the paymentToken
// or any deposit id created off it on the /pay page.
const status = await client.getDepositStatus(payment.paymentToken);
if (status.status === "completed") {
console.log("Credited:", status.totalCreditedUSD);
}Webhooks
Configure your webhook URL in the Branding page, copy the signing secret, and verify every incoming request:
import express from "express";
import { verifyWebhookSignature } from "@easycryptopay/node";
const app = express();
// IMPORTANT: use `express.raw()` — the HMAC is computed over the raw bytes.
app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
const ok = verifyWebhookSignature({
rawBody: req.body.toString("utf8"),
signatureHeader: req.header("X-ECP-Signature") || "",
secret: process.env.ECP_WEBHOOK_SECRET!,
});
if (!ok) return res.status(401).end("Invalid signature");
const event = JSON.parse(req.body.toString("utf8"));
switch (event.event) {
case "deposit.completed":
// event.data.metadata.order_id, event.data.receivedAmountUsd, etc.
break;
case "invoice.paid":
break;
case "deposit.expired":
break;
}
res.json({ received: true });
});Test mode
Use an ecp_test_* API key and payments are created in a sandbox — no exchange needed, no real money:
const client = new EasyCryptoPay({ apiKey: process.env.ECP_TEST_KEY! });
// Create a sandbox payment.
const payment = await client.createDeposit({ amount: 25 });
// Open `payment.paymentLink` in a browser and pick a currency. That
// materialises a real (but TEST_-flagged) deposit. Then poll /check to
// grab its id and drive it to completion on demand:
const status = await client.getDepositStatus(payment.paymentToken);
// once status moves past "pending", grab the deposit id from your dashboard
// (or via /api/payments) and:
// await client.completeTestDeposit(depositId);Test payments are excluded from your balance and volume stats.
Idempotency
Pass an idempotencyKey on any mutating request. Retrying with the same key within 24 hours returns the original response — no duplicate deposits even if your network dropped halfway through a request.
await client.createDeposit({
amount: 100,
idempotencyKey: crypto.randomUUID(),
});If you reuse the key with a different body, you get a 409 so you notice the collision.
API reference
| Method | Description |
|---|---|
| createDeposit(params) | Create a deposit, returns address + payment link |
| getDepositStatus(idOrToken) | Poll a deposit by id or payment token |
| getPayment(idOrToken) | Fetch the full deposit record by id or payment token |
| completeTestDeposit(idOrToken) | Drive a test deposit to completed (test-mode only) |
| createInvoice(params) | Create an invoice with a shareable payment link |
| getInvoice(idOrToken) | Fetch a single invoice by id or payment token |
| listInvoices(opts?) | List invoices |
| listPayments(opts?) | List deposits |
| listCustomers(opts?) | List customers (filter by search / status) |
| getBalance() | Current balance + volume stats (live only) |
| listCurrencies() | Available currencies and networks |
Errors
Every non-2xx response throws an EasyCryptoPayError with .status, .code (if present), and .body:
try {
await client.createDeposit({ amount: -1 });
} catch (err) {
if (err.status === 400) {
console.error("Validation failed:", err.message);
}
}Security & supply chain
This package has zero runtime dependencies and runs no install scripts — nothing executes when you npm install it. Its only network access is the API client itself: every request goes to the baseUrl you configure (default https://easycryptopay.xyz) and nowhere else. Pass the fetchImpl option to inspect, proxy, or stub the transport. Supply-chain scanners (e.g. Socket) surface a "network access" note because this is an HTTP client — that's expected for a payments SDK, and the request target is always your own configured host.
License
MIT
