Integrations
Node.js

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-sdk

Node.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,
});
OptionRequiredDefault
baseUrlyes—
keyIdyes—
privateKeyyes—
timeoutMsno30000
maxRetriesno2

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

ProductAccess
Paymentslunixi.payments
Payment linkslunixi.paymentLinks
Fraud decisions and eventslunixi.fraud
Walletlunixi.wallet
Bank accountslunixi.bankConnect
Sanction screeninglunixi.sanction
Reference data (address codes)lunixi.reference
Insurancelunixi.insurance
Webhook signature verificationWebhookVerifier

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