Package Exports
- @ariary/pay
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
secret
Setup
import Ariari from '@ariary/pay'
Ariari.config({
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',
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).