Payments

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.

Endpoints

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 object

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
  }
}

POST /payments/webhook/{provider}

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

POST /orders/{orderId}/payment

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

GET /orders/{orderId}/payment

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

POST /orders/{orderId}/payment/prepare

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

POST /orders/{orderId}/payment/report

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

Examples

# 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"}'