Online charges for an order: create a payment intent, read it, and run the
interactive two-step Sypago (Débito Inmediato) flow. Provider confirmation webhooks
land here too. Paths are relative to the /api/v1 prefix; all routes require
Authorization: Bearer {token} except the public provider webhook.
Manual, form-based payments (bank transfer, Pago MĂłvil, etc.) live under Payment forms. The catalog of selectable methods is Payment methods. See also the order lifecycle.
See Common errors.
| Method | URI | Role | Description |
|---|---|---|---|
POST |
/payments/webhook/{provider} |
Public | Provider confirmation webhook (HMAC-verified) |
POST |
/orders/{orderId}/payment |
any | Create/refresh the order's payment intent |
GET |
/orders/{orderId}/payment |
any | Read the order's payment |
POST |
/orders/{orderId}/payment/prepare |
any | Sypago step 1 — trigger the bank OTP |
POST |
/orders/{orderId}/payment/report |
any | Sypago step 2 — submit the OTP |
The payment key is the payment record. Money is exposed as *_usd (decimal)
alongside *_cents. expires_at is the payment window deadline.
{
"payment": {
"id": 9,
"order_id": 42,
"user_id": 1,
"method": "SYPAGO_DI",
"provider": "SYPAGO",
"payment_form_id": null,
"internal_type": null,
"status": "PENDING",
"amount_cents": 2020,
"amount_bs_cents": null,
"bcv_rate_cents": null,
"reference": null,
"provider_ref": null,
"receipt_url": null,
"reported_at": null,
"expires_at": "2026-06-26T12:15:00.000000Z",
"confirmed_at": null,
"currency": "USD",
"created_at": "2026-06-26T12:00:00.000000Z",
"updated_at": "2026-06-26T12:00:00.000000Z",
"amount_usd": 20.2,
"aguita_revenue_usd": 3.6,
"solidary_fund_usd": 0.36
}
}
Public endpoint that providers call to confirm a charge. There is no session auth;
the {provider} path segment (e.g. sypago) selects the provider. The webhook
accepts a provider-defined body.
Response 204 No Content on a verified, applied event.
Errors
| Status | Body | When |
|---|---|---|
400 |
{ "error": "Provider no soportado: {provider}" } |
Unknown provider segment |
500 |
{ "error": "raw body no disponible" } |
Empty raw body |
400 |
{ "error": "Webhook inválido.", "message": "…" } |
Signature/parse failure |
Creates (or refreshes) the payment intent for the caller's order. method must
resolve to a known method; to change the order's method use
PATCH /orders/{orderId}/payment-method.
Request body
| Field | Type | Required | Rules |
|---|---|---|---|
method |
string | yes | a payment method code (must resolve to a known method) |
Response 200
{
"payment": { "id": 9, "status": "PENDING", "method": "PAGO_MOVIL", "amount_usd": 20.2 },
"checkoutUrl": "https://checkout.example/pay/abc123"
}
checkoutUrl is the provider redirect/checkout link when applicable.
Errors
| Status | Body | When |
|---|---|---|
404 |
{ "error": "Orden no encontrada." } |
Unknown order, or not owned by the caller |
400 |
{ "error": "Método inválido." } |
method is not a known payment method |
400 |
{ "error": "…" } |
Intent creation failed at the provider |
Returns the payment associated with the order. Owner only.
Response 200 — { "payment": { … } }. See
the payment object.
Errors
| Status | Body | When |
|---|---|---|
404 |
{ "error": "Sin pago para esta orden." } |
No payment exists for the order |
403 |
{ "error": "No autorizado." } |
The payment belongs to another user |
Sypago step 1. Triggers the bank OTP to the payer. The order must use Débito
Inmediato (SYPAGO_DI), be in WAITING_FOR_PAYMENT, and belong to the caller.
Request body
| Field | Type | Required | Rules |
|---|---|---|---|
bank_code |
string | yes | payer's bank code |
id_number |
string | yes | payer document, e.g. V-12345678 |
phone |
string | yes | payer phone |
name |
string | no | payer name |
Response 200 — { "prepared": true, "payment": { … } }.
Errors
| Status | Body | When |
|---|---|---|
404 |
{ "error": "NotFound", "message": "Orden no encontrada." } |
Unknown order |
403 |
{ "error": "Forbidden", "message": "No es tu pedido." } |
Caller is not the owner |
422 |
{ "error": "Unprocessable", "message": "…" } |
Order isn't Débito Inmediato, or not WAITING_FOR_PAYMENT |
400 |
{ "error": "PaymentError", "message": "…" } |
Provider call failed |
Sypago step 2. Submits the OTP the payer received and finalizes the charge. Same
ownership/method/state gating as prepare.
Request body
| Field | Type | Required | Rules |
|---|---|---|---|
bank_code |
string | yes | payer's bank code |
id_number |
string | yes | payer document, e.g. V-12345678 |
phone |
string | yes | payer phone |
name |
string | no | payer name |
otp |
string | yes | the code the payer received from their bank |
Response 200 — { "payment": { … } }.
Errors
| Status | Body | When |
|---|---|---|
404 |
{ "error": "NotFound", "message": "Orden no encontrada." } |
Unknown order |
403 |
{ "error": "Forbidden", "message": "No es tu pedido." } |
Caller is not the owner |
422 |
{ "error": "Unprocessable", "message": "…" } |
Order isn't Débito Inmediato, or not WAITING_FOR_PAYMENT |
400 |
{ "error": "PaymentError", "message": "…" } |
Provider call failed |
# Sypago (Débito Inmediato): step 1 triggers the bank OTP
curl -X POST /api/v1/orders/42/payment/prepare \
-H "Authorization: Bearer {client_token}" \
-H "Content-Type: application/json" \
-d '{"bank_code": "0102", "id_number": "V-12345678", "phone": "+584241000001"}'
# Step 2 submits the OTP the payer received
curl -X POST /api/v1/orders/42/payment/report \
-H "Authorization: Bearer {client_token}" \
-H "Content-Type: application/json" \
-d '{"bank_code": "0102", "id_number": "V-12345678", "phone": "+584241000001", "otp": "123456"}'