Desarrolladores/Integración

Recargas de celular

Una recarga de celular agrega saldo directamente a un número de teléfono prepago. Tu cliente recibe tiempo aire o datos en su línea. No hay ningún código que entregar. Pagas con tu billetera CardV, igual que en los demás pedidos.

Ver también: Autenticación · Convenciones · Webhooks

#Cómo funciona

Text
GET  /recharge/countries            países que puedes recargar
GET  /recharge/operators?country=US  operadores, tipos de recarga y montos
POST /recharge/quote                tu precio y un quote_token válido por 300 s
POST /recharge/orders               haz el pedido, pagado con tu billetera
GET  /recharge/orders/{order_id}    consulta el estado o espera un webhook
  • Todas las rutas empiezan con /api/v1. Envía los mismos encabezados que en cualquier llamada.
  • Las dos llamadas POST tienen que ir firmadas. Fírmalas igual que POST/orders, pero con su propia ruta, por ejemplo /api/v1/recharge/quote.
  • Los pedidos de recarga son independientes de los pedidos de tarjetas de regalo. Para leerlos, usa los endpoints /recharge.
  • Solo se ofrecen recargas directas. Los productos con PIN (un código que el cliente escribe) no están disponibles.

#Países

GET/api/v1/recharge/countries muestra los países que puedes recargar ahora.

JSON
{
  "count": 2,
  "results": [
    {"code": "MX", "name": "Mexico", "currency_codes": ["MXN"], "operator_count": 4, "offer_count": 37},
    {"code": "US", "name": "United States", "currency_codes": ["USD"], "operator_count": 6, "offer_count": 52}
  ]
}
  • code es el código de país ISO 3166-1 alfa-2. Envíalo como country en las siguientes llamadas.
  • currency_codes son las monedas locales en las que venden los operadores de ese país.
  • La lista cambia cuando se agregan operadores o dejan de estar disponibles. Cárgala cada pocas horas.

#Operadores

GET/api/v1/recharge/operators?country=US muestra los operadores de un país. Agrega search=att para filtrar por nombre de operador.

JSON
{
  "count": 1,
  "results": [
    {
      "operator_key": "us-att",
      "name": "AT&T",
      "country": "US",
      "country_name": "United States",
      "logo_url": "https://b2b.cardv.net/api/v1/catalog-assets/3f5c...a1.png",
      "subtypes": ["airtime", "data"],
      "amount_model": "range",
      "currency_codes": ["USD"],
      "amounts": [
        {"min": "5.0000", "max": "100.0000", "currency": "USD", "subtype": "airtime", "label": "5.0000-100.0000 USD"},
        {"min": "15.0000", "max": "15.0000", "currency": "USD", "subtype": "data", "label": "15.0000 USD"}
      ],
      "offer_count": 3
    }
  ]
}
CampoQué significa
operator_keyEl ID que envías para cotizar y pedir, por ejemplo us-att. Guárdalo como texto.
subtypesQué puedes comprar: airtime (saldo para llamadas), data o bundle (llamadas y datos).
amount_modelfixed si todos los montos son valores fijos, range si alguno es un rango.
amounts[]Cada opción. Si min es igual a max, es un monto fijo. Si no, vale cualquier monto intermedio.
amounts[].currencyLa moneda local de esa opción. Envíala como local_currency.
logo_urlImagen alojada por CardV, o "".
  • Los montos son montos locales: lo que recibe la línea telefónica, en la moneda local.
  • Un country desconocido o mal formado devuelve HTTP 400.

#Cotización

POST/api/v1/recharge/quote te da tu precio para una recarga. Esta llamada tiene que ir firmada.

JSON
{
  "country": "US",
  "operator_key": "us-att",
  "amount": "10.00",
  "local_currency": "USD",
  "subtype": "airtime"
}
CampoObligatorioQué significa
countrySíCódigo de país de la lista de países.
operator_keySíDe la lista de operadores.
amountSíMonto local, como texto. Fijo: uno de los valores de la lista. Rango: entre min y max.
local_currencyRecomendadoCódigo ISO 4217 de amount, tomado de amounts[].currency. Envíalo cuando un operador tenga más de una moneda.
subtypeNoairtime (por defecto), data o bundle.

La respuesta:

