
Company News
Socket Partners with Replit to Block Malicious Packages in AI-Powered Development
Replit is integrating Socket Firewall into its AI-powered development experience to help protect builders from malicious open source packages.
@shwary/node-sdk
Advanced tools
Official Node.js/TypeScript SDK for Shwary Payment API - Mobile Money payments for DRC, Kenya, and Uganda
Official Node.js/TypeScript SDK for Shwary Payment API - Make Mobile Money payments for DRC, Kenya, and Uganda.
Developed by Tresor Kasenda - Independent Developer
| Country | Currency | Dial Code | Min Amount |
|---|---|---|---|
| DRC | CDF | +243 | 2,900 |
| Kenya | KES | +254 | 0 |
| Uganda | UGX | +256 | 0 |
npm install @shwary/node-sdk
Or with other package managers:
yarn add @shwary/node-sdk
pnpm add @shwary/node-sdk
fetch support)Create a .env file (copy from .env.example):
SHWARY_MERCHANT_ID=your-merchant-id
SHWARY_MERCHANT_KEY=your-merchant-secret-key
SHWARY_SANDBOX=true
import { Shwary, Country } from '@shwary/node-sdk';
// Initialize once (load from env vars)
Shwary.initFromEnvironment();
// Make a payment for DRC
const transaction = await Shwary.payDRC(
5000, // amount in CDF
'+243812345678', // customer phone
'https://myapp.com/webhooks/shwary' // optional callback URL
);
console.log(`Payment ID: ${transaction.id}`);
console.log(`Status: ${transaction.status}`); // 'pending' or 'completed' in sandbox
Use the static Shwary class for straightforward usage:
import { Shwary } from '@shwary/node-sdk';
Shwary.initFromEnvironment();
// Country-specific shortcuts
const drcTx = await Shwary.payDRC(5000, '+243812345678');
const kenyaTx = await Shwary.payKenya(2500, '+254712345678');
const ugandaTx = await Shwary.payUganda(10000, '+256701234567');
// Generic payment
const customTx = await Shwary.pay(5000, '+243812345678', Country.DRC);
// Retrieve transaction
const tx = await Shwary.getTransaction(drcTx.id);
// Check status
if (tx.isCompleted()) {
console.log('Payment successful');
}
Use ShwaryClient for dependency injection and testability:
import { ShwaryClient, Config } from '@shwary/node-sdk';
const config = new Config({
merchantId: process.env.SHWARY_MERCHANT_ID!,
merchantKey: process.env.SHWARY_MERCHANT_KEY!,
sandbox: true,
});
const client = new ShwaryClient(config);
// Use like Shwary facade
const transaction = await client.payDRC(5000, '+243812345678');
import { ShwaryClient, Config } from '@shwary/node-sdk';
const logger = {
debug: (msg, ctx) => console.log(`[DEBUG] ${msg}`, ctx),
info: (msg, ctx) => console.log(`[INFO] ${msg}`, ctx),
error: (msg, ctx) => console.error(`[ERROR] ${msg}`, ctx),
};
const client = ShwaryClient.fromEnvironment(logger);
Shwary.payDRC(amount, phone, callbackUrl?)Create a payment for Democratic Republic of Congo.
const tx = await Shwary.payDRC(5000, '+243812345678');
// amount: must be > 2900 CDF
// phone: must start with +243
Shwary.payKenya(amount, phone, callbackUrl?)Create a payment for Kenya.
const tx = await Shwary.payKenya(2500, '+254712345678');
Shwary.payUganda(amount, phone, callbackUrl?)Create a payment for Uganda.
const tx = await Shwary.payUganda(10000, '+256701234567');
Shwary.pay(amount, phone, country, callbackUrl?)Create a payment for any supported country.
const tx = await Shwary.pay(5000, '+243812345678', Country.DRC);
Shwary.getTransaction(id)Retrieve a transaction by ID.
const tx = await Shwary.getTransaction('transaction-uuid');
console.log(tx.status); // 'pending', 'completed', or 'failed'
Shwary.parseWebhook(payload)Parse a webhook payload from Shwary.
app.post('/webhooks/shwary', (req, res) => {
const transaction = Shwary.parseWebhook(JSON.stringify(req.body));
if (transaction.isCompleted()) {
// Handle successful payment
}
res.json({ success: true });
});
Shwary.webhook().createResponse(success, message?)Create a webhook response.
const response = Shwary.webhook().createResponse(
true,
'Webhook processed'
);
res.json(response);
The SDK provides custom error classes for different scenarios:
import {
ValidationError,
AuthenticationError,
ApiError,
} from '@shwary/node-sdk';
try {
await Shwary.payDRC(100, '+243812345678'); // Amount too low
} catch (error) {
if (error instanceof ValidationError) {
console.error('Invalid input:', error.message);
console.error('Context:', error.context); // { field: 'amount', minimum: 2900, ... }
} else if (error instanceof AuthenticationError) {
console.error('Auth failed:', error.message);
} else if (error instanceof ApiError) {
console.error('API error:', error.code);
}
}
ValidationError - Input validation failed (400)
invalidAmount(amount, country)invalidPhoneNumber(phone, country)invalidCallbackUrl(url)missingRequiredField(field)AuthenticationError - Credentials invalid (401)
invalidCredentials()ApiError - API call failed
fromResponse(response, body) - Parse HTTP error responsenetworkError(message, cause)badGateway(message)clientNotFound(phone)# Required
SHWARY_MERCHANT_ID=your-merchant-id
SHWARY_MERCHANT_KEY=your-merchant-secret-key
# Optional
SHWARY_SANDBOX=true # default: false
SHWARY_BASE_URL=https://api.shwary.com # default: https://api.shwary.com
SHWARY_TIMEOUT=30000 # default: 30000 (milliseconds)
import { Config, ShwaryClient } from '@shwary/node-sdk';
const config = new Config({
merchantId: 'your-id',
merchantKey: 'your-key',
sandbox: true,
timeout: 60000,
baseUrl: 'https://api.shwary.com',
});
const client = new ShwaryClient(config);
The SDK is fully typed and provides excellent TypeScript support:
import { Shwary, Country, Transaction, ValidationError } from '@shwary/node-sdk';
Shwary.initFromEnvironment();
async function processPayment(
amount: number,
phone: string,
country: Country['DRC']
): Promise<void> {
try {
const transaction: Transaction = await Shwary.pay(
amount,
phone,
country
);
if (transaction.isCompleted()) {
console.log('Payment successful');
}
} catch (error) {
if (error instanceof ValidationError) {
// TypeScript knows error.context exists
console.error(error.context);
}
throw error;
}
}
import express from 'express';
import { Shwary, TransactionStatus } from '@shwary/node-sdk';
const app = express();
app.use(express.json());
Shwary.initFromEnvironment();
app.post('/webhooks/shwary', async (req, res) => {
try {
const transaction = Shwary.parseWebhook(JSON.stringify(req.body));
if (transaction.status === TransactionStatus.COMPLETED) {
// Update database, send confirmation email, etc.
console.log(`✓ Payment completed: ${transaction.id}`);
} else if (transaction.status === TransactionStatus.FAILED) {
console.log(`✗ Payment failed: ${transaction.failureReason}`);
}
res.json(Shwary.webhook().createResponse(true));
} catch (error) {
res.status(400).json(
Shwary.webhook().createResponse(false, error.message)
);
}
});
Shwary sends POST requests to your callback URL with this format:
{
"id": "transaction-uuid",
"userId": "merchant-uuid",
"amount": 5000,
"currency": "CDF",
"status": "completed",
"recipientPhoneNumber": "+243812345678",
"referenceId": "merchant-ref-id",
"createdAt": "2025-01-16T10:15:00Z",
"updatedAt": "2025-01-16T10:16:00Z",
"completedAt": "2025-01-16T10:16:00Z",
"isSandbox": false,
...
}
Test your integration without making real payments:
// Initialize in sandbox mode
Shwary.initFromObject({
merchantId: 'test-id',
merchantKey: 'test-key',
sandbox: true,
});
// Sandbox payments complete immediately
const tx = await Shwary.payDRC(5000, '+243812345678');
console.log(tx.status); // 'completed'
console.log(tx.isSandbox); // true
Sandbox payments:
Transactions can be in three states:
const tx = await Shwary.getTransaction(id);
// Check individual status
if (tx.isPending()) { /* payment in progress */ }
if (tx.isCompleted()) { /* payment successful */ }
if (tx.isFailed()) { /* payment failed */ }
// Check terminal status
if (tx.isTerminal()) { /* will not change */ }
// Access status directly
switch (tx.status) {
case TransactionStatus.PENDING:
// Awaiting processing
break;
case TransactionStatus.COMPLETED:
// Successfully processed
break;
case TransactionStatus.FAILED:
// Failed, check failureReason
console.log(tx.failureReason);
break;
}
try {
const tx = await Shwary.payDRC(amount, phone);
} catch (error) {
// Handle specific error types
if (error instanceof ValidationError) {
// Show user-friendly validation message
} else if (error instanceof AuthenticationError) {
// Check credentials
} else if (error instanceof ApiError) {
// Log and retry
}
}
Don't poll for transaction status; use webhooks instead:
// ❌ Don't do this
for (let i = 0; i < 10; i++) {
const tx = await Shwary.getTransaction(id);
if (tx.isTerminal()) break;
await delay(5000);
}
// ✅ Do this instead
// Set callback URL when creating payment
const tx = await Shwary.payDRC(5000, phone, callbackUrl);
// Shwary will POST to your callback URL when status changes
app.post('/webhooks/shwary', async (req, res) => {
const tx = Shwary.parseWebhook(JSON.stringify(req.body));
// Check if we already processed this transaction
const existing = await db.transactions.findOne({ id: tx.id });
if (existing) {
// Already processed, just return success
return res.json({ success: true });
}
// Process new transaction
await db.transactions.insert(tx.toJSON());
res.json({ success: true });
});
// Save the merchant's reference ID for reconciliation
const tx = await Shwary.payDRC(5000, phone);
await db.payments.insert({
shwaryId: tx.id, // Shwary's transaction ID
referenceId: tx.referenceId, // Your merchant reference
status: tx.status,
createdAt: tx.createdAt,
});
See the examples/ directory for complete examples:
basic-payment.ts - Simple payment usagewebhook-handler.ts - Express.js webhook server// ❌ Empty credential
Shwary.initFromEnvironment(); // Env var not set
// ✅ Set environment variable
process.env.SHWARY_MERCHANT_ID = 'your-id';
Shwary.initFromEnvironment();
// ❌ Wrong format
await Shwary.payDRC(5000, '243812345678'); // Missing +
// ✅ Correct E.164 format
await Shwary.payDRC(5000, '+243812345678');
// ❌ DRC minimum is 2900
await Shwary.payDRC(1000, '+243812345678');
// ✅ Meet country minimum
await Shwary.payDRC(5000, '+243812345678');
// Check your credentials in the Shwary dashboard
// Make sure you've copied the key (only shown once)
Shwary.initFromObject({
merchantId: 'check-your-dashboard',
merchantKey: 'copy-fresh-key-from-dashboard',
sandbox: true, // Test in sandbox first
});
Contributions are welcome! Please follow these guidelines:
git checkout -b feature/amazing-feature)npm test)git commit -m 'Add amazing feature')git push origin feature/amazing-feature)# Clone the repository
git clone https://github.com/Tresor-Kasenda/shwary-node-sdk.git
cd shwary-node-sdk
# Install dependencies
npm install
# Run tests
npm test
# Build
npm run build
# Type check
npm run type-check
# Run tests
npm test
# Watch mode
npm run test:watch
# Coverage
npm run test:coverage
# Build for distribution
npm run build
# Type check
npm run type-check
# Lint
npm run lint
# Format
npm run format
Complete API documentation is available in the TypeScript type definitions. Your IDE will provide IntelliSense/autocomplete for all methods and parameters.
For detailed JSDoc comments, see the source files in the src/ directory.
MIT - see LICENSE file for details
For issues and questions:
Tresor Kasenda - Independent Developer
This SDK is developed and maintained by Tresor Kasenda. For professional inquiries or custom development, feel free to reach out.
See CHANGELOG.md for version history and breaking changes.
Made with ❤️ by Tresor Kasenda for the African developer community
Repository: github.com/Tresor-Kasenda/shwary-node-sdk
FAQs
Official Node.js/TypeScript SDK for Shwary Payment API - Mobile Money payments for DRC, Kenya, and Uganda
We found that @shwary/node-sdk demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Company News
Replit is integrating Socket Firewall into its AI-powered development experience to help protect builders from malicious open source packages.

Security News
npm confirmed a tooling bug incorrectly marked several one-character packages as security holders and said it was working on a rollback.

Research
/Security News
Newer packages in this compromise use native extensions and .pth loaders to execute JavaScript stealers in developer environments.