Node.js SDK
@lunixi/node-sdk is a thin server-side wrapper over the Lunixi REST API. It does Ed25519 request signing, caches the token and renews it when it expires, and unwraps the response envelope for you. It is framework-agnostic and works with Express, Fastify, NestJS, Hapi, serverless functions and plain Node.js.
Installation
npm install @lunixi/node-sdkNode.js 18 or newer is required. TypeScript type definitions ship with the package.
Creating the client
const { LunixiClient } = require('@lunixi/node-sdk');
const lunixi = new LunixiClient({
baseUrl: 'https://api-gateway.lunixi.com',
keyId: process.env.LUNIXI_KEY_ID,
privateKey: process.env.LUNIXI_PRIVATE_KEY_PEM,
});| Option | Required | Default |
|---|---|---|
baseUrl | yes | — |
keyId | yes | — |
privateKey | yes | — |
timeoutMs | no | 30000 |
maxRetries | no | 2 |
The key decides the environment: calls made with a TEST key (mk_test_…) go to the test environment; there is no separate environment option.
Create the client once at application start and share it. The token cache lives in this object; creating a new client per request means fetching a new token per request.
Key pair
You can generate the key pair with the SDK:
const { publicKey, privateKey } = LunixiClient.generateKeyPair();Register the public key in the dashboard; the private key stays on your own server only. → Authentication
Coverage
| Product | Access |
|---|---|
| Payments | lunixi.payments |
| Payment links | lunixi.paymentLinks |
| Fraud decisions and events | lunixi.fraud |
| Wallet | lunixi.wallet |
| Bank accounts | lunixi.bankConnect |
| Sanction screening | lunixi.sanction |
| Reference data (address codes) | lunixi.reference |
| Insurance | lunixi.insurance |
| Webhook signature verification | WebhookVerifier |
Subscription, KYC, marketplace and identity endpoints are not in the Node.js SDK; use the PHP SDK or REST without an SDK for those.
Starting a payment with Express
The SDK has no Express dependency; the example only shows the usage.
const express = require('express');
const {
LunixiClient,
CreateIntentRequest,
Buyer,
Address,
BasketItem,
} = require('@lunixi/node-sdk');
const lunixi = new LunixiClient({
baseUrl: process.env.LUNIXI_BASE_URL,
keyId: process.env.LUNIXI_KEY_ID,
privateKey: process.env.LUNIXI_PRIVATE_KEY,
});
const app = express();
app.use(express.json());
app.post('/checkout', async (req, res, next) => {
try {
const request = new CreateIntentRequest(10050, 'TRY', `ORD-${Date.now()}`)
.withCallbackUrl('https://merchant.example/payment/callback')
.withBuyer(new Buyer({
name: 'Ada',
surname: 'Yilmaz',
identityNumber: '11111111111',
email: '[email protected]',
gsmNumber: '+905350000000',
city: 'Istanbul',
country: 'Turkey',
zipCode: '34000',
ip: req.ip,
}))
.withBillingAddress(new Address({
address: 'Example Street 1',
zipCode: '34000',
contactName: 'Ada Yilmaz',
city: 'Istanbul',
country: 'Turkey',
}))
.withBasketItems([
new BasketItem({
id: 'SKU-001',
price: 10050,
name: 'Demo Product',
category1: 'Demo',
itemType: BasketItem.TYPE_PHYSICAL,
}),
]);
const intent = await lunixi.payments.createIntent(request, `checkout:${request.toJSON().orderId}`);
res.json(intent);
} catch (error) {
next(error);
}
});amount (the first argument) is an integer in the currency's minor unit (10050 = 100.50 TRY); the basket items' price values must add up to amount. → Checkout Form
Using it with NestJS
Create one LunixiClient provider and inject it into your services:
import { DynamicModule, Global, Module } from '@nestjs/common';
import { LunixiClient, LunixiClientOptions } from '@lunixi/node-sdk';
export const LUNIXI_CLIENT = Symbol('LUNIXI_CLIENT');
@Global()
@Module({})
export class LunixiModule {
static forRoot(options: LunixiClientOptions): DynamicModule {
return {
module: LunixiModule,
providers: [{ provide: LUNIXI_CLIENT, useValue: new LunixiClient(options) }],
exports: [LUNIXI_CLIENT],
};
}
}import { Inject, Injectable } from '@nestjs/common';
import { CreateIntentRequest, LunixiClient } from '@lunixi/node-sdk';
import { LUNIXI_CLIENT } from './lunixi.module';
@Injectable()
export class MerchantPaymentsService {
constructor(@Inject(LUNIXI_CLIENT) private readonly lunixi: LunixiClient) {}
createCheckoutIntent(input: { orderId: string; amount: number }) {
const request = new CreateIntentRequest(input.amount, 'TRY', input.orderId)
.withCallbackUrl('https://merchant.example/payments/callback');
return this.lunixi.payments.createIntent(request, `checkout:${input.orderId}`);
}
capture(paymentId: string) {
return this.lunixi.payments.capture(paymentId, null, `capture:${paymentId}`);
}
}Add LunixiModule.forRoot({ baseUrl, keyId, privateKey }) to your application module. For a real payment also send the buyer, billing address and basket items (as in the Express example above).
Webhook verification
The signature is computed over the raw bytes of the incoming body. Verify the body raw, before any JSON middleware touches it:
const express = require('express');
const { WebhookVerifier } = require('@lunixi/node-sdk');
const verifier = new WebhookVerifier(process.env.LUNIXI_WEBHOOK_SECRET);
const app = express();
app.post('/webhooks/lunixi', express.raw({ type: '*/*' }), (req, res) => {
let event;
try {
event = verifier.verify(req.body, req.headers);
} catch {
res.status(400).send('invalid signature');
return;
}
// Dedupe on event.id; delivery is at least once.
switch (event.type) {
case 'payment.captured':
break;
case 'payment.failed':
break;
}
res.status(200).send('ok');
});By default the verification also checks a 300-second timestamp window (new WebhookVerifier(secret, { toleranceSeconds })). → Webhooks
Idempotency
Money-moving calls require a stable idempotency key: capture, refund, void, directMethodPayment, storeCard, deactivateStoredCard, paymentLinks.create, paymentLinks.send and the wallet.* calls that create or move something. Use a deterministic key per operation and send the same key when you retry the same operation.
Payment methods, split payments, payment links, wallet, bank accounts, insurance and sanction calls are described on their product pages; the Node.js examples there use this SDK.
Related
- Authentication — key, signature and token lifetime
- Testing — working with a TEST key
- Error Handling — response envelope and error codes