PaymentLink
POST /v1/payment_links — crear un cobro
Alcance: payment_links:write. Idempotencia: requerida.
| campo | tipo | requerido | constraint |
|---|---|---|---|
amount | integer | sí | unidad más pequeña de la moneda; > 0 |
currency | string | sí | BRL | USD |
reference | string | no | ≤ 255 chars; su clave de conciliación |
payment_methods | array<string> | no | subconjunto de ["pix","card"]; default ["pix"] |
max_installments | integer | no | 1–21; default 12; solo si card está habilitado |
fee_handling | string | no | absorb | pass_through; default absorb |
expires_in | integer | no | segundos hasta expirar; >= 0 (0 = sin expiración) |
campaign | boolean | no | default false; true mantiene el link abierto tras cada pago y cotiza FX por sesión |
description | string | no | ≤ 500; nombre del producto mostrado en el checkout |
product_ref | string | no | ≤ 255; SKU/referencia de producto (mostrado) |
details | string | no | ≤ 500; descripción larga (mostrada) |
product_images | array<string> | no | ≤ 5; imágenes del producto |
collect_fields | array<string> | no | subconjunto de ["phone","address"]; si se omite, hereda la config del tenant; recolecta esos campos del pagador en el checkout |
metadata | object | no | ≤ 20 claves; ver Metadata |
amount + currency son la única denominación que usted informa. target_currency, charge_currency y merchant_payout_currency son derivados por la plataforma y devueltos en la respuesta — nunca son input. La cobertura cambiaria — el margen sobre el tipo de cambio que protege contra la variación entre la cotización y la liquidación — se aplica siempre que hay conversión, independientemente de la moneda de denominación del link.
campaign=false crea un link de cobro único: tras el primer pago capturado/liquidado, se rechazan nuevos intentos. campaign=true crea un link de campaña: acepta múltiples pagos, cada sesión bloquea el tipo de cambio mostrado por 900 segundos, y pagos diferentes pueden usar tipos de cambio diferentes.
Response 201 — objeto PaymentLink:
{
"id": "pl_9f8c...", "mode": "live",
"status": "active", "checkout_url": "https://loja.pgz.link/pay/pl_9f8c...",
"amount": 250000, "currency": "BRL", "reference": "pedido-8842",
"target_currency": "BRL", "charge_currency": "BRL", "merchant_payout_currency": "USD",
"payment_methods": ["pix"], "max_installments": 12, "fee_handling": "absorb",
"expires_at": 1790000000, "description": "Rolex Submariner", "product_ref": "126610LN",
"details": null, "product_images": [], "collect_fields": [], "metadata": {}, "created": 1789990000
}Errores: unknown_parameter (400), invalid_request (400), missing_idempotency_key (400), invalid_idempotency_key (400), idempotency_key_reuse (409), invalid_api_key (401), insufficient_scope (403), merchant_net_not_positive (400 — ver Protección del link).
Quote — la única superficie externa de precio
El precio de un cobro se obtiene exclusivamente por este endpoint. Ninguna tarifa, porcentaje o tipo de cambio se publica en este contrato: usted le pregunta al endpoint en el momento.
POST /v1/quotes — cotizar
Alcance: quotes:write. Idempotencia: requerida.
| campo | tipo | requerido | constraint |
|---|---|---|---|
amount | integer | sí | unidad más pequeña; > 0 |
currency | string | sí | BRL | USD |
payment_method | string | no | pix | ted | card; si se omite, devuelve todos los escenarios |
installments | integer | no | 1–21; default 12 |
fee_handling | string | no | absorb | pass_through; si se omite, usa el default de la regla de precio |
También se acepta el shape legado { "payment_intent_id": "pi_..." } con payment_method? e installments?.
Response 201 — objeto Quote cuando se informa payment_method:
{
"id": "qt_9f8c...",
"tenant_id": "three-hands",
"mode": "live",
"payment_intent_id": null,
"payment_method": "card",
"installments": 5,
"payer_total": 5000000,
"payer_currency": "BRL",
"receiver_net": 843375,
"receiver_currency": "USD",
"fx": {
"rate": 5.195993,
"base": "USD",
"quote": "BRL",
"locked_at": 1789990000,
"valid_until": 1789990900,
"rate_history_id": 123
},
"breakdown": {
"fee_handling": "absorb",
"total_fee": { "amount": 118905, "currency": "USD" },
"service_fee": { "final_cents": 118905, "final_currency": "USD", "percent_amount_cents": 118905, "fixed_cents": 0, "raw_total_cents": 118905, "min_applied": false, "max_applied": false },
"installment_surcharge": { "amount": 0, "currency": "USD" },
"tax": { "iof_amount_cents": 0, "iof_applicable": false, "iof_enabled": false, "iof_currency": "USD" }
},
"fx_rate_history_id": 123,
"rule_id": "cg_three-hands_5x",
"expires_at": 1789991800,
"signature": "sha256...",
"created_at": 1789990000
}La Quote es válida por 1800 segundos para ser aceptada. Esa validez de negociación es diferente de la validez interna del FX (fx.valid_until) y de la sesión de checkout de 900 segundos. Los valores numéricos de arriba son ilustrativos de forma, no de precio.
Cuando se omite payment_method, la respuesta contiene scenarios.pix, scenarios.ted y scenarios.card[] con tarjeta desde 1x hasta el límite del tenant. Cada escenario lleva quote_id, método, cuotas, total del pagador, neto del merchant, FX público, expires_at y signature.
Errores: unknown_parameter (400), invalid_request (400), payment_method_unsupported (400), quote_fx_required (400), idempotencia, auth.
POST /v1/quotes/{id}/accept — aceptar una cotización
Alcance: payment_intents:write.
| campo | tipo | requerido | constraint |
|---|---|---|---|
name | string | sí | título mostrado en el checkout; 1–500 chars |
description | string | no | descripción larga mostrada en el checkout; ≤ 500 chars |
media_urls | array<string> | no | hasta 5 URLs alojadas por Pagooz |
expires_in | integer | no | segundos hasta expirar; 0 = sin expiración |
campaign | boolean | no | default false; true no guarda el FX de la quote en el link y cotiza FX por sesión |
reference | string | no | ≤ 255 chars; su clave de conciliación |
metadata | object | no | metadata libre |
payer_country | string | no | ISO 3166-1 alpha-2 |
Crea un PaymentIntent interno y un PaymentLink. El intent usa los valores persistidos en la quote: amount, currency, legal_entity_id, settlement_term, fee_strategy y fx_rate_history_id. Un link único (campaign=false) usa el fx_rate_history_id de la quote, aplica el trinquete del 1% en el checkout y expira en 24h cuando se omite expires_in. Un link de campaña (campaign=true) no guarda el FX de la quote en el link; cada sesión cotiza y bloquea el tipo de cambio mostrado por 900 segundos. Una quote expirada devuelve 400 quote_expired; una quote de otro tenant devuelve 404 quote_not_found; una quote ya consumida devuelve 400 quote_consumed.
Response 201: link de pago.
{
"id": "pl_9f8c...",
"url": "https://loja.pgz.link/pay/pl_9f8c...",
"checkout_url": "https://loja.pgz.link/pay/pl_9f8c...",
"amount": 5000000,
"currency": "BRL",
"payment_method": "card",
"installments": 5,
"payer_total": 5000000,
"payer_currency": "BRL",
"receiver_net": 843375,
"receiver_currency": "USD",
"fx_rate_history_id": 123,
"payment_intent_id": "pi_9f8c...",
"expires_at": 1790000000,
"reference": "pedido-8842"
}PaymentIntent
POST /v1/payment_intents — crear un intent legado
Alcance: payment_intents:write.
| campo | tipo | requerido | constraint |
|---|---|---|---|
legal_entity_id | string | no | default: la única entidad activa del tenant |
amount | integer | sí | unidad más pequeña; > 0 |
currency | string | sí | BRL | USD |
payer_country | string | no | ISO 3166-1 alpha-2 |
settlement_term | string | no | default: tenants.settlement_window_days como D+N |
fee_strategy | object | no | legado |
metadata | object | no | metadata libre |
reference | string | no | ≤ 255 chars |
Este endpoint permanece por compatibilidad. El flujo canónico es POST /v1/quotes seguido de POST /v1/quotes/{id}/accept. quote_id no se acepta en este endpoint y devuelve 400 unknown_parameter.
Payment — objeto de conciliación
El Payment es la fuente de verdad de la conciliación. Sus valores son los mismos producidos al momento del cobro, en la misma precisión (centésimo de basis point), leídos de la misma fuente persistida. Ninguna capa recalcula el precio para mostrar o notificar. Una diferencia entre lo que el pagador vio en el checkout y lo que usted recibe aquí es un bug crítico, no redondeo.
Cada objeto Payment lleva los campos de abajo; los campos condicionales obedecen a la regla de su propia fila (pueden ser omitidos o null exactamente cuando la fila lo indica):
| campo | significado para la conciliación |
|---|---|
id | identificador del pago (pay_...) |
link_id | el PaymentLink que lo originó, o null |
status | estado persistido del pago |
reference | su clave libre, definida al crear el link, devuelta aquí y en cada webhook |
created_at | cuándo se inició el pago (unix) |
updated_at | última actualización (unix; puede ser null) |
amount + currency | alias legado del bruto cobrado |
gross_amount + gross_currency | bruto cobrado al pagador |
fee_amount + fee_currency | tarifa total persistida cuando está disponible |
net_amount + net_currency | neto del merchant |
fee_handling | absorb | pass_through — quién pagó la tarifa de servicio en este pago (valor efectivo); independiente de breakdown/fees[] |
fx | { rate, base, quote, locked_at, valid_until, rate_history_id } — tipo de cambio bloqueado con cobertura. Presente solo cuando hay conversión de moneda. Sin conversión (ej.: BRL→BRL) el campo fx se omite — nunca fabricamos un objeto FX, rate: 1.0, un objeto vacío o null solo para satisfacer la forma. No expone spot, cobertura ni fuente interna |
payment_method | pix | ted | card |
installments | integer cuando aplica, si no null |
failure_code | presente cuando status = failed — código Pagooz del motivo (ver Códigos de rechazo) |
Códigos de rechazo (payment.failed)
Cuando un pago falla, decline_code es siempre un código semántico de Pagooz. El motivo original de cualquier sistema upstream nunca se repasa: se mapea internamente a uno de estos códigos, y lo que no tiene mapeo conocido se vuelve payment_declined.
decline_code | significado |
|---|---|
insufficient_funds | saldo/límite insuficiente |
card_declined | rechazado por el emisor |
expired_card | tarjeta expirada |
invalid_card | datos de tarjeta inválidos |
card_not_supported | marca/tipo no soportado |
authentication_failed | autenticación (3-D Secure) no completada |
pix_expired | el QR expiró sin pago |
pix_rejected | PIX rechazado por el banco del pagador |
payment_declined | rechazo genérico (motivo sin mapeo) |
GET /v1/payments/{id}
Alcance: payments:read. Response 200: objeto Payment. Errores: payment_not_found (404).
GET /v1/payments — cierre de período
Alcance: payments:read. Es el endpoint de conciliación de período.
Query:
| campo | tipo | requerido | descripción |
|---|---|---|---|
from | string | sí | fecha inicial en America/Sao_Paulo, YYYY-MM-DD, inclusiva en 00:00:00 |
to | string | sí | fecha final en America/Sao_Paulo, YYYY-MM-DD, inclusiva en 23:59:59 |
limit | integer | no | default 20, máximo 100 |
starting_after | string | no | cursor: id del último pago recibido en la página anterior |
Los límites siguen el calendario bancario brasileño usado en el cockpit: un pago a las 2026-08-20T22:00:00-03:00 pertenece al día 2026-08-20, aunque sea 2026-08-21 en UTC.
Orden: created_at DESC, id DESC. No hay filtro de status: el integrador recibe todos los pagos del período y filtra localmente si quiere.
Response 200: { data: Payment[], has_more, next_cursor }.
fee_handling y el flujo de reconocimiento de tarifa
fee_handling define quién paga la tarifa de servicio:
absorb— el pagador paga el valor del producto; la tarifa reduce el neto del merchant. Es el default.pass_through— el pagador paga producto + tarifa; el merchant recibe el valor completo.
En pass_through, la página de checkout exige el reconocimiento explícito del total por parte del pagador antes de generar el cobro (la plataforma valida que el total reconocido coincida, al centavo, con el total bloqueado). El reconocimiento se registra como el evento interno payment.fees_acknowledged. En absorb no hay reconocimiento. Usted no maneja este flujo por la API — pertenece al checkout alojado; usted solo recibe el fee_handling efectivo en el Payment.
Reglas del link
- Monedas:
BRLyUSD. El cobro de PIX y tarjeta se liquida en BRL al pagador; conversión y cobertura son derivadas. - Expiración:
expires_inen segundos;0= sin expiración. Tras expirar, el link no acepta pago y emitelink.expired. - Cuotas:
1–21(solo tarjeta). El default de producto de un link es12(ADR-0006). - Métodos:
pixy/ocard. - Protección económica (
absorb): si, con el valor informado, el neto del merchant sería≤ 0tras las tarifas, la creación falla conmerchant_net_not_positive(400). Usepass_througho aumente el valor.