JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 50
  • Score
    100M100P100Q57543F
  • License ISC

Ariary Payment SDK (ariari.mg) — Accept mobile money payments in Madagascar. Create payment links, manage products, track payment status, handle webhooks. Supports MVola, Orange Money, Airtel Money. Multi-instance, TypeScript-first.

Package Exports

  • @ariary/pay

Readme

@ariary/pay

Payment SDK for Madagascar mobile money via the Ariari API.

Server-side only — your secret must never be exposed in client-side code.

Install

yarn add @ariary/pay

Get your credentials

  1. Sign in at ariari.mg
  2. Create a project
  3. You'll receive a secret

Setup

import Ariari from '@ariary/pay'

Ariari.config({
  secret: 'your-secret',
})

How it works

  1. Your server creates a payment with an amount (in Ariary)
  2. You redirect the end-user to the returned payment.url — a hosted page on ariari.mg
  3. The end-user sends money via mobile money to Ariary numbers and receives ticket codes
  4. The end-user enters their ticket codes on the payment page (or you collect them and call .pay() from your backend)
  5. A payment can be paid in multiple steps — each ticket code applied is a partial payment
  6. Once fully paid, your hooks are called and/or the user is redirected

Create a Payment

Ariari.create() accepts the following fields:

Field Type Required Description
amount number Yes (without productId) Payment amount in Ariary
productId string No Product ID — resolves amount, name, imgUrl automatically
name string No Payment label shown on the payment page
imgUrl string No Image URL shown on the payment page
hooks.redirectSuccess string No URL the user is redirected to on success
hooks.redirectFailure string No URL the user is redirected to on failure
hooks.silentSuccess string No Webhook called when payment is fully completed
hooks.silentProgress string No Webhook called on each partial payment
hooks.silentFailure string No Webhook called on payment failure
// with amount only
const payment = await Ariari.create({ amount: 5000 })
console.log(payment.url) // https://pay.ariari.mg/abc123encrypted

// with product (amount, name, imgUrl resolved automatically)
const payment = await Ariari.create({ productId: product.id })

// with product + override amount
const payment = await Ariari.create({ productId: product.id, amount: 3000 })

// with hooks
const payment = await Ariari.create({
  amount: 10000,
  name: 'T-shirt bleu',
  imgUrl: 'https://example.com/img.png',
  hooks: {
    redirectSuccess: 'https://yoursite.com/payment/success',
    redirectFailure: 'https://yoursite.com/payment/failure',
    silentSuccess: 'https://yoursite.com/api/webhook/payment-success',
    silentProgress: 'https://yoursite.com/api/webhook/payment-progress',
    silentFailure: 'https://yoursite.com/api/webhook/payment-failure',
  },
})

Hooks

redirect* hooks are browser-side redirects. After the user completes or fails on the payment page, their browser navigates to the given URL. Best suited when the payment page replaces the current page (same-tab navigation). If you open the payment page in a new tab (target="_blank"), redirect hooks have no effect on the parent page — use silent hooks instead.

silent* hooks are server-to-server webhooks called directly by the Ariari backend. They must be publicly accessible URLs with a valid domain name (e.g. https://yoursite.com/api/webhook). localhost or local IPs will not work — use a tunneling service like ngrok for development.

Both can be used together — e.g. redirect the user and notify your server at the same time.

Track a Payment

Ariari.create() returns a Payment object with two methods:

  • payment.status() — fetches the full payment details from the server (see the response table below)
  • payment.pay(ticketCode) — applies a ticket code from your backend (alternative to the payment page)
const payment = await Ariari.create({ amount: 5000 })

// fetch payment details
const info = await payment.status()
// info.status → the payment's current state (see below)
// info.rest   → remaining amount to pay
// info.parts  → list of partial payments applied so far

// apply a ticket code from your backend
const result = await payment.pay('Y-1234')

The status field

The info.status field (not to be confused with the .status() method) indicates the payment's current state:

Value Description
Status.PENDING Payment created, no ticket code applied yet
Status.INCOMPLETE At least one ticket code applied, remaining amount > 0
Status.PAID Fully paid
Status.FAILED Server-side error (should not happen under normal conditions)
import { Status } from '@ariary/pay'

if (info.status === Status.PAID) { ... }

Retrieve an existing Payment

Store payment.id to retrieve it later with .getPayment(). Calling .status() returns the full payment details:

Field Type Description
id string Payment ID
amount number Total amount in Ariary
rest number Remaining amount to pay
productId string? Linked product ID
name string? Payment label
imgUrl string? Image URL
status Status PENDING | INCOMPLETE | PAID | FAILED
parts PaymentPart[] List of partial payments
createdAt string Creation date
updatedAt string Last update date
url string Payment page URL
const payment = await Ariari.create({ amount: 5000 })
const paymentId = payment.id // save this

// later
const payment = Ariari.getPayment(paymentId)
const info = await payment.status()

Products

A product is a reusable template so you don't have to send the same name, price, and imgUrl every time you create a payment. When you pass a productId to Ariari.create(), the product's data is automatically applied to the payment.

// create
const product = await Ariari.products.create({ name: 'Forfait Premium', price: 5000, imgUrl: 'https://example.com/img.png' })

// list all
const products = await Ariari.products.list()

// update
const updated = await Ariari.products.update(product.id, { name: 'Forfait Gold', price: 8000 })

// delete
await Ariari.products.delete(product.id)

Multiple instances

Use name to manage separate Ariari instances with different credentials. Ariari.config() returns the instance directly.

const shop = Ariari.config({
  name: 'shop',
  secret: 'secret-a',
})

const billing = Ariari.config({
  name: 'billing',
  secret: 'secret-b',
})

await shop.create({ amount: 5000 })
await billing.create({ productId: 'prod-123' })

You can also retrieve a named instance later with Ariari.get().

const shop = Ariari.get('shop')

Without name, the instance defaults to "main" and is used by static methods (Ariari.create, Ariari.products, Ariari.getPayment).