JSON
{
  "country": "US",
  "country_name": "United States",
  "operator": {"operator_key": "us-att", "name": "AT&T", "logo_url": ""},
  "subtype": "airtime",
  "local_amount": "10.0000",
  "local_currency": "USD",
  "merchant_price": "9.6200",
  "merchant_currency": "USD",
  "expires_at": "2026-09-30T08:20:30.123456+00:00",
  "quote_token": "eyJ2ZXJzaW9uIjox...:1uXyZa:8c1f..."
}
  • merchant_price es lo que paga tu billetera, en merchant_currency.
  • quote_token fija este precio durante 300 segundos, hasta expires_at. Envíalo sin cambios con el pedido.
  • El token está ligado a tu cuenta y a este país, operador, tipo y monto.
  • Antes de hacer el pedido, revisa que local_currency sea la moneda que esperabas.
  • Una cotización no reserva dinero. Puedes pedir una cotización nueva en cualquier momento.

#Hacer un pedido de recarga

POST/api/v1/recharge/orders recarga el teléfono y lo paga con tu billetera. Esta llamada tiene que ir firmada.

JSON
{
  "external_order_id": "SHOP-RC-20260930-0001",
  "country": "US",
  "operator_key": "us-att",
  "amount": "10.00",
  "local_currency": "USD",
  "subtype": "airtime",
  "account": "12125550100",
  "quote_token": "eyJ2ZXJzaW9uIjox...:1uXyZa:8c1f..."
}
CampoObligatorioQué significa
external_order_idSíTu número de pedido. No se puede repetir en ninguno de tus pedidos, incluidos los de tarjetas de regalo.
country, operator_key, amount, local_currency, subtypeSíLos mismos valores que enviaste en la cotización.
accountSíEl número de teléfono que se va a recargar: solo dígitos, con el código de país, sin + ni espacios.
quote_tokenSíEl de la cotización, antes de que venza.

Ejemplos de números de teléfono: 12125550100 (Estados Unidos), 525512345678 (México). Revisa que el número pertenezca al operador elegido. Una recarga enviada a un número equivocado no se puede revertir.

CardV revisa la cotización, cobra merchant_price de tu billetera en ese momento y empieza la recarga en segundo plano. Un pedido nuevo devuelve HTTP 201:

JSON
{
  "idempotent_replay": false,
  "order": {
    "order_id": "O-00005678",
    "external_order_id": "SHOP-RC-20260930-0001",
    "status": "accepted",
    "status_title": "Recharge accepted",
    "poll_after_seconds": 12,
    "account": "12***00",
    "local_amount": "10.0000",
    "local_currency": "USD",
    "merchant_price": "9.6200",
    "merchant_currency": "USD",
    "...": "more fields"
  }
}
  • Guarda order.order_id.
  • El número de teléfono se devuelve enmascarado, nunca completo.

#Reintentar sin riesgo

external_order_id evita que recargues dos veces.

Si envíasRecibes
Un número de pedido nuevoHTTP 201. Un pedido nuevo. Se cobra de tu billetera.
El mismo número de pedido y la misma recargaHTTP 200 y "idempotent_replay": true. El pedido que ya existía. Sin cobro.
El mismo número de pedido pero otra recargaHTTP 400 en external_order_id. No pasa nada.

"La misma recarga" significa el mismo país, operador, tipo, monto y número de teléfono. CardV reconoce una repetición antes de revisar la cotización, así que un quote_token vencido igual devuelve el primer pedido.

  • Después de un tiempo agotado, un 5xx o una conexión perdida, envía el mismo cuerpo con el mismo número de pedido. Fírmalo de nuevo con una marca de tiempo y un nonce nuevos.
  • Nunca uses un número de pedido nuevo porque se perdió una respuesta. Eso puede recargar el teléfono dos veces.

#Consultar pedidos de recarga

GET/api/v1/recharge/orders/{order_id} devuelve un pedido. Puedes usar el ID de pedido de CardV (O-00005678).

JSON
{
  "order_id": "O-00005678",
  "external_order_id": "SHOP-RC-20260930-0001",
  "status": "processing",
  "order_status": "processing",
  "status_title": "Recharge processing",
  "status_message": "The recharge request is being processed. Delivery is not confirmed yet.",
  "next_step": "Keep this order open and wait for confirmation before placing another recharge.",
  "poll_after_seconds": 12,
  "country": "US",
  "operator": {"operator_key": "us-att", "name": "AT&T", "logo_url": ""},
  "subtype": "airtime",
  "account": "12***00",
  "local_amount": "10.0000",
  "local_currency": "USD",
  "merchant_price": "9.6200",
  "merchant_currency": "USD",
  "attempts": [{"attempt_no": 1, "status": "processing", "error_message": "", "submitted_at": "2026-09-30T08:16:02.511201Z", "completed_at": null}],
  "created_at": "2026-09-30T08:16:01.004211Z",
  "updated_at": "2026-09-30T08:16:02.611978Z",
  "...": "more fields"
}
  • Un ID de pedido desconocido, o un pedido de otra cuenta, devuelve HTTP 404.
  • status_title, status_message y next_step son textos en inglés que puedes mostrar a tu equipo.
  • poll_after_seconds indica cuánto esperar antes de la siguiente consulta. 0 significa que el pedido ya terminó.

