AstraCloudPayAPI reference
Sign inGet API keys

Accept Fonepay payments with one API call

Create a payment from your server, send your customer to the hosted checkout (or show the QR yourself), and get a signed webhook when the money lands in your Fonepay account.

  1. Create a payment with your API key — you get a checkoutUrl and a QR payload.
  2. Show the checkout to your customer. They scan with any Fonepay-enabled app.
  3. Receive a payment.paid webhook once AstraCloud Pay has verified the payment with Fonepay.

Base URL: https://pay.astracloud.com.np. All amounts are integers in paisa (Rs 1 = 100 paisa), so Rs 500 is 50000. Times are ISO 8601 in UTC.

Authentication

Every server-to-server request carries your secret API key in the X-Api-Key header. Find it in Dashboard → Developers. Keep it on your server — anyone with the key can create payments on your account. If it leaks, rotate it from the same page; the old key stops working immediately.

X-Api-Key: ap_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Create a payment

POST/api/payments
FieldTypeDescription
amountPaisaintegerRequired. Amount in paisa. Maximum 100,000,000 (Rs 10,00,000).
descriptionstringShown to the customer on the checkout. Up to 255 characters.
customerNamestringFor your records and search. Up to 160 characters.
customerPhonestringFor your records and search. Up to 32 characters.
Request
curl -X POST https://pay.astracloud.com.np/api/payments \
  -H "X-Api-Key: $ASTRAPAY_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amountPaisa": 50000,
    "description": "Order #1042 — 2 momo plates",
    "customerName": "Sita Sharma",
    "customerPhone": "9800000000"
  }'
Response · 201 Created
{
  "publicRef": "ORDER-7Q2KX9MB",
  "internalReference": "PRN-4H8K2M9QX7TW3A",
  "qrPayload": "…",
  "amountPaisa": 50000,
  "expiresAt": "2026-09-24T10:05:00.000Z",
  "status": "QR_CREATED",
  "checkoutUrl": "https://pay.astracloud.com.np/pay/?ref=PRN-4H8K2M9QX7TW3A"
}

The customer has 5 minutes to pay. After that the payment becomes EXPIRED and nothing can be charged — create a new one.

Retrieve a payment

GET/api/payments/{internalReference}

Returns the payment with its customer details and the full verification timeline. Only payments on your own account are returned.

Request
curl https://pay.astracloud.com.np/api/payments/PRN-4H8K2M9QX7TW3A -H "X-Api-Key: $ASTRAPAY_KEY"
Response · 200 OK
{
  "internal_reference": "PRN-4H8K2M9QX7TW3A",
  "fonepay_reference": "FP-88213377",
  "status": "PAID",
  "amount_paisa": 50000,
  "public_ref": "ORDER-7Q2KX9MB",
  "description": "Order #1042 — 2 momo plates",
  "customer_name": "Sita Sharma",
  "customer_phone": "9800000000",
  "paid_at": "2026-09-24T10:02:41.000Z",
  "verified_at": "2026-09-24T10:02:41.000Z",
  "timeline": [
    { "action": "create_qr", "ok": 1, "detail": "provider=live", "created_at": "…" },
    { "action": "verify",    "ok": 1, "detail": "provider says PAID", "created_at": "…" }
  ]
}

Checkout page

Redirect or link the customer to checkoutUrl. The hosted page shows your business name, the amount, the QR, a live countdown and the result — on any phone, with no login. It updates by itself when the payment is confirmed.

Prefer your own UI? Render qrPayload as a QR code yourself and poll GET /api/payments/{ref}/status (public, read-only) — but always treat the webhook or an authenticated retrieve as the source of truth before you ship goods.

Payment statuses

StatusMeaningFinal?
QR_CREATEDQR is ready; waiting for the customer to scan.No
PROCESSINGFonepay reported a payment; we’re confirming amount and reference.No
PAIDVerified with Fonepay. The money is in your account. Safe to fulfil.Yes
EXPIREDNot paid within 5 minutes. Nothing was charged.Yes
FAILEDThe payment was declined or couldn’t be verified.Yes

