Accept payments
Customers pay through a hosted page that Olympus Pay runs for you, so card and bank details never touch your servers. You create a payment link or a QR code, send the customer to it, and follow the payment with the API and webhooks.
The lifecycle of a payment#
pending -> processing -> succeeded -> partially_refunded -> refunded
| |
| +--------> failed
+-----------------------> cancelled| Status | Meaning |
|---|---|
pending | Created, the customer has not completed checkout |
processing | Submitted to the payment provider, awaiting confirmation |
succeeded | Paid in full, nothing refunded |
partially_refunded | Paid, and part has been refunded |
refunded | Paid, and all of it has been refunded |
failed | Declined or abandoned after an attempt, no money collected |
cancelled | You cancelled it while it was still pending |
GET /reference returns this list, always current, with no authentication.
Create a payment link#
curl https://merchant.olympuspay.co/api/v1/payment-links \
-X POST \
-H "Authorization: Bearer $OLYMPUS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Order 1001",
"description": "Two nights, deluxe room",
"amount": 250.00,
"currency": "BWP",
"returnUrl": "https://shop.example.com/orders/1001",
"idempotencyKey": "order-1001"
}'
const res = await fetch('https://merchant.olympuspay.co/api/v1/payment-links', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.OLYMPUS_API_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({
title: 'Order 1001',
description: 'Two nights, deluxe room',
amount: 250.0,
currency: 'BWP',
returnUrl: 'https://shop.example.com/orders/1001',
idempotencyKey: 'order-1001',
}),
})
const { id, payUrl } = await res.json()
import os, requests
res = requests.post(
"https://merchant.olympuspay.co/api/v1/payment-links",
headers={"Authorization": f"Bearer {os.environ['OLYMPUS_API_KEY']}"},
json={
"title": "Order 1001",
"description": "Two nights, deluxe room",
"amount": 250.00,
"currency": "BWP",
"returnUrl": "https://shop.example.com/orders/1001",
"idempotencyKey": "order-1001",
},
timeout=30,
)
link = res.json()Omit amount to let the customer type one. Use expiresAt (ISO 8601) to make a link stop working at a time you choose. If the key has a redirect-domain allowlist, returnUrl must be on it. After payment the customer is sent to returnUrl with ?payment_id=...&status=success (or status=cancelled) appended.
The response is 201 with the link's id and the payUrl to give the customer.
Create a QR code#
For in-person payments, create a dynamic QR code and show it on a screen. Note that amount here is in minor units.
curl https://merchant.olympuspay.co/api/v1/qr-codes \
-X POST \
-H "Authorization: Bearer $OLYMPUS_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "mode": "fixed_amount", "amount": 25000, "currency": "BWP", "singleUse": true }'mode is fixed_amount (needs amount) or open_amount (the payer chooses). A singleUse code stops accepting payments once it has been paid.
Retrieve payments#
curl https://merchant.olympuspay.co/api/v1/payments/PAYMENT_ID \
-H "Authorization: Bearer $OLYMPUS_API_KEY"{
"id": "6f0c1a52-3f3a-4b6e-9b0e-6d0a1c2f9a11",
"method": "hosted_checkout",
"amount": 25000,
"currency": "BWP",
"fee_amount": 750,
"net_amount": 24250,
"refunded_amount": 0,
"status": "succeeded",
"payment_link_id": "b3a4e1c8-7d52-4a53-8c0b-2f1a9d44e8aa",
"created_at": "2026-09-26T10:15:00.000Z"
}processor and the other provider fields are opaque strings: do not build logic on their values. Prefer webhooks to polling. To list payments see Pagination.
Cancel a payment#
POST /payments/{id}/cancel (scope payments:cancel) works only while the payment is pending. A payment already sent to the provider cannot be cancelled: refund it once it succeeds.
Amounts#
The API uses both units. Check each field.
| Where | Field | Unit |
|---|---|---|
| Payment link, create | amount, presetAmount | Major units (250.00) |
| QR code, create | amount | Minor units (25000) |
| Payment object | amount, fee_amount, net_amount, refunded_amount | Minor units |
| Refund, request | amount | Minor units |
| Payout, request | amount | Major units |
Webhook data | amounts | Minor units |
Minor units are one hundredth of the currency unit for BWP, USD, GBP and ZAR.