#Listar pedidos de recarga

GET/api/v1/recharge/orders muestra tus pedidos de recarga, del más reciente al más antiguo.

HTTP
GET /api/v1/recharge/orders?status=processing&limit=50&offset=0
JSON
{"count": 3, "limit": 50, "results": ["..."]}
  • Filtros: status y search (ID de pedido de CardV, tu número de pedido o nombre del operador).
  • limit vale 20 por defecto. El máximo es 100. Los valores mayores se reducen a 100.
  • Un limit u offset negativo o que no sea un número devuelve HTTP 400.

#Estado

Text
accepted ──► processing ──► succeeded
                  │
                  ├──► manual_review ──► succeeded o refunded
                  │
                  └──► failed ──► refunded   (el dinero vuelve a tu billetera)
Estado¿Terminado?Qué hacer
acceptedNoEspera. Ya se cobró de la billetera, pero la recarga no ha empezado.
processingNoEspera. Puede tardar varios minutos. No vuelvas a hacer el pedido.
manual_reviewNoCardV está revisando el resultado con el operador. Espera.
succeededSíEl teléfono se recargó. Avísale a tu cliente.
failedTodavía noLa recarga no se completó. Espera a que pase a refunded.
refundedSíEl dinero ya volvió a tu billetera. Puedes hacer un pedido nuevo.
  • Consulta después de poll_after_seconds y luego cada vez con más espera: 30 s, 60 s y después cada 5 minutos. Respeta el límite de solicitudes.
  • Trata un estado desconocido como "todavía no terminado".
  • Mientras un pedido no haya terminado, no envíes otra recarga al mismo número con un número de pedido nuevo. Si el primero también se completa, el teléfono se recarga dos veces.

#Webhooks y reembolsos

Los pedidos de recarga envían los mismos webhooks que los demás pedidos: order.succeeded, order.failed y order.refunded. El webhook trae el ID de pedido de CardV y tu número de pedido, con una lista items vacía. Después de un webhook, consulta el pedido con GET/api/v1/recharge/orders/{order_id}.

Los reembolsos son automáticos. Cuando el operador confirma una falla, CardV devuelve el merchant_price completo a tu billetera y el pedido pasa a refunded. Puedes ver el reembolso en la página de transacciones del Portal. Una recarga que se completó no se puede reembolsar ni cancelar.

#Errores

Los errores siguen las Convenciones. Una cotización o un pedido rechazado devuelve HTTP 400 y no se cobra nada.

ClaveDóndeQué hacer
detailCotización, pedidoPaís, operador, tipo o monto no disponible. Revisa la lista de operadores.
amountCotización, pedidoNo es un número, es cero o está fuera de rango.
local_currencyCotización, pedidoNo es un código ISO 4217 de 3 letras.
accountPedidoFalta el número de teléfono.
quote_tokenPedidoFalta, venció, se modificó o no coincide. Revisa code y vuelve a cotizar.
balancePedidoAgrega fondos en el Portal.
riskPedidoLlegaste a un límite de pedidos. Contacta a CardV.
external_order_idPedidoYa se usó en otro pedido distinto. Ver Reintentar sin riesgo.
walletPedidoNo hay una billetera activa. Contacta a CardV.

Los errores de quote_token incluyen un code:

JSON
{"code": "quote_expired", "quote_token": "This quote expired. Refresh the price and confirm again."}
codeQué significa
quote_requiredNo se envió ningún quote_token.
quote_expiredTiene más de 300 segundos. Vuelve a cotizar.
quote_invalidSe modificó o es de otra recarga. Vuelve a cotizar.
price_changedTu precio cambió desde la cotización. Vuelve a cotizar y confirma el precio nuevo.

HTTP 403 significa un problema de credenciales, firma, IP o aprobación. Ver Autenticación.

¿Dudas sobre tu integración? Escribe a [email protected] con tu Merchant ID y el ID del pedido o de la solicitud.