paid o canceled no son modificables.409 en vez de sobreescribir el pago.| Prioridad | Forma | Notas |
|---|---|---|
| 1 | Authorization: Bearer ak_xxxxxxxx | Solo se acepta si el token empieza con el prefijo ak_ |
Authorization, se rechaza la peticion.| Header | Req | Valor |
|---|---|---|
Authorization | sí | Ver sección 1 |
| Param | Tipo | Validación |
|---|---|---|
id | string | Debe ser un ObjectId de MongoDB válido; si no, 404 |
| Campo | Tipo | Requerido | Reglas |
|---|---|---|---|
amount | number | string numérico | no | Debe ser > 0. Se acepta number (120.5) o string numérico ("120.50"). Se redondea a 2 decimales. Cualquier otro tipo (bool, null, array, object) → 400. Ver reglas de montos en sección 6. |
description | string | null | no | String libre. null limpia el campo (queda ""). |
metadata | object | no | Objeto de campos libres. Se reemplaza completo (no hace merge con el anterior). |
reference | string | no | Solo lectura. Si viene y es distinta a la actual → 400. Si viene idéntica, se ignora sin error. Nunca modifica la referencia. |
reference actual o el amount vigente), responde 200 con el documento sin modificar (PUT idempotente).amountReceived (lo ya acreditado por abonos SPEI):| Estado del intent | Aumentar | Mantener | Bajar |
|---|---|---|---|
pending | ✅ 200 | ✅ 200 (no-op) | ✅ 200 — cualquier valor > 0 |
partial | ✅ 200 | ✅ 200 (no-op) | ⚠️ Hasta amountReceived; por debajo → 409 |
paid / canceled | ❌ 409 | ❌ 409 | ❌ 409 |
partial el nuevo monto queda <= amountReceived (el abono ya recibido cubre el nuevo total), el intent se promueve a paid con completedAt en la misma operación atómica. Si el nuevo monto queda entre amountReceived y el monto actual, permanece partial.409 con el motivo re-classificado. Nunca queda un intent pagado con el monto alterado por esta vía.200 OK — Actualización aplicada (o no-op idempotente){
"paymentIntent": {
"_id": "665f1a2b3c4d5e6f7a8b9c0d",
"tenantId": "demo",
"centro_costo": null,
"reference": "pi3fa2c1",
"amount": 120.50,
"amountReceived": 0,
"amountUpdatedAt": "2026-08-28T14:40:10.773Z",
"amountUpdatedBy": "apiKey:665f0a11b2c3d4e5f6a7b8c9",
"status": "pending",
"description": "Pago actualizado",
"metadata": { "orderId": "123" },
"payments": [],
"createdBy": "apiKey:665f0a11b2c3d4e5f6a7b8c9",
"lastPaidAt": null,
"completedAt": null,
"createdAt": "2026-08-28T13:00:00.000Z",
"updatedAt": "2026-08-28T14:40:10.773Z"
}
}paymentIntent| Campo | Tipo | Descripción |
|---|---|---|
_id | string | ObjectId del intent (el mismo del path) |
tenantId | string | Tenant dueño (implícito por la API key) |
centro_costo | string | null | Centro de costo; heredado del creador al crear el intent |
reference | string | Referencia inmutable que el pagador pone en el concepto de pago SPEI. Formato pi + 6 hex |
amount | number | Monto esperado, 2 decimales. Este es el campo que modifica el endpoint |
amountReceived | number | Suma acreditada de abonos aplicados. |
amountUpdatedAt | string ISO | null | Fecha del último cambio de amount hecho por este endpoint |
amountUpdatedBy | string | Actor del último cambio de monto: apiKey:<id> o el sub del usuario JWT |
status | string | pending | partial | paid | canceled |
description | string | Descripción libre |
metadata | object | Campos libres del integrador |
payments | array | Historial de abonos aplicados: { transaccionId, claveRastreo, montoNeto, montoOriginal, fechaOperacion, createdAt }. Solo lectura desde la API |
createdBy | string | Actor que creó el intent |
lastPaidAt | string ISO | null | Fecha del último abono aplicado |
completedAt | string ISO | null | Fecha en que quedó paid. |
createdAt / updatedAt | string ISO | Timestamps del documento |
{ "message": "<motivo>" } con el status correspondiente. (Los errores de infraestructura no controlados usan el envelope global { "success": false, "message": "..." }.)400 Bad Request| message exacto | Causa |
|---|---|
Invalid amount | amount presente con valor <= 0, no numérico ("abc"), o de tipo no permitido (true, null, array, object) |
reference is immutable | Body trae reference distinta a la actual del intent |
401 Unauthorized| message exacto | Causa |
|---|---|
API key required | No llegó ninguna API key (header ausente/vacío) |
Invalid API key | La key no coincide con ninguna activa en el índice maestro |
404 Not Found| message exacto | Causa |
|---|---|
Not found | El id no es un ObjectId válido, el intent no existe, pertenece a otro tenant, o (solo en la variante JWT) está fuera del centro de costo del operador. Deliberadamente indistinguible entre estos casos |
Tenant not found or inactive (con success: false) | La API key es válida pero el tenant está inactivo |
409 Conflict| message exacto | Causa |
|---|---|
Cannot update a paid/canceled intent | El intent está paid o canceled (verificación previa o detección de carrera) |
Amount can only be lowered down to the received amount on a partially paid intent | Decremento ilegal: en pending cualquier baja; en partial, bajar por debajo de amountReceived. También se devuelve si un abono concurrente convirtió la baja en ilegal (carrera) |
amount: 100:| # | Estado inicial | amountReceived | Body | Status | Resultado |
|---|---|---|---|---|---|
| 1 | pending | 0 | {"amount": 150} | 200 | amount=150, amountUpdatedAt/By seteados |
| 2 | pending | 0 | {"amount": 50} | 200 | amount=50 |
| o | |||||
| 3 | partial | 50 | {"amount": 50} | 200 | amount=50, promovido a paid + completedAt |
| 4 | partial | 50 | {"amount": 80} | 200 | amount=80, sigue partial |
| 5 | partial | 50 | {"amount": 40} | 409 | Monto intacto |
| 6 | paid | 100 | {"amount": 150} | 409 | Nada cambia |
| 7 | canceled | 0 | {"description": "x"} | 409 | Nada cambia |
| 8 | pending | 0 | {"reference": "piotra", "amount": 120} | 400 | reference is immutable |
| 9 | pending | 0 | {"reference": "pi3fa2c1"} (la misma) | 200 | No-op idempotente |
| 10 | pending | 0 | {"amount": 0} / -10 / "abc" / true | 400 | Invalid amount |
| 11 | pending | 0 | {"amount": "120.50"} | 200 | String numérico aceptado → 120.5 |
| 12 | pending | 0 | {} o {} con campos sin efecto | 200 | No-op idempotente |
| 13 | partial | 50 | abono STP entra durante el PUT | 409 | Guard atómico; el pago manda |
| 14 | — | — | id inexistente / de otro tenant / malformado | 404 | Not found |
200 con el documento (no error), incluyendo el caso de bajar el monto a un valor que ya es el vigente.120.504 → 120.5.curl --location --request PUT 'https://stp.paydaymx.com/api/developer/payment-intents/6a91af05659dd8c8fe1843b0' \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"amount": 150.00
}'{
"paymentIntent": {
"_id": "665f1a2b3c4d5e6f7a8b9c0d",
"tenantId": "demo",
"centro_costo": null,
"reference": "pi3fa2c1",
"amount": 120.50,
"amountReceived": 0,
"amountUpdatedAt": "2026-08-28T14:40:10.773Z",
"amountUpdatedBy": "apiKey:665f0a11b2c3d4e5f6a7b8c9",
"status": "pending",
"description": "Pago actualizado",
"metadata": { "orderId": "123" },
"payments": [],
"createdBy": "apiKey:665f0a11b2c3d4e5f6a7b8c9",
"lastPaidAt": null,
"completedAt": null,
"createdAt": "2026-08-28T13:00:00.000Z",
"updatedAt": "2026-08-28T14:40:10.773Z"
}
}