A payment only becomes PAID after backend verification of the amount and Fonepay reference. A screenshot or a client-side redirect never marks a payment paid.

Webhooks

Add an HTTPS endpoint in Dashboard → Developers. When a payment is verified we send a POST with a JSON body. Respond with any 2xx within 8 seconds. Each payment is settled once, so you receive payment.paid once per payment — still, make your handler idempotent on data.reference.

payment.paid
POST /webhooks/astrapay
X-AstraPay-Event: payment.paid
X-AstraPay-Signature: t=1790244161,v1=5f2c…e91a

{
  "id": "evt_9b1c0f4a2e7d3b6c8a10",
  "type": "payment.paid",
  "created": "2026-09-24T10:02:41.512Z",
  "data": {
    "reference": "PRN-4H8K2M9QX7TW3A",
    "order": "ORDER-7Q2KX9MB",
    "amountPaisa": 50000,
    "currency": "NPR",
    "description": "Order #1042 — 2 momo plates",
    "customer": { "name": "Sita Sharma", "phone": "9800000000" },
    "fonepayReference": "FP-88213377",
    "paidAt": "2026-09-24T10:02:41.000Z"
  }
}

Use Send test event on the Developers page to receive a webhook.test event. Every delivery, with its HTTP result, is listed there.

Verify signatures

The signature is HMAC-SHA256(secret, "<t>.<raw body>") in hex, where secret is your webhook signing secret (whsec_…). Compute it over the raw request body, compare in constant time, and reject timestamps older than 5 minutes.

Node.js (Express)
import crypto from 'node:crypto';
import express from 'express';

app.post('/webhooks/astrapay', express.raw({ type: 'application/json' }), (req, res) => {
  const parts = Object.fromEntries(req.get('x-astrapay-signature').split(',').map(p => p.split('=')));
  const expected = crypto.createHmac('sha256', process.env.ASTRAPAY_WEBHOOK_SECRET)
    .update(`${parts.t}.${req.body}`).digest('hex');
  const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
  const valid = expected.length === parts.v1.length &&
    crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
  if (!fresh || !valid) return res.sendStatus(400);

  const event = JSON.parse(req.body);
  if (event.type === 'payment.paid') {
    // look up event.data.reference and mark the order paid (idempotently)
  }
  res.sendStatus(200);
});
PHP
$body = file_get_contents('php://input');
parse_str(str_replace(',', '&', $_SERVER['HTTP_X_ASTRAPAY_SIGNATURE']), $sig);
$expected = hash_hmac('sha256', $sig['t'] . '.' . $body, getenv('ASTRAPAY_WEBHOOK_SECRET'));
if (abs(time() - (int)$sig['t']) > 300 || !hash_equals($expected, $sig['v1'])) {
    http_response_code(400); exit;
}
$event = json_decode($body, true);

Errors

Errors return JSON with a single readable error message.

CodeWhen
400A field is missing or invalid — the message names the field.
401Missing or wrong X-Api-Key.
404The payment doesn’t exist on your account.
429Too many sign-in attempts — wait and retry.
500Something failed on our side. Safe to retry creating a payment.
{ "error": "amountPaisa: Number must be greater than 0" }

Sandbox testing

Until your account is connected to a live Fonepay gateway, payments run in sandbox: QR codes can’t be paid with a real app, and no money moves. Simulate a successful scan to exercise your whole flow — status changes, wallet credit and the payment.paid webhook all behave exactly as in live mode.

POST/api/sandbox/mark-paid/{internalReference}
curl -X POST https://pay.astracloud.com.np/api/sandbox/mark-paid/PRN-4H8K2M9QX7TW3A

You can also click Simulate payment in a payment’s details in the dashboard. This endpoint doesn’t exist in live mode.