@moanapay/sdk
v0.1.0
Published
MoanaPay TypeScript SDK for payment processing, split payments, and connected accounts
Maintainers
Readme
@moanapay/sdk
Official TypeScript SDK for the MoanaPay API. Supports payments, connected accounts, split payments, refunds, payouts, and webhook verification.
Installation
npm install @moanapay/sdkQuick Start
import { MoanaPay } from '@moanapay/sdk';
const moanapay = new MoanaPay('tp_live_xxx');
// Create a payment
const payment = await moanapay.payments.create({
amountMinor: '10000', // T$100.00 in seniti
currency: 'TOP',
returnUrl: 'https://example.com/success',
});
console.log(payment.id, payment.checkoutUrl);Configuration
const moanapay = new MoanaPay('tp_live_xxx', {
baseUrl: 'https://api.moanapay.io', // default
timeout: 10000, // 10s default
maxRetries: 3, // retries on 429/5xx
});The SDK auto-detects the environment from the API key prefix:
tp_live_→ Live environmenttp_test_→ Sandbox environment
Payments
// Create
const payment = await moanapay.payments.create({
amountMinor: '5000',
currency: 'TOP',
returnUrl: 'https://example.com/return',
metadata: { orderId: 'order-123' },
}, { idempotencyKey: MoanaPay.generateIdempotencyKey() });
// Get
const payment = await moanapay.payments.get('pay_abc123');
// List
const { items, total, totalPages } = await moanapay.payments.list({
status: 'SUCCEEDED',
page: 1,
pageSize: 20,
});Split Payments
Create a payment with inline splits:
const payment = await moanapay.payments.create({
amountMinor: '100000',
currency: 'TOP',
returnUrl: 'https://example.com/return',
splits: [
{ connectedAccountId: 'ca_vendor1', amountMinor: 60000 },
{ connectedAccountId: 'ca_vendor2', amountMinor: 30000 },
],
}, { idempotencyKey: MoanaPay.generateIdempotencyKey() });Or use split rules:
const payment = await moanapay.payments.create({
amountMinor: '100000',
currency: 'TOP',
returnUrl: 'https://example.com/return',
applySplitRules: true,
splitAccounts: ['ca_vendor1', 'ca_vendor2'],
}, { idempotencyKey: MoanaPay.generateIdempotencyKey() });Release held splits:
// Release all splits for a payment
await moanapay.payments.releaseSplits('pay_abc123', {
idempotencyKey: MoanaPay.generateIdempotencyKey(),
});
// Release a single split
await moanapay.payments.releaseSplit('pay_abc123', 'split_xyz', {
idempotencyKey: MoanaPay.generateIdempotencyKey(),
});Connected Accounts
// Create a connection (invite a vendor)
const account = await moanapay.connectedAccounts.create({
vendorEmail: '[email protected]',
platformLabel: 'Supplier A',
commissionType: 'PERCENTAGE',
commissionBps: 500, // 5%
}, { idempotencyKey: MoanaPay.generateIdempotencyKey() });
// List
const { items } = await moanapay.connectedAccounts.list({ status: 'ACTIVE' });
// Get
const account = await moanapay.connectedAccounts.get('ca_abc123');
// Update
const updated = await moanapay.connectedAccounts.update('ca_abc123', {
commissionBps: 750,
});
// Approve / Reject
await moanapay.connectedAccounts.approve('ca_abc123', {
idempotencyKey: MoanaPay.generateIdempotencyKey(),
});
await moanapay.connectedAccounts.reject('ca_abc123', 'Not a valid vendor');
// Revoke
await moanapay.connectedAccounts.revoke('ca_abc123', 'Contract ended');Split Rules
// Create
const rule = await moanapay.connectedAccounts.createRule('ca_abc123', {
name: 'Standard Split',
splitType: 'PERCENTAGE',
splitBps: 3000, // 30%
priority: 1,
});
// List
const rules = await moanapay.connectedAccounts.listRules('ca_abc123');
// Update
await moanapay.connectedAccounts.updateRule('ca_abc123', 'rule_xyz', {
splitBps: 3500,
});
// Delete
await moanapay.connectedAccounts.deleteRule('ca_abc123', 'rule_xyz');Connections (Vendor-side)
// List incoming connections
const { items } = await moanapay.connections.list();
// Get connection details
const connection = await moanapay.connections.get('ca_abc123');
// Approve invitation
await moanapay.connections.approve('ca_abc123', {
idempotencyKey: MoanaPay.generateIdempotencyKey(),
});
// Reject invitation
await moanapay.connections.reject('ca_abc123', 'Not interested');
// Disconnect
await moanapay.connections.disconnect('ca_abc123', 'No longer partnering');
// View split payment history
const { items: splits } = await moanapay.connections.listSplits('ca_abc123', {
page: 1,
pageSize: 50,
});Refunds
// Create
const refund = await moanapay.refunds.create({
paymentId: 'pay_abc123',
amount: 5000,
reason: 'Customer requested',
}, { idempotencyKey: MoanaPay.generateIdempotencyKey() });
// Get
const refund = await moanapay.refunds.get('ref_abc123');
// List
const { items } = await moanapay.refunds.list({ paymentId: 'pay_abc123' });Payouts
// Create
const payout = await moanapay.payouts.create({
amount: '50000',
currency: 'TOP',
bankDetails: {
bankAccountName: 'Business Account',
bankAccountNumber: '1234567890',
bankName: 'BSP Tonga',
},
description: 'Weekly payout',
}, { idempotencyKey: MoanaPay.generateIdempotencyKey() });
// Get
const payout = await moanapay.payouts.get('po_abc123');
// List
const { items } = await moanapay.payouts.list({ status: 'COMPLETED' });Webhook Verification
import { MoanaPay } from '@moanapay/sdk';
const moanapay = new MoanaPay('tp_live_xxx');
// In your webhook handler (e.g. Express)
app.post('/webhooks', (req, res) => {
const signature = req.headers['x-webhook-signature'] as string;
const webhookSecret = process.env.WEBHOOK_SECRET!;
try {
const event = moanapay.webhooks.constructEvent(
req.body, // raw body string
signature,
webhookSecret,
);
switch (event.type) {
case 'payment.succeeded':
// Handle successful payment
break;
case 'payment.split.credited':
// Handle split payment credited to vendor
break;
case 'refund.completed':
// Handle refund
break;
}
res.status(200).send('OK');
} catch (error) {
if (error instanceof MoanaPay.errors.SignatureVerificationError) {
res.status(400).send('Invalid signature');
} else {
res.status(500).send('Server error');
}
}
});Or verify without parsing:
const isValid = moanapay.webhooks.verifySignature(rawBody, signature, secret);Error Handling
import { MoanaPay, ApiError } from '@moanapay/sdk';
try {
await moanapay.payments.create({ ... });
} catch (error) {
if (error instanceof MoanaPay.errors.AuthenticationError) {
// 401 — invalid API key
} else if (error instanceof MoanaPay.errors.PermissionError) {
// 403 — capability disabled or insufficient permissions
} else if (error instanceof MoanaPay.errors.NotFoundError) {
// 404 — resource not found
} else if (error instanceof MoanaPay.errors.RateLimitError) {
// 429 — rate limited
console.log('Retry after:', error.retryAfter, 'seconds');
} else if (error instanceof MoanaPay.errors.IdempotencyError) {
// 409 — idempotency conflict
} else if (error instanceof ApiError) {
// Other API error
console.log(error.status, error.code, error.message, error.requestId);
}
}Idempotency
All state-changing operations (POST/PUT/PATCH) accept an optional idempotencyKey to ensure exactly-once processing:
const key = MoanaPay.generateIdempotencyKey();
const payment = await moanapay.payments.create(params, {
idempotencyKey: key,
});Requirements
- Node.js >= 18.0.0 (uses native
fetchandcrypto) - TypeScript >= 5.0 (recommended)
License
MIT
