@marmopay/node-sdk
v1.0.5
Published
Official Node.js SDK for Marmopay Payment Gateway
Downloads
67
Readme
Marmopay Node.js SDK
Official Node.js SDK for Marmopay Payment Gateway. Accept payments across Kenya and Uganda with M-Pesa, Airtel Money, and card payments.
Features
- 💳 Payment Processing - Create and manage payments
- 🔍 Transaction Management - Query and track transactions
- 💰 Refund Processing - Process full and partial refunds
- 🔐 Webhook Verification - Secure webhook signature validation
- 📘 TypeScript Support - Full type definitions included
- 🔄 Automatic Retries - Built-in retry logic with exponential backoff
- ⚡ Promise-based API - Modern async/await support
Installation
npm install @marmopay/node-sdkyarn add @marmopay/node-sdkpnpm add @marmopay/node-sdkQuick Start
import { Marmopay } from '@marmopay/node-sdk';
// Initialize the SDK
const marmopay = new Marmopay({
apiKey: process.env.MARMOPAY_API_KEY,
environment: 'live', // or 'test'
webhookSecret: process.env.MARMOPAY_WEBHOOK_SECRET,
});
// Create a payment
const payment = await marmopay.payments.create({
amount: 1000, // Amount in cents (1000 = 10.00 KES)
currency: 'KES',
customer: {
email: '[email protected]',
phone: '+254712345678',
},
callbackUrl: 'https://your-domain.com/payment/callback',
description: 'Order #12345',
});
console.log('Payment URL:', payment.paymentUrl);
// Redirect customer to payment.paymentUrlConfiguration
Required
apiKey- Your Marmopay API key (get from dashboard)
Optional
environment-'test'or'live'(default:'test')webhookSecret- Your webhook secret for signature verificationbaseUrl- Custom API base URL (default: auto-selected based on environment)timeout- Request timeout in milliseconds (default:30000)retries- Number of retry attempts (default:3)
API Reference
Payments
Create Payment
const payment = await marmopay.payments.create({
amount: 1000, // IMPORTANT: Must be a number (1000 = 10.00 KES in cents)
currency: 'KES',
customer: {
email: '[email protected]',
phone: '+254712345678',
firstName: 'John', // optional
lastName: 'Doe', // optional
},
callbackUrl: 'https://your-domain.com/callback', // optional
reference: 'ORD-12345', // optional, auto-generated if not provided
description: 'Order payment', // optional
metadata: { // optional
orderId: '12345',
customField: 'value',
},
});⚠️ Important Note About Amount:
amountmust be a number, not a string- Specify amounts in the smallest currency unit (cents/kobo)
- Examples: 1000 = 10.00 KES, 50000 = 500.00 KES
Get Payment
const payment = await marmopay.payments.get('TXN-20240115-ABC123');
console.log(payment.status); // 'pending', 'success', 'failed'List Payments
const payments = await marmopay.payments.list({
limit: 20,
page: 1,
});Transactions
Get Transaction
const transaction = await marmopay.transactions.get('TXN-20240115-ABC123');
if (transaction.status === 'success') {
console.log('Payment confirmed!');
// Fulfill order
}List Transactions
const transactions = await marmopay.transactions.list({
status: 'success',
startDate: '2024-01-01',
endDate: '2024-01-31',
limit: 50,
page: 1,
});Refunds
Create Refund
// Full refund
const refund = await marmopay.refunds.create({
transactionId: 123,
reason: 'Customer request',
});
// Partial refund
const partialRefund = await marmopay.refunds.create({
transactionId: 123,
amount: '500.00', // Refund half
reason: 'Partial refund',
});Get Refund
const refund = await marmopay.refunds.get(456);
console.log(refund.status); // 'pending', 'completed', 'failed'Webhooks
Webhooks are the recommended way to receive real-time payment notifications.
📌 Getting Your Webhook Secret:
- Log in to your Marmopay dashboard at https://pay.marmope.com
- Navigate to Settings → Webhooks
- Click "Rotate Secret" to generate a new webhook secret
- Copy the secret and store it securely in your environment variables
🔐 Security Note:
- Each merchant has a unique webhook secret
- Marmopay signs all webhooks using your merchant-specific secret
- The SDK verifies signatures using the same secret you provide
- Never share your webhook secret publicly or commit it to version control
⚠️ Important: Use raw body for webhook routes
Signature verification uses the exact raw request body (the bytes sent over the wire). You must use express.raw({ type: 'application/json' }) for your webhook route and not express.json(). If the body is parsed as JSON first, req.body becomes an object; re-stringifying it can produce a different string than the original (e.g. key order), so verification fails with "Invalid signature". Apply express.raw() only to the webhook route, and ensure no global express.json() runs before that route.
Express.js Example
import express from 'express';
import { Marmopay } from '@marmopay/node-sdk';
const app = express();
const marmopay = new Marmopay({
apiKey: process.env.MARMOPAY_API_KEY,
webhookSecret: process.env.MARMOPAY_WEBHOOK_SECRET, // ⚠️ REQUIRED for webhook verification
});
// Use express.raw() for webhook routes to preserve signature
app.post(
'/webhooks/marmopay',
express.raw({ type: 'application/json' }),
(req, res) => {
const signature = req.headers['x-marmopay-signature'] as string;
try {
// Verify and parse webhook using YOUR merchant-specific secret
const event = marmopay.webhooks.parse(req.body, signature);
// Process event
switch (event.event) {
case 'payment.success':
console.log('Payment successful:', event.data);
// Fulfill order
break;
case 'payment.failed':
console.log('Payment failed:', event.data);
// Notify customer
break;
case 'refund.processed':
console.log('Refund processed:', event.data);
// Update order
break;
}
// Always respond quickly (within 5 seconds)
res.json({ received: true });
} catch (error) {
console.error('Webhook verification failed:', error);
res.status(401).json({ error: 'Invalid signature' });
}
}
);Manual Verification
const isValid = marmopay.webhooks.verify(req.body, signature);
if (isValid) {
const event = JSON.parse(req.body);
// Process event
}Error Handling
The SDK throws specific error types for different scenarios:
import {
AuthenticationError,
ValidationError,
NotFoundError,
RateLimitError,
NetworkError,
WebhookSignatureError,
} from '@marmopay/node-sdk';
try {
const payment = await marmopay.payments.create(paymentData);
} catch (error) {
if (error instanceof AuthenticationError) {
console.error('Invalid API key');
} else if (error instanceof ValidationError) {
console.error('Invalid data:', error.details);
} else if (error instanceof NotFoundError) {
console.error('Resource not found');
} else if (error instanceof RateLimitError) {
console.error('Rate limit exceeded');
} else if (error instanceof NetworkError) {
console.error('Network error:', error.message);
} else {
console.error('Unexpected error:', error);
}
}TypeScript Support
The SDK is written in TypeScript and includes full type definitions:
import { Marmopay, Payment, Transaction, Refund } from '@marmopay/node-sdk';
const marmopay = new Marmopay({ apiKey: 'your-key' });
// Types are automatically inferred
const payment: Payment = await marmopay.payments.create({...});
const transaction: Transaction = await marmopay.transactions.get('ref');Environment Variables
Create a .env file:
MARMOPAY_API_KEY=mmp_test_your_key_here
MARMOPAY_WEBHOOK_SECRET=whsec_your_secret_here
MARMOPAY_ENVIRONMENT=testLoad with dotenv:
import 'dotenv/config';
import { Marmopay } from '@marmopay/node-sdk';
const marmopay = new Marmopay({
apiKey: process.env.MARMOPAY_API_KEY!,
environment: process.env.MARMOPAY_ENVIRONMENT as 'test' | 'live',
webhookSecret: process.env.MARMOPAY_WEBHOOK_SECRET,
});Testing
The SDK works seamlessly with test API keys:
const marmopay = new Marmopay({
apiKey: 'mmp_test_your_test_key', // Test key
environment: 'test',
});
// Use test phone numbers and amounts
const payment = await marmopay.payments.create({
amount: '100.00', // Test amount
currency: 'KES',
customer: {
email: '[email protected]',
phone: '+254700000000', // Test phone
},
});Best Practices
1. Use Environment Variables
Never hardcode API keys in your code.
2. Always Verify Webhooks
if (!marmopay.webhooks.verify(req.body, signature)) {
return res.status(401).send('Invalid signature');
}3. Respond to Webhooks Quickly
app.post('/webhooks/marmopay', async (req, res) => {
// Verify signature
// Respond immediately
res.json({ received: true });
// Process async
processWebhookAsync(event);
});4. Implement Idempotency
const processedWebhooks = new Set();
if (processedWebhooks.has(event.data.reference)) {
console.log('Already processed');
return;
}
processedWebhooks.add(event.data.reference);
// Process webhook5. Handle Errors Gracefully
try {
await marmopay.payments.create(data);
} catch (error) {
if (error instanceof ValidationError) {
// Show user-friendly message
} else {
// Log and alert developers
}
}Support
- 📧 Email: [email protected]
- 📚 Documentation: https://pay.marmope.com
- 💬 Discord: https://discord.gg/marmopay
- 🐛 Issues: https://github.com/marmopay/node-sdk/issues
License
MIT License - see LICENSE file for details
Changelog
v1.0.0 (2024-01-15)
- Initial release
- Payment creation and retrieval
- Transaction management
- Refund processing
- Webhook verification
- Full TypeScript support
- Automatic retries
- Comprehensive error handling
