@ariary/pay
v2.0.2
Published
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.
Downloads
1,161
Keywords
Readme
@ariary/pay
Payment SDK for Madagascar mobile money via the Ariari API.
Server-side only — your
secretmust never be exposed in client-side code.
Install
yarn add @ariary/payGet your credentials
- Sign in at ariari.mg
- Create a project
- You'll receive a
projectIdand asecret
Setup
import Ariari from '@ariary/pay'
Ariari.config({
projectId: 'your-project-id',
secret: 'your-secret',
})How it works
- Your server creates a payment with an amount (in Ariary)
- You redirect the end-user to the returned
payment.url— a hosted page on ariari.mg - The end-user sends money via mobile money to Ariary numbers and receives ticket codes
- The end-user enters their ticket codes on the payment page (or you collect them and call
.pay()from your backend) - A payment can be paid in multiple steps — each ticket code applied is a partial payment
- 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',
projectId: 'project-a',
secret: 'secret-a',
})
const billing = Ariari.config({
name: 'billing',
projectId: 'project-b',
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).
