Un voucher nominado es un instrumento de descuento personal, emitido a un
documento (no a una cuenta, no a un passengerId). Es la base del programa de
becas y de los pasajes abiertos personales.
Se distingue del voucher de cuenta (prepago corporativo) en que:
| Voucher de cuenta | Voucher nominado | |
|---|---|---|
| Atado a | acc_id (empresa) |
Documento (IDNumber + IDCountry) |
| Lo usa | Cualquiera de la cuenta | Solo la persona cuyo documento coincide |
| Valor | Prepago (punit) |
% de descuento (discount) |
| Se aplica sobre | La tarifa | Tarifa + equipaje del pasajero |
La regla de oro: un voucher nominado solo aplica si uno de los pasajeros del booking tiene el documento del voucher. Ese match es la validación central y el mecanismo de seguridad.
Todos los endpoints se consumen por POST al motor de Humming:
POST {BASE_URL}/intranet/sys/mods/flights/motor.php
{BASE_URL} depende del entorno (PRD / DEV / LOC).
Header obligatorio:
Authorization: Bearer {HUMMING_API_TOKEN}
Content-Type: application/x-www-form-urlencoded (o JSON; el motor acepta ambos)
No hay gate de rol. Cualquier cliente autenticado por Bearer (incluido un Lambda) puede consumir estos endpoints. La seguridad no es el rol, es el match de documento: no se puede aplicar la beca de otra persona a un booking donde esa persona no viaja.
1. OFFER getBookingSummary -> qué pasajeros son elegibles (nominal_vouchers)
2. VERIFY verifyPassengerVoucher -> ¿el código aplica? (dry-run, NO consume, devuelve preview)
3. REDEEM redeemPassengerVoucher -> aplica + consume el descuento
4. REFRESH getBookingSummary -> el total ya refleja el descuento
El paso VERIFY es opcional (para previsualizar antes de aplicar). REDEEM revalida todo internamente, así que se puede ir directo de OFFER a REDEEM.
getBookingSummary (el OFFER)Devuelve el resumen del booking incluyendo nominal_vouchers: qué reservas/pasajeros
son sujetos de aplicar un voucher nominado.
POST /intranet/sys/mods/flights/motor.php
Authorization: Bearer {HUMMING_API_TOKEN}
{
"execute": "getBookingSummary",
"id": "{md5(bookingId)}"
}
⚠️ Ojo con el identificador:getBookingSummaryrecibeid= md5 del bookingId (no el entero). Los otros dos endpoints recibenbookingId(entero). Es una asimetría del contrato actual — tenerla presente.
{
"nominal_vouchers": {
"148264": {
"reservationId": 148264,
"passengerId": 2954,
"passengerName": "Perez, Juan",
"IDNumber": "17923900",
"IDCountry": "AR",
"vouchers": [
{
"voucherId": 130,
"voucherCode": "XW9I7PSGZ4",
"discount": 50.0,
"untilDate": "2027-02-28",
"remaining": 35,
"applied": false
}
]
}
}
}
nominal_vouchersObjeto keyed por reservationId. Cada entrada:
| Campo | Tipo | Significado |
|---|---|---|
reservationId |
int | La reserva (pasajero × tramo) elegible |
passengerId |
int | Pasajero de esa reserva |
passengerName |
string | "Apellido, Nombre" |
IDNumber / IDCountry |
string | Documento normalizado del pasajero |
vouchers[] |
array | Vouchers nominados disponibles para ese documento |
vouchers[].voucherId |
int | ID del voucher |
vouchers[].voucherCode |
string | Código del voucher |
vouchers[].discount |
float | % de descuento |
vouchers[].untilDate |
date | Vigencia |
vouchers[].remaining |
int / null | Usos restantes (tripCount − usados); null = sin límite |
vouchers[].applied |
bool | Si ya está aplicado a esta reserva |
nominal_vouchers viene vacío
([] o {}).verifyPassengerVoucher (validar, sin consumir)Valida un código contra el booking en modo dry-run: corre todas las validaciones y devuelve el preview del descuento, sin insertar nada ni tocar la facturación. Ideal para el momento en que el usuario ingresa el código.
POST /intranet/sys/mods/flights/motor.php
Authorization: Bearer {HUMMING_API_TOKEN}
{
"execute": "verifyPassengerVoucher",
"voucherCode": "XW9I7PSGZ4",
"bookingId": 73269
}
Se puede pasar voucherCode o voucherId. bookingId es el
entero.
{
"status": 1000,
"msg": "OK",
"reservations": [148264],
"discountAmount": 107.50,
"discount": 50.0
}
| Campo | Significado |
|---|---|
status |
1000 = válido / aplicable · 1026 = rechazado · 2000 = error
técnico |
reservations[] |
Las reservas del booking a las que aplicaría (las del becado) |
discountAmount |
Monto total del descuento en USD (sobre tarifa + equipaje) |
discount |
% del voucher |
err[] |
Presente si hubo rechazo/error: motivo(s) legibles |
redeemPassengerVoucher (aplicar + consumir)Valida (igual que verify) y, si pasa, aplica el descuento (registra la línea per-reserva) y refresca la facturación. Es idempotente: reaplicar el mismo voucher al mismo booking recalcula, no duplica.
POST /intranet/sys/mods/flights/motor.php
Authorization: Bearer {HUMMING_API_TOKEN}
{
"execute": "redeemPassengerVoucher",
"voucherCode": "XW9I7PSGZ4",
"bookingId": 73269
}
Response: idéntica a verifyPassengerVoucher (status,
reservations, discountAmount, discount, err).
Tras un redeem status=1000, volvé a llamar getBookingSummary para que el total refleje el
descuento.
Al verificar/redimir, el motor aplica en orden:
discount > 0 y tiene IDNumber. Si no → error (no es
una beca).untilDate >= hoy. Si venció → rechazo
(1026).tripCount. Si se agotó → rechazo (1026).upper(regexp_replace(IDNumber,'[^A-Za-z0-9]','')) +
upper(trim(IDCountry))). Si ninguno → rechazo (1026, "el código
es del documento X, que no está entre los pasajeros").(tarifa base + equipaje de esa reserva) × discount%. Los demás pasajeros del booking pagan
full.status |
Constante | Significado |
|---|---|---|
1000 |
stxOk |
Válido / aplicado correctamente |
1026 |
stxRejected |
Rechazo de negocio: documento no coincide, vencido, o agotado. Mostrar
msg/err al usuario. |
2000 |
stxError |
Error técnico: falta bookingId/voucherId, voucher
inexistente, etc. |
Regla para el cliente Lambda: 1026 es esperable y accionable (mostrar el motivo).
2000 es un problema de integración (loguear).
1) getBookingSummary(id = md5(bookingId))
-> leer nominal_vouchers
-> si vacío: no hay becas, seguir el checkout normal
2) Por cada reservationId elegible:
mostrar el pasajero y pedir/confirmar el código
3) verifyPassengerVoucher(voucherCode, bookingId)
-> status 1000: mostrar "aplica {discount}% — ahorro USD {discountAmount}"
-> status 1026: mostrar err (documento/vencido/agotado)
4) Al confirmar o cerrar:
redeemPassengerVoucher(voucherCode, bookingId)
-> status 1000: aplicado
5) getBookingSummary(...) de nuevo
-> el total ya viene con el descuento
redeemPassengerVoucher se puede llamar varias veces sin duplicar
(borra el descuento previo de ese voucher en el booking y recalcula). Útil para reintentos.getBookingSummary usa
id = md5(bookingId); verify/redeem usan bookingId entero.voucherCode viaja en el OFFER: hoy nominal_vouchers expone el
voucherCode de los vouchers disponibles. Si el modelo de negocio exige que la persona
presente el código (y no que el sistema lo sugiera), pedir que se oculte del offer.