Code examples
Python
Minimal FastAPI server that creates orders and verifies webhooks
A minimal FastAPI server with two routes: one creates an order and redirects the payer, the other receives webhooks and verifies the X-Signature header.
pip install fastapi uvicorn requestsimport hashlib
import hmac
import os
import requests
from fastapi import FastAPI, HTTPException, Request
from fastapi.responses import JSONResponse, RedirectResponse
API_URL = "https://api.riopay.online/v1"
API_TOKEN = os.environ["API_TOKEN"] # issued by your manager
app = FastAPI(title="Payments example")
# 1. Create an order and redirect the payer to the payment page
@app.post("/create-order")
def create_order(amount: float, order_id: str, user_id: str | None = None):
payload = {
"amount": str(amount),
"externalId": order_id,
"externalUserId": user_id,
"purpose": f"Payment for order {order_id}",
"successUrl": "https://your-site.com/payment-success",
"failUrl": "https://your-site.com/payment-failed",
"callbackUrl": "https://your-site.com/webhooks/payments",
}
headers = {"X-Api-Token": API_TOKEN, "Content-Type": "application/json"}
response = requests.post(f"{API_URL}/orders", json=payload, headers=headers, timeout=30)
if response.status_code != 200:
raise HTTPException(status_code=response.status_code, detail=response.text)
order = response.json()
# Store order["id"] next to your own order_id to match webhooks later
return RedirectResponse(url=order["paymentLink"])
# 2. Receive webhooks
@app.post("/webhooks/payments")
async def webhook(request: Request):
raw_body = await request.body()
signature = request.headers.get("x-signature", "")
expected = hmac.new(API_TOKEN.encode(), raw_body, hashlib.sha512).hexdigest()
if not hmac.compare_digest(expected, signature):
raise HTTPException(status_code=403, detail="Invalid signature")
data = await request.json()
status, external_id = data.get("status"), data.get("externalId")
# Webhooks may arrive more than once: make this idempotent
if status == "COMPLETED":
print(f"Order {external_id} paid")
# mark the order as paid in your database
elif status in {"REFUND", "CHARGEBACK"}:
print(f"Order {external_id} reversed: {status}")
# the money went back to the payer: mark the order as refunded
elif status in {"FAILED", "CANCELED", "EXPIRED", "BLOCKED"}:
print(f"Order {external_id} finished with {status}")
# may still become COMPLETED later if the bank confirms the payment
return JSONResponse(content={"message": "OK"})Run it with:
uvicorn main:app --port 3000What to adapt
- Replace the hard-coded URLs with your own success, fail and webhook addresses.
- Persist the
idfrom the order response together with yourexternalId. - Move status handling into your order model; only
COMPLETEDmeans the money arrived.