Skip to content

MoanaPay/node-sdk

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@moanapay/sdk

Official TypeScript SDK for the MoanaPay API. Supports payments, connected accounts, split payments, refunds, payouts, and webhook verification.

Installation

npm install @moanapay/sdk

Quick 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 environment
  • tp_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: 'vendor@example.com',
  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 fetch and crypto)
  • TypeScript >= 5.0 (recommended)

License

MIT

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors