Payment Providers

Mayar.id integration

Invoice-based payments with a hosted checkout link and the payment.received webhook.

Configuration

Create a Read & Write API key at web.mayar.id/api-keys and set:

MAYAR_API_KEY=your-api-key
MAYAR_WEBHOOK_TOKEN=your-webhook-token   # optional
MAYAR_SANDBOX=false                       # true to use the sandbox host
MAYAR_API_BASE=                           # optional override
EnvironmentBase URL
Productionhttps://api.mayar.id/hl/v2
Sandboxhttps://api.mayar.io/hl/v2
The environment belongs to your API key, not to your app, so it is not derived from NODE_ENV. PGLink uses the production host by default and the sandbox host only when MAYAR_SANDBOX=true. A production key sent to the sandbox host (or the other way around) is rejected with 401 Unauthorized. Set MAYAR_API_BASE to override the host entirely.

Creating a payment

PGLink maps POST /payments (and /payments/payment-link) to Mayar's POST /invoices/create. Mayar requires a customer name, email and mobile number.

curl -X POST http://localhost:3000/payments \
  -H "X-API-Key: your-app-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 50000,
    "provider": "MAYAR",
    "applicationOrderId": "ORDER-12345",
    "customerName": "Budi Santoso",
    "customerEmail": "budi@example.com",
    "customerPhone": "081234567890",
    "mayarPaymentMethod": "qris",
    "mayarExpiredAt": "2026-12-31T23:59:59.000Z",
    "description": "Order #12345"
  }'

Response (abridged):

{
  "data": {
    "externalId": "a1b2c3d4-...",
    "checkoutUrl": "https://testingmayar.myr.id/invoices/ycfyxbj2h3",
    "status": "PENDING",
    "provider": "MAYAR"
  },
  "checkoutUrl": "https://testingmayar.myr.id/invoices/ycfyxbj2h3"
}

Redirect the customer to checkoutUrl to complete payment.

Payment channels

Set mayarPaymentMethod to restrict the invoice to one channel (it must be enabled in your Mayar settings):

CategoryValues
QRISqris
Virtual accountva/bni, va/bri, va/mandiri, va/cimb, va/permata, va/bjb, va/bsi
E-walletewallet/dana, ewallet/gopay, ewallet/linkaja, ewallet/shopeepay, ewallet/jenius
Retail outletoutlet/alfamart

When omitted, Mayar shows its configured checkout page with all enabled channels.

Webhooks

Register your callback URL in the Mayar dashboard (Integration โ†’ Webhook). Mayar posts events such as:

EventMapped statusMeaning
payment.receivedSUCCESSThe customer paid
payment.reminderPENDINGInformational: the invoice is unpaid after 29 minutes. The payment is not changed.
payment.failedFAILEDThe payment failed
payment.expiredEXPIREDThe invoice expired

Mayar sends the event to POST /payments/mayar-callback:

{
  "event": "payment.received",
  "data": {
    "id": "6f8c19ff-...",
    "transactionId": "a1b2c3d4-...",
    "status": "SUCCESS",
    "transactionStatus": "paid"
  }
}

PGLink matches the payment by data.transactionId (falling back to data.id) and forwards a normalized webhook to your application's webhookUrl.

data.status is the status of the webhook delivery โ€” Mayar reports SUCCESS there even for an invoice that has not been paid. PGLink therefore derives the payment status from the event name and from data.transactionStatus, never from data.status. An unknown event (for example membership.* or shipper.status) never marks a payment as paid.

If MAYAR_WEBHOOK_TOKEN is set it must be presented. Mayar sends no signature and no custom header, so register the webhook URL as https://your-host/payments/mayar-callback?token=YOUR_TOKEN โ€” the token is read from the token query parameter (the x-mayar-token header is also accepted).

Limitations