API reference
Orders API
Follow an order from payment to delivery, and refund a paid order to take its coins and items back, with the server API.
On this page
An order is the purchase of a pack with real money, created by a checkout. It moves through a few statuses, from pending when the player has not paid yet to fulfilled once the coins and items are delivered. This page reads an order and refunds it. The examples on this page continue from the setup, and use the id of an order that the player has paid.
Get an order
Returns an order with its current status. Use it when the player comes back from the payment page, to confirm that the coins were delivered before you tell them, and to follow an order you created.
/orders/{orderId}Authentication: secret key. Idempotency: not needed.
Path parameters
| Parameter | Type | Description |
|---|---|---|
orderId | string | The id of the order: 24 hexadecimal characters, as returned by Order a currency pack. |
curl "https://gamecoin.apilow.com/api/v1/orders/665f1c2e8a3b4d5e6f7081b4" \
-H "Authorization: Bearer $GAMECOIN_SECRET_KEY"const order = await gamecoin.orders.get("665f1c2e8a3b4d5e6f7081b4");
console.log(order.status, order.amountCents, order.vatCents, order.netCents, order.paidAt);order = gamecoin.orders.get("665f1c2e8a3b4d5e6f7081b4")
print(order.status, order.amount_cents, order.vat_cents, order.net_cents, order.paid_at)$order = $gamecoin->orders->get('665f1c2e8a3b4d5e6f7081b4');
echo $order->status, ' ', $order->amountCents, ' ', $order->vatCents, ' ', $order->netCents, ' ', $order->paidAt?->format(DATE_ATOM), "\n";order, err := client.Orders.Get(ctx, "665f1c2e8a3b4d5e6f7081b4")
check(err)
fmt.Println(order.Status, order.AmountCents, order.VatCents, order.NetCents, order.PaidAt)var order = await gamecoin.Orders.GetAsync("665f1c2e8a3b4d5e6f7081b4");
Console.WriteLine($"{order.Status} {order.AmountCents} {order.VatCents} {order.NetCents} {order.PaidAt:u}");Response 200 OK: an Order. The one below was created by a checkout and paid in the simulated payment page.
{
"id": "665f1c2e8a3b4d5e6f7081b4",
"playerId": "665f1c2e8a3b4d5e6f708192",
"status": "fulfilled",
"pack": { "sku": "gems-500", "name": "Bag of gems" },
"amountCents": 499,
"currency": "EUR",
"country": "BE",
"vatRateBp": 2100,
"vatCents": 87,
"netCents": 412,
"createdAt": "2026-10-05T22:14:32.995Z",
"paidAt": "2026-10-05T22:14:34.054Z",
"fulfilledAt": "2026-10-05T22:14:34.054Z",
"refundedAt": null,
"creatorCode": null
}Errors
| Status | Code | When | What to do |
|---|---|---|---|
| 404 | NOT_FOUND | No order has this id in your game and environment (including an id that is not 24 hexadecimal characters) | Check the id, and that the key is the one of the game that created the order. |
{ "error": { "code": "NOT_FOUND", "message": "Order not found" } }Notes
- The
statusis the truth. Never rely on the?status=of the return URL: read the order. - A new order is
pending. When the payment succeeds it goes tofulfilledwithin moments: the coins and items are delivered,paidAtandfulfilledAtare set.paidis a short step on the way, so you will rarely see it. If the player has not paid yet, poll the order every few seconds, or read it when they come back. - The final statuses are
fulfilled(until a refund),refunded,chargeback,failed(the payment was refused),canceledandexpired(apendingorder that was not paid within one hour). They are described in Objects. amountCentsis the price in euro cents, VAT included;vatRateBpis the rate in basis points (2100 is 21 %);vatCentsandnetCentssplit the amount.- An order is only visible with the key of the game that created it. The browser reads its own orders with Get an order of the token player.
Refund an order
Refunds a paid order: the money goes back to the player, and the coins and items the order delivered are taken back. Use it when a player asks for their money back, or when you cancel a sale.
/orders/{orderId}/refundAuthentication: secret key. Idempotency: none: this call has no Idempotency-Key. Do not blindly retry it: see the notes.
Path parameters
| Parameter | Type | Description |
|---|---|---|
orderId | string | The id of a fulfilled order: 24 hexadecimal characters. |
Body (JSON; optional, an empty body means {})
| Field | Type | Required | Rules |
|---|---|---|---|
reason | string | no | Free text, at most 500 characters, recorded with the refund. |
curl -X POST "https://gamecoin.apilow.com/api/v1/orders/665f1c2e8a3b4d5e6f7081b4/refund" \
-H "Authorization: Bearer $GAMECOIN_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{"reason": "Player request"}'const orderId = "665f1c2e8a3b4d5e6f7081b4"; // the id of a fulfilled order
const refunded = await gamecoin.orders.refund(orderId, { reason: "Player request" });
console.log(refunded.status, refunded.refundedAt.toISOString());order_id = "665f1c2e8a3b4d5e6f7081b4" # the id of a fulfilled order
refunded = gamecoin.orders.refund(order_id, reason="Player request")
print(refunded.status, refunded.refunded_at.isoformat())$orderId = '665f1c2e8a3b4d5e6f7081b4'; // the id of a fulfilled order
$refunded = $gamecoin->orders->refund($orderId, ['reason' => 'Player request']);
echo $refunded->status, ' ', $refunded->refundedAt->format(DATE_ATOM), "\n";orderID := "665f1c2e8a3b4d5e6f7081b4" // the id of a fulfilled order
refunded, err := client.Orders.Refund(ctx, orderID, &gamecoin.RefundParams{Reason: "Player request"})
check(err)
fmt.Println(refunded.Status, *refunded.RefundedAt)var orderId = "665f1c2e8a3b4d5e6f7081b4"; // the id of a fulfilled order
var refunded = await gamecoin.Orders.RefundAsync(orderId, new RefundRequest { Reason = "Player request" });
Console.WriteLine($"{refunded.Status} {refunded.RefundedAt:u}");Response 200 OK: the Order, now refunded, with refundedAt set.
{
"id": "665f1c2e8a3b4d5e6f7081b4",
"playerId": "665f1c2e8a3b4d5e6f708192",
"status": "refunded",
"pack": { "sku": "gems-500", "name": "Bag of gems" },
"amountCents": 499,
"currency": "EUR",
"country": "BE",
"vatRateBp": 2100,
"vatCents": 87,
"netCents": 412,
"createdAt": "2026-10-05T22:14:32.995Z",
"paidAt": "2026-10-05T22:14:34.054Z",
"fulfilledAt": "2026-10-05T22:14:34.054Z",
"refundedAt": "2026-10-05T22:14:40.586Z",
"creatorCode": null
}Errors
| Status | Code | When | What to do |
|---|---|---|---|
| 400 | VALIDATION_FAILED | An unknown field, or a reason longer than 500 characters | Read details.fieldErrors. |
| 402 | PAYMENT_FAILED | The payment provider could not refund | Retry later; the order is unchanged. |
| 404 | NOT_FOUND | No order has this id in your game and environment | Check the id. |
| 409 | CONFLICT | The order is not fulfilled: it is pending, failed, expired, already refunded… (details.from is its status) | Nothing to refund. Read the order to see where it stands. |
{
"error": {
"code": "CONFLICT",
"message": "Order cannot go from pending to refunded",
"details": { "from": "pending", "to": "refunded" }
}
}Notes
- Only a
fulfilledorder can be refunded, and only once. The money is refunded to the player; in the test environment the refund is simulated. - The coins are taken back with a ledger entry of type
refund_clawbackthat points to the order: thepaidandbonusamounts that the pack delivered. The items of the pack are removed from the inventory, up to what the player still owns. - If the player has already spent the coins, the balance goes below zero: the shortfall is kept as a debt on the
paidpart, and the next credits repay it first. A game setting (« Après un remboursement » in the dashboard, which is in French for now) can stop balances at zero instead. A player in debt cannot spend until the debt is repaid. - A refund has no idempotency key. After a network error or a
5xx, do not retry blindly: read the order with Get an order. If it isrefunded, the refund went through; a second refund would answerCONFLICTanyway. The SDKs retry a refund on429only. - A pack with
maxPerPlayercan be bought again after a refund: a refunded order does not count toward the limit.