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 GET /settlements/webhook/sypago Public SyPago payout (dispersion) confirmation webhook (token in URL)
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
  }
}

amount_cents / amount_usd is the order's client_charged_*, i.e. the total after any promotional credit — not client_pays_*.


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 /settlements/webhook/sypago

SyPago payout (dispersion) confirmation callback for the driver-settlement (liquidación) module — distinct from the customer-charge webhook above. Public, but authenticated by a shared secret in the URL (?token=…) rather than a bearer token. Accepts GET or POST.

Request body (provider-sent; only the fields below are read — amount and receiving_user are echoed from the original payout and ignored)

Field Type Required Rules
internal_id string yes the payout's internal reference
transaction_id string yes SyPago transaction id
status string yes one of ACCP, PEND, PROC, RJCT, CANC
rejected_code string no present on a rejection
ref_ibp string no interbank reference
amount object no echoed from the transaction
receiving_user object no echoed from the transaction

Response 200 — { "message": "ok" } for any well-formed, authorized call, including an unknown or already-resolved internal_id.

Errors

Status Body When
401 { "error": "Unauthorized" } Missing or wrong URL token
422 { "error": "ValidationError", "details": { "fieldErrors": { … } } } Missing internal_id, transaction_id, or status

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