MagicPayments Integration Guides

Madagascar P2P Gate

Pay-inPay-outMGA

Accept mobile-money pay-ins on a MagicPayments-hosted page and send mobile-money payouts in Malagasy Ariary (MGA).

When to use it

The Madagascar P2P Gate accepts mobile-money pay-ins (MVola and other Malagasy operators) on a MagicPayments-hosted page, and sends mobile-money payouts to a phone number. All amounts are in MGA (Malagasy Ariary, 2 decimal places — so 5000000 means 50,000.00 MGA).

OperationRailHow the integration looks
Pay-inMobile money (P2P)Create an invoice, redirect the payer to the hosted page.
Pay-outMobile money (phone)Server-to-server call to the phone payout endpoint.
Prerequisites

You need your signing credentials from Getting started, a pay-in gate_id for Madagascar mobile money, and a pay-out cascade_id enabled for MGA phone payouts. Your account manager provides both.

Pay-in — hosted P2P page

The payer never touches your servers with their payment details. You create an invoice; we host the page.

  1. Create the invoice Call the gate endpoint with your gate_id and the amount in MGA minor units.
  2. Redirect the payer Send the customer's browser to the invoice_url we return. They complete the MVola flow on our page.
  3. Receive the result We call your callback_url on every status change and you can poll invoice status as a fallback.
POST/api/invoice
Request body
{
  "gate_id": "mg-mobile-money-gate",
  "invoice": {
    "invoice_id": "order-mg-2026-000123",
    "currency": "MGA",
    "amount": 5000000,
    "description": "Wallet top-up",
    "ttl_minutes": 30
  },
  "customer": {
    "id": "cust-77120",
    "full_name": "Rakoto Andrianina",
    "phone_number": "+261340000000"
  },
  "workflow_hooks": {
    "callback_url": "https://merchant.example.com/mp/callbacks",
    "return_success_url": "https://merchant.example.com/orders/123/done",
    "return_decline_url": "https://merchant.example.com/orders/123/retry"
  }
}
Python
resp = signed_post("/api/invoice", {
    "gate_id": "mg-mobile-money-gate",
    "invoice": {
        "invoice_id": "order-mg-2026-000123",
        "currency": "MGA",
        "amount": 5_000_000,          # 50,000.00 MGA
        "description": "Wallet top-up",
        "ttl_minutes": 30,
    },
    "customer": {
        "id": "cust-77120",
        "full_name": "Rakoto Andrianina",
        "phone_number": "+261340000000",
    },
    "workflow_hooks": {
        "callback_url": "https://merchant.example.com/mp/callbacks",
        "return_success_url": "https://merchant.example.com/orders/123/done",
        "return_decline_url": "https://merchant.example.com/orders/123/retry",
    },
})
invoice = resp.json()
redirect_url = invoice["invoice_url"]   # send the payer here
Response
{
  "request_id": "0f0b1d2e-...",
  "request_status": "success",
  "invoice_id": "62051f64-9a79-4edb-8a9e-95c86c55ee4e",
  "merchant_invoice_id": "order-mg-2026-000123",
  "invoice_status": "unpaid",
  "invoice_url": "https://stage.example-mp.com/public/invoice/62051f64-.../gate",
  "message": "Invoice (Gate PayIn) with internal uid=62051f64-... has been created."
}
Skip the JSON round-trip

If you'd rather have us redirect straight to the hosted page from the create call, set "redirect_to_gate_payment_page": true at the top level of the request body and follow the 303 redirect instead of reading invoice_url.

Tracking the pay-in

Look up the invoice by your own id (or our invoice_uid). It reaches paid on success.

POST/api/invoice/status
resp = signed_post("/api/invoice/status", {
    "merchant_invoice_id": "order-mg-2026-000123"
})
invoice = resp.json()
# invoice["status"] -> "paid" | "payment_failed" | "expired" | "canceled" | ...
# invoice["actual_payment"]["processing_info"]["amount_acquired"] -> settled MGA minor units

Pay-out — mobile money to a phone

Payouts go server-to-server. For Madagascar mobile money the destination is a phone number plus the operator. Use the phone payout endpoint with a cascade_id enabled for MGA payouts.

POST/api/payment/payout/phone
Request body
{
  "cascade_id": "mg-mobile-payout",
  "payment": {
    "payment_id": "payout-mg-2026-000045",
    "currency": "MGA",
    "amount": 3000000,
    "description": "Affiliate payout"
  },
  "phone": {
    "provider": "mvola",
    "phone_number": "+261340000000"
  },
  "customer": {
    "id": "partner-2391",
    "full_name": "Rasoa Hery"
  },
  "workflow_hooks": {
    "callback_url": "https://merchant.example.com/mp/callbacks"
  }
}

The phone.provider names the Malagasy operator. Supported here:

Operatorprovider
MVola (Telma)mvola
Orange Moneyorange
Airtel Moneyairtel
Python
resp = signed_post("/api/payment/payout/phone", {
    "cascade_id": "mg-mobile-payout",
    "payment": {
        "payment_id": "payout-mg-2026-000045",
        "currency": "MGA",
        "amount": 3_000_000,
        "description": "Affiliate payout",
    },
    "phone": {"provider": "mvola", "phone_number": "+261340000000"},
    "customer": {"id": "partner-2391", "full_name": "Rasoa Hery"},
    "workflow_hooks": {"callback_url": "https://merchant.example.com/mp/callbacks"},
})
payout = resp.json()
payout_uid = payout["payment_id"]   # our uid; status starts at "initiated"

Tracking the pay-out

Payouts use the payment status endpoint (not the invoice one).

POST/api/payment/status
resp = signed_post("/api/payment/status", {
    "merchant_payment_id": "payout-mg-2026-000045"
})
# resp.json()["status"] -> "success" | "processing" | "decline" | "error"
Phone number format

Send the destination in international format (+261…). A mismatch between the operator and the number's prefix is the most common cause of a decline on payout.

Testing & go-live