Skip to content

About

NylonPay Typescript SDK

Resources

Stars

5 stars

Watchers

0 watching

Forks

Repository files navigation

Nylon Pay TypeScript SDK

Server-side SDK for integrating Nylon Pay into merchant applications. Supports TypeScript and JavaScript (ESM and CJS).

This package is the reference implementation of the Nylon Pay SDK Spec, the canonical, language-agnostic contract for building Nylon Pay SDKs in any language.

Full documentation

Install

npm install @nile-squad/nylonpay-ts

Quick Start

import { createNylonPay } from "@nile-squad/nylonpay-ts";

const nylonpay = createNylonPay({
  apiKey: "npk_test_...",
  apiSecret: "nps_test_...",
  onError: (error) => {
    if (error.reason === "SERVICES_DOWN") {
      pausePaymentAttempts(error.message);
    }
  },
});

const payment = await nylonpay.collectPayment({
  amount: 10000,
  currency: "UGX",
  customer: { name: "Jane", phoneNumber: "+256700000000" },
  description: "Order #1234",
});

payment.on("success", ({ transaction }) => fulfillOrder(transaction));
payment.on("failed", ({ error }) => notifyCustomer(error));

Configuration

Use your test keys to work in sandbox, or your production keys to go live.

Option Required Default Description
apiKey Yes Must start with npk_
apiSecret Yes Must start with nps_
baseUrl No https://api.nylonpay.com/api/services Override for a custom endpoint. LEGACY_BASE_URL is the original address, still served
timeoutMs No 90000 Request timeout in milliseconds
maxRetries No 3 Retry count for failed requests
maxPollIntervalMs No 2000 Polling interval for async payments
maxPollDurationMs No (none) Optional cap on total polling time. Omit to wait until terminal.
maxPollAttempts No (none) Optional cap on poll count. Omit to wait until terminal.
onDelayed No "wait" "return" hands back a delayed still-pending payment; "wait" keeps polling
onError No (none) Global handler for structured operation errors on this SDK instance

Operations

collectPayment

Initiate a payment collection. Returns a PaymentInstance with event-driven updates.

const payment = await nylonpay.collectPayment({
  amount: 10000,
  currency: "UGX",
  customer: { name: "Jane", phoneNumber: "+256700000000" },
  description: "Order #1234",
  method: "mobileMoney",
  reference: "550e8400-e29b-41d4-a716-446655440000",
});

payment.on("success", ({ transaction }) => { /* ... */ });
payment.on("failed", ({ error }) => { /* ... */ });

reference is optional and auto-generated if omitted. A supplied reference must be a valid UUID (any version); the SDK throws a validation error otherwise. Omit the field to auto-generate a UUID v4.

collectPaymentAndResolve

Block until the collection reaches a terminal state. Single request and response; the server resolves the payment and returns the final state.

const result = await nylonpay.collectPaymentAndResolve({
  amount: 5000,
  currency: "UGX",
  customer: { name: "Jane", phoneNumber: "+256700000000" },
  description: "Quick payment",
});

if (result.isOk) console.log("Paid:", result.value.reference);

makePayout

Send funds to a destination mobile money wallet or bank account. destination.phone is required on every payout.

const payout = await nylonpay.makePayout({
  amount: 50000,
  currency: "UGX",
  customer: { name: "Jane", phoneNumber: "+256700000000" },
  destination: {
    accountHolderName: "Jane Doe",
    accountNumber: "123456",
    phone: "+256700000000",
  },
  description: "Refund for order #1234",
});

const tx = await payout.wait();

makePayoutAndResolve

Block until the payout reaches a terminal state. Single request and response.

const result = await nylonpay.makePayoutAndResolve({
  amount: 50000,
  currency: "UGX",
  customer: { name: "Jane", phoneNumber: "+256700000000" },
  destination: {
    accountHolderName: "Jane Doe",
    accountNumber: "123456",
    phone: "+256700000000",
  },
  description: "Refund",
});

Sandbox options

With a sandbox key, collectPayment, collectPaymentAndResolve, makePayout and makePayoutAndResolve accept two test-only options:

  • testOutcome: "success" | "fail" (or a failure code) forces the result.
  • prompt: "pin" holds the payment until the merchant types the customer's 5-digit PIN, shown on the dashboard phone simulator. The wait is up to about 2 minutes with 3 tries. The PIN is one per merchant and works with every sandbox key of that merchant.

If both are set, testOutcome wins. With neither, the sandbox decides on its own. A live key rejects both. The AndResolve variants can wait about 2 minutes with prompt, so raise maxPollDurationMs above that if you cap polling.

Payout Lifecycle

makePayout returns immediately with a reference for tracking and idempotent retries. The payout status flows through several stages:

  • pending, Payout accepted and queued for processing
  • processing, Payout is being processed
  • on_hold, Payout waiting on review. Polling continues and it finishes on its own as successful, failed, or cancelled. From a KES, TZS, RWF, or CDF wallet it also waits for you to confirm the exchange rate on the dashboard; with no answer within 24 hours it ends cancelled and the money returns to your wallet.
  • successful, Payout completed; funds sent to destination
  • failed, Payout failed; funds refunded to merchant account
  • cancelled, Payout was cancelled by the merchant

