Node.js SDK
The official Node.js library for Salam Gateway.
Installation
bash
npm install @salamgateway/nodebash
yarn add @salamgateway/nodebash
pnpm add @salamgateway/nodeSetup
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
| Option | Type | Default | Description |
|---|---|---|---|
apiKey | string | required | Your API key |
sandbox | boolean | false | Use sandbox environment |
webhookSecret | string | - | Webhook signing secret |
timeout | number | 30000 | Request timeout in ms |
maxRetries | number | 3 | Max 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);
});
});