Online payment (YooKassa)
A player tops up the wallet by card or other methods on the YooKassa payment page; the wallet pays for bookings, rentals and court shares. YooKassa is a Russian payment provider; payments are in Russian roubles.
With a connected shop Online payment is switched on once the club has connected a YooKassa shop and the shop keys are set in the cloud. Until then the top-up answers 503 payments_provider_not_configured. Payment methods on the YooKassa page follow the shop settings in the YooKassa dashboard.
Top-up flow
- An app or website creates a payment on behalf of the player:
POST /v1/wallet/topup. The cloud creates its own payment record (pending), creates a YooKassa payment and returnspaymentIdandconfirmationUrl. - The player opens
confirmationUrland pays on the YooKassa page. - YooKassa sends a notification to
POST /v1/wallet/yookassa/webhook. The cloud re-requests the payment status from YooKassa and, if it is paid and the amount matches, credits the wallet. - The player returns from the payment page; the app asks
GET /v1/wallet/payments/{payment_id}. If the notification has not arrived yet, the cloud re-requests the status from YooKassa itself — the payment is not lost.
Creating a payment
curl -X POST "https://your-server/api/v1/wallet/topup" \
-H "Authorization: Bearer <player token>" \
-H "Content-Type: application/json" \
-d '{"amount": 5000}'
# → {"ok": true, "status": "pending",
# "paymentId": "e4a19c7d-6b3f-4c21-8d5e-0f2a7b9c1d36",
# "confirmationUrl": "https://yoomoney.ru/checkout/payments/v2/contract?orderId=…"}- The amount is whole roubles within the cloud limits (100 to 100,000 by default); outside them —
400with{"error": "bad_amount", "min": …, "max": …}. - The YooKassa payment is created with immediate capture and returns the player to the URL configured in the cloud. The YooKassa idempotence key is the cloud
paymentId. - YooKassa unavailable or did not create the payment —
502 payment_create_failed; nothing is charged, the request can be retried.
YooKassa notification
In the YooKassa dashboard set the notification URL to https://your-server/api/v1/wallet/yookassa/webhook and the event to a successful payment (and, optionally, cancellation).
The notification body is never trusted. YooKassa notifications are not signed, so the cloud takes only object.id from the body and re-requests the status and amount from YooKassa with the shop key, checking them against its own record. A forged notification credits nothing.
- Money is credited exactly once: the payment record is locked during the check, the pending → succeeded transition happens once, repeated notifications change nothing.
- If the YooKassa amount differs from the cloud record, nothing is credited.
- A notification about an unknown payment —
200with{"ok": true, "ignored": true}, so YooKassa stops resending it. Noobject.id—400 no_payment_id.
Payment status
curl "https://your-server/api/v1/wallet/payments/e4a19c7d-6b3f-4c21-8d5e-0f2a7b9c1d36" \
-H "Authorization: Bearer <player token>"
# → {"paymentId": "e4a19c7d-…", "status": "succeeded", "amount": 5000, "balance": 9200}| Status | Meaning |
|---|---|
pending | Waiting for payment or for YooKassa. Ask again in a few seconds. |
succeeded | Paid, the money is in the wallet; balance is the new balance. |
canceled | Cancelled in YooKassa, nothing credited. |
A payment is visible only to the player who created it; someone else's — 404 payment_not_found.
Where wallet money goes
Balance and the last 30 operations — GET /v1/wallet. The wallet pays for bookings (POST /v1/bookings), rentals (POST /v1/bookings/{booking_id}/rental) and court shares (POST /v1/bookings/splits/{split_id}/accept); booking cancellation refunds come back to it. Payments taken at the club and posted in 1C are also credited to the wallet — see 1C payment import.