Polling and webhooks: Monitor payout progress by:

  1. Subscribing to "processing" events (covers pending, processing, and on_hold states)
  2. Listening for terminal events: "success", "failed", "cancelled"
  3. Receiving webhook notifications at your configured endpoint

The SDK keeps polling while a payout is on_hold and stops when it reaches a final state. statusText carries the review sentence, for example "This payout is being reviewed and will complete shortly."

const payout = await nylonpay.makePayout({ /* ... */ });

payout.on("processing", ({ reference, transaction }) => {
  // Payout is in flight (pending, processing, or on_hold)
  if (transaction?.status === "on_hold") {
    console.log("Payout under review:", transaction.statusText);
    // "This payout is being reviewed and will complete shortly"
  }
});

payout.on("success", ({ transaction }) => {
  console.log("Payout complete:", transaction?.reference);
});

// Or wait for terminal state
const tx = await payout.wait();
if (tx) {
  console.log("Final status:", tx.status);
}

getStatus

One-shot status check for a transaction. Returns the current server-side state in a single call.

const result = await nylonpay.getStatus({ reference: "550e8400-e29b-41d4-a716-446655440000" });
if (result.isOk) console.log(result.value.status);

getTransaction

Look up a full transaction record by id or reference. At least one must be provided.

const result = await nylonpay.getTransaction({ reference: "550e8400-e29b-41d4-a716-446655440000" });
if (result.isOk) console.log(result.value.failureReason);

verifyPhone

Pre-validate a phone number and get the registered name.

const result = await nylonpay.verifyPhone({ phoneNumber: "+256700000000" });
if (result.isOk && result.value.verified) {
  console.log("Registered to:", result.value.customerName);
}

Kenya, Tanzania, Rwanda, and DR Congo numbers work too: pass them with the country code (+254710000000). A local number starting with 0 is treated as Uganda.

createInvoice

Generate a hosted payment link. Card payments run through this hosted flow. Invoices require an approved Level 2 account in every currency.

const result = await nylonpay.createInvoice({
  amount: 25000,
  currency: "UGX",
  customerEmail: "customer@example.com",
  description: "Monthly subscription",
  items: [{ name: "Pro Plan", quantity: 1, unitPrice: 25000 }],
});

if (result.isOk) sendEmail(result.value.paymentLink);

verifyWebhookSignature

Verify incoming webhook payloads before processing.

app.post("/webhooks", (req, res) => {
  const isValid = nylonpay.verifyWebhookSignature({
    payload: req.rawBody,
    signature: req.headers["x-nylon-signature"],
    secret: "nps_...",
  });

  if (!isValid) return res.status(401).send("Invalid signature");
});

PaymentInstance Events

collectPayment and makePayout return a PaymentInstance with event-driven updates. The SDK automatically polls for status changes and emits events as the transaction progresses.

Event Description
processing Transaction is being processed (covers pending, processing, and on_hold states)
success Transaction completed successfully
failed Transaction failed
cancelled Transaction was cancelled
error Network or polling error

A payout on on_hold is waiting on review and completes on its own. From a KES, TZS, RWF, or CDF wallet it also waits for you to confirm the exchange rate on the dashboard; with no answer within 24 hours it ends cancelled and the money returns to your wallet. Polling continues automatically; read transaction?.statusText for the review sentence.

payment.on("success", ({ transaction }) => { /* ... */ });
payment.once("success", ({ transaction }) => { /* fires once */ });
payment.off("success", handler);

const tx = await payment.wait();

Delayed payments (v1.5+): By default wait() keeps polling until the payment finishes. After about three minutes in flight, status responses include delayed: true. To hand control back early and rely on webhooks instead:

const nylonpay = createNylonPay({
  apiKey: "npk_...",
  apiSecret: "nps_...",
  onDelayed: "return",
});

const tx = await nylonpay.collectPaymentAndResolve({ /* ... */ });
if (tx.isOk && tx.value.delayed && tx.value.status === "pending") {
  // Payment still in flight, listen for webhooks
}

Set maxPollDurationMs when you want to bound the total wait time.

Use safeTry from slang-ts to handle the promise without try/catch:

import { safeTry } from "slang-ts";

const result = await safeTry(() => payment.wait());
if (result.isOk) {
  console.log("paid:", result.value.reference);
} else {
  console.log("failed or timed out:", result.error);
}

Error Handling

All operations return Result<T, string> from slang-ts. Use parseError to get structured error objects.

import { parseError } from "@nile-squad/nylonpay-ts";

const result = await nylonpay.getStatus({ reference: "550e8400-e29b-41d4-a716-446655440000" });
if (!result.isOk) {
  const error = parseError(result.error);
  if (error.retryable) {
    // retry
  }
}

Supported Currencies

USD, EUR, GBP, KES, UGX, TZS, RWF, CDF

Links

License

MIT

About

NylonPay Typescript SDK

Resources

Stars

5 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages