Node.js Examples
Illustrative Node.js examples for integrating with the LuxCore API usingaxios.
Installation
npm install axios
LuxCore Client Class
Save this API v1 example inluxcore.js for the imports below.
const axios = require("axios");
const crypto = require("crypto");
class LuxCoreClient {
static BASE_URL = "https://api.lux-core.io/api/v1";
constructor(apiKey = process.env.LUXCORE_API_KEY) {
if (!apiKey) {
throw new Error("API key is required");
}
this.client = axios.create({
baseURL: LuxCoreClient.BASE_URL,
timeout: 30000,
maxRedirects: 0,
headers: {
"X-API-Key": apiKey,
"Content-Type": "application/json",
},
});
// Add response interceptor for error handling
this.client.interceptors.response.use(
(response) => response.data,
(error) => {
const err = new LuxCoreError(
error.response?.data?.message || error.message,
error.response?.status,
error.response?.data?.error_code ?? error.response?.data?.error,
);
throw err;
},
);
}
// Payments
async createPayment(data) {
return this.client.post("/payments", data);
}
async getPayment(paymentId) {
return this.client.get(`/payments/${paymentId}`);
}
async listPayments(filters = {}) {
return this.client.get("/payments", { params: filters });
}
async cancelPayment(paymentId) {
return this.client.post(`/payments/${paymentId}/cancel`);
}
// Balance
async getBalance(currency) {
const params = currency ? { currency } : {};
params.balance_type = "main";
return this.client.get("/balance", { params });
}
async getAllBalances() {
return this.client.get("/balance/all");
}
// Webhooks
async createWebhook(data) {
return this.client.post("/webhooks", data);
}
async listWebhooks() {
return this.client.get("/webhooks");
}
async deleteWebhook(webhookId) {
return this.client.delete(`/webhooks/${webhookId}`);
}
// Static method for webhook verification
// IMPORTANT: Pass the raw request body string, not a parsed/re-serialized object
static verifyWebhookSignature(rawBody, signature, timestamp, secret) {
// Check timestamp (reject if older than 5 minutes)
const currentTime = Math.floor(Date.now() / 1000);
if (Math.abs(currentTime - timestamp) > 300) {
throw new Error("Webhook timestamp too old");
}
// Calculate expected signature using raw body
const payloadToSign = `${timestamp}.${rawBody}`;
const expectedSignature =
"hmac_sha256=" +
crypto.createHmac("sha256", secret).update(payloadToSign).digest("hex");
// Compare signatures (timing-safe)
if (
!crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature),
)
) {
throw new Error("Invalid webhook signature");
}
return true;
}
}
class LuxCoreError extends Error {
constructor(message, statusCode, errorCode) {
super(message);
this.name = "LuxCoreError";
this.statusCode = statusCode;
this.errorCode = errorCode;
}
}
module.exports = { LuxCoreClient, LuxCoreError };
Usage Examples
Create a Deposit Payment
const { LuxCoreClient, LuxCoreError } = require("./luxcore");
const client = new LuxCoreClient();
async function createDeposit() {
try {
const payment = await client.createPayment({
amount: 100000, // $1,000.00 MXN (in centavos)
currency: "MXN",
method: "spei",
type: "deposit",
merchant_reference: `order_${Date.now()}`,
customer: {
name: "Juan Perez",
email: "juan@example.com",
phone: "+525551234567",
},
metadata: {
order_id: "12345",
source: "web",
},
});
console.log("Payment created:", payment.transaction_id);
console.log("Status:", payment.status);
if (payment.bank_details) {
console.log("Bank:", payment.bank_details.bank_name);
console.log("Account:", payment.bank_details.account_number);
console.log("Holder:", payment.bank_details.account_holder);
}
return payment;
} catch (error) {
if (error instanceof LuxCoreError) {
console.error(`API Error (${error.statusCode}): ${error.message}`);
} else {
console.error("Unexpected error:", error);
}
}
}
createDeposit();
Create a Withdrawal (Payout)
async function createPayout() {
const payout = await client.createPayment({
amount: 50000,
currency: "MXN",
method: "spei",
type: "withdrawal",
merchant_reference: `payout_${Date.now()}`,
customer: {
name: "Finance Team",
email: "finance@company.com",
},
payout: {
recipient_name: "Maria Garcia",
bank_account: "012345678901234567",
bank_code: "002",
reference: "PAYOUT-001",
},
});
console.log("Payout created:", payout.transaction_id);
return payout;
}
List Payments
async function listRecentPayments() {
const result = await client.listPayments({
status: "completed",
limit: 20,
created_at_from: "2025-01-01T00:00:00Z",
});
console.log(`Found ${result.total} payments`);
console.log(
`Pagination: page ${result.pagination?.offset || 0}, total pages: ${result.pagination?.total_pages}`,
);
for (const payment of result.data) {
console.log(
`${payment.transaction_id}: ${payment.amount} ${payment.currency}`,
);
}
}
Webhook Handler (Express)
const express = require("express");
const { LuxCoreClient } = require("./luxcore");
const app = express();
// Capture raw body for signature verification
app.use(
express.json({
verify: (req, res, buf) => {
req.rawBody = buf.toString();
},
}),
);
const WEBHOOK_SECRET = process.env.LUXCORE_WEBHOOK_SECRET;
app.post("/webhooks/luxcore", (req, res) => {
const signature = req.headers["x-webhook-signature"];
const timestamp = parseInt(req.headers["x-webhook-timestamp"]);
// Verify using raw body (not re-serialized JSON)
try {
LuxCoreClient.verifyWebhookSignature(
req.rawBody,
signature,
timestamp,
WEBHOOK_SECRET,
);
} catch (error) {
console.error("Webhook verification failed:", error.message);
return res.status(400).json({ error: error.message });
}
// Payment examples: persist/deduplicate the envelope id before applying business effects
if (req.headers["x-webhook-test"] === "true" || !req.body?.data?.payment) {
return res.status(200).json({ received: true });
}
const event = `payment.${req.body.data.payment.status}`;
const webhookId = req.headers["x-webhook-id"];
const payment = req.body.data.payment;
console.log(
`Received ${event} for payment ${payment.id} (webhook: ${webhookId})`,
);
switch (event) {
case "payment.completed":
// Handle completed payment
console.log(`Payment ${payment.id} completed!`);
// Update database, send confirmation email, etc.
break;
case "payment.failed":
// Handle failed payment
console.log(`Payment ${payment.id} failed: ${payment.failure_reason}`);
break;
case "payment.cancelled":
// Handle cancelled payment
console.log(`Payment ${payment.id} cancelled`);
break;
default:
console.log(`Unhandled event: ${event}`);
}
res.status(200).json({ received: true });
});
app.listen(3000, () => {
console.log("Webhook server listening on port 3000");
});
TypeScript Version
import axios, { AxiosInstance } from "axios";
import crypto from "crypto";
interface PaymentRequest {
amount: number;
currency: string;
method: string;
type: "deposit" | "withdrawal" | "deposit_pp" | "withdrawal_pp";
merchant_reference: string;
customer: {
name: string;
email: string;
phone?: string;
external_id?: string;
};
payout?: {
recipient_name: string;
bank_account: string;
bank_code: string;
reference?: string;
account_type?: string;
beneficiary_tax_id?: string;
description?: string;
payid?: string;
payid_type?: "email" | "phone" | "abn";
};
webhook_url?: string;
webhook_events?: string[];
customer_id?: number;
description?: string;
traffic_type?: "primary" | "secondary" | "vip";
metadata?: Record<string, unknown>;
}
interface Payment {
transaction_id?: string; // Creation response
id?: string; // Lookup response
status: string;
amount: number;
currency: string;
method: string | null;
type: string;
fee_amount?: number;
net_amount?: number;
error_code?: string;
error_message?: string;
created_at: string;
bank_details?: {
amount: string;
purpose: string;
currency: string;
bank_name: string;
swift_code: string | null;
account_holder: string;
account_number: string;
};
}
class LuxCoreClient {
private client: AxiosInstance;
private static BASE_URL = "https://api.lux-core.io/api/v1";
constructor(apiKey: string = process.env.LUXCORE_API_KEY!) {
this.client = axios.create({
baseURL: LuxCoreClient.BASE_URL,
headers: {
"X-API-Key": apiKey,
"Content-Type": "application/json",
},
});
}
async createPayment(data: PaymentRequest): Promise<Payment> {
const response = await this.client.post<Payment>("/payments", data);
return response.data;
}
async getPayment(paymentId: string): Promise<Payment> {
const response = await this.client.get<Payment>(`/payments/${paymentId}`);
return response.data;
}
// IMPORTANT: Pass the raw request body string, not a parsed/re-serialized object
static verifyWebhookSignature(
rawBody: string,
signature: string,
timestamp: number,
secret: string,
): boolean {
const currentTime = Math.floor(Date.now() / 1000);
if (Math.abs(currentTime - timestamp) > 300) {
throw new Error("Webhook timestamp too old");
}
const payloadToSign = `${timestamp}.${rawBody}`;
const expectedSignature =
"hmac_sha256=" +
crypto.createHmac("sha256", secret).update(payloadToSign).digest("hex");
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature),
);
}
}
export { LuxCoreClient, PaymentRequest, Payment };
Error Handling
const { LuxCoreClient, LuxCoreError } = require("./luxcore");
async function createPaymentOnce(data) {
const client = new LuxCoreClient();
try {
return await client.createPayment(data);
} catch (error) {
// Preserve merchant_reference and any payment ID. After a timeout or
// uncertain server response, verify the original outcome before another POST.
console.error(error.errorCode ?? error.statusCode, error.message);
throw error;
}
}
