Skip to content

Node.js SDK

The official Node.js library for Salam Gateway.

Installation

bash
npm install @salamgateway/node
bash
yarn add @salamgateway/node
bash
pnpm add @salamgateway/node

Setup

typescript
import Salam from '@salamgateway/node';

const salam = new Salam({
  apiKey: process.env.SALAM_API_KEY,
  sandbox: process.env.NODE_ENV !== 'production',
  webhookSecret: process.env.SALAM_WEBHOOK_SECRET,
});

Configuration Options

OptionTypeDefaultDescription
apiKeystringrequiredYour API key
sandboxbooleanfalseUse sandbox environment
webhookSecretstring-Webhook signing secret
timeoutnumber30000Request timeout in ms
maxRetriesnumber3Max retry attempts

Payments

Create Payment

typescript
const payment = await salam.payments.create({
  amount: 10000, // RM 100.00
  description: 'Order #1234',
  success_url: 'https://example.com/success',
  cancel_url: 'https://example.com/cancel',
  metadata: {
    order_id: '1234',
    customer_email: 'customer@example.com',
  },
});

console.log(payment.redirect_url);

Retrieve Payment

typescript
const payment = await salam.payments.retrieve('pay_abc123');

console.log(payment.status); // 'captured', 'pending', etc.

List Payments

typescript
const payments = await salam.payments.list({
  status: 'captured',
  limit: 10,
  page: 1,
});

for (const payment of payments.data) {
  console.log(payment.id, payment.amount);
}

Capture Payment

typescript
const payment = await salam.payments.capture('pay_abc123');

Void Payment

typescript
const payment = await salam.payments.void('pay_abc123');

Refunds

Create Refund

typescript
const refund = await salam.refunds.create({
  payment_id: 'pay_abc123',
  amount: 10000, // Full or partial refund
  reason: 'requested_by_customer',
});

Retrieve Refund

typescript
const refund = await salam.refunds.retrieve('rfnd_xyz789');

List Refunds

typescript
const refunds = await salam.refunds.list({
  payment_id: 'pay_abc123',
});

Webhooks

Construct Event

typescript
import express from 'express';

app.post('/webhooks/salam',
  express.raw({ type: 'application/json' }),
  (req, res) => {
    const signature = req.headers['salam-signature'];
    
    try {
      const event = salam.webhooks.constructEvent(
        req.body,
        signature
      );
      
      // Handle event
      switch (event.type) {
        case 'payment.captured':
          handlePaymentCaptured(event.data);
          break;
        case 'payment.failed':
          handlePaymentFailed(event.data);
          break;
      }
      
      res.json({ received: true });
    } catch (error) {
      res.status(400).send('Invalid signature');
    }
  }
);

Error Handling

The SDK throws typed errors:

typescript
import {
  ApiError,
  AuthError,
  ValidationError,
  ResourceError,
  RateLimitError,
} from '@salamgateway/node';

try {
  const payment = await salam.payments.create(data);
} catch (error) {
  if (error instanceof AuthError) {
    console.error('Authentication failed');
  } else if (error instanceof ValidationError) {
    console.error(`Invalid ${error.param}:`, error.message);
  } else if (error instanceof ResourceError) {
    console.error('Resource not found');
  } else if (error instanceof RateLimitError) {
    console.error('Rate limited. Retry after:', error.retryAfter);
  } else if (error instanceof ApiError) {
    console.error('API error:', error.statusCode, error.message);
  }
}

TypeScript Support

Full TypeScript support with type definitions:

typescript
import { Payment, PaymentStatus, Refund } from '@salamgateway/node';

const payment: Payment = await salam.payments.retrieve('pay_xxx');
const status: PaymentStatus = payment.status;

const refund: Refund = await salam.refunds.create({
  payment_id: payment.id,
  amount: payment.amount,
});

Advanced Usage

Custom Timeout

typescript
const salam = new Salam({
  apiKey: 'sk_live_xxx',
  timeout: 60000, // 60 seconds
});

Disable Retries

typescript
const salam = new Salam({
  apiKey: 'sk_live_xxx',
  maxRetries: 0,
});

Request Logging

typescript
const salam = new Salam({
  apiKey: 'sk_live_xxx',
  onRequest: (config) => {
    console.log('Request:', config.method, config.url);
  },
  onResponse: (response) => {
    console.log('Response:', response.status);
  },
});

Examples

Express.js Integration

typescript
import express from 'express';
import Salam from '@salamgateway/node';

const app = express();
const salam = new Salam({
  apiKey: process.env.SALAM_API_KEY,
  webhookSecret: process.env.SALAM_WEBHOOK_SECRET,
});

// Create payment endpoint
app.post('/api/payments', async (req, res) => {
  try {
    const payment = await salam.payments.create({
      amount: req.body.amount,
      description: req.body.description,
      success_url: `${process.env.APP_URL}/success`,
      cancel_url: `${process.env.APP_URL}/cancel`,
    });
    
    res.json({ redirect_url: payment.redirect_url });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Webhook endpoint
app.post('/webhooks/salam',
  express.raw({ type: 'application/json' }),
  async (req, res) => {
    try {
      const event = salam.webhooks.constructEvent(
        req.body,
        req.headers['salam-signature']
      );
      
      res.json({ received: true });
      
      // Process async
      await processWebhook(event);
    } catch (error) {
      res.status(400).send('Webhook Error');
    }
  }
);

app.listen(3000);

NestJS Integration

typescript
import { Injectable } from '@nestjs/common';
import Salam from '@salamgateway/node';

@Injectable()
export class PaymentService {
  private salam: Salam;
  
  constructor() {
    this.salam = new Salam({
      apiKey: process.env.SALAM_API_KEY,
    });
  }
  
  async createPayment(amount: number, description: string) {
    return this.salam.payments.create({
      amount,
      description,
      success_url: process.env.SUCCESS_URL,
      cancel_url: process.env.CANCEL_URL,
    });
  }
  
  async handleWebhook(body: Buffer, signature: string) {
    const event = this.salam.webhooks.constructEvent(body, signature);
    
    switch (event.type) {
      case 'payment.captured':
        await this.handlePaymentSuccess(event.data);
        break;
    }
  }
}

Testing

Unit Tests

typescript
import { Salam } from '@salamgateway/node';

describe('Payment', () => {
  let salam: Salam;
  
  beforeEach(() => {
    salam = new Salam({
      apiKey: 'sk_test_xxx',
      sandbox: true,
    });
  });
  
  it('should create payment', async () => {
    const payment = await salam.payments.create({
      amount: 10000,
      success_url: 'https://example.com/success',
      cancel_url: 'https://example.com/cancel',
    });
    
    expect(payment.id).toMatch(/^pay_/);
    expect(payment.amount).toBe(10000);
  });
});

Support

Released under the MIT License.