# Librerías oficiales de Recarga.do Un solo archivo por lenguaje. Sin dependencias. Sin Composer, npm ni pip. Se copia al proyecto y se usa. | Lenguaje | Archivo | Requiere | |---|---|---| | PHP | [`php/recargado.php`](php/recargado.php) | PHP 7.2 o superior | | Node.js | [`node/recargado.js`](node/recargado.js) | Node 12 o superior | | Python | [`python/recargado.py`](python/recargado.py) | Python 3.6 o superior | Empiece por [EMPIECE-AQUI.md](../EMPIECE-AQUI.md). --- ## Por qué usarlas en vez de llamar a la API directamente La API es un REST normal y puede llamarla con `curl` si quiere. Estas librerías existen porque hay cuatro cosas que, hechas a mano, cuestan dinero de verdad, y que aquí ya vienen resueltas: **1. Un reintento no se convierte en un doble cobro.** Usted pasa su número de factura y la librería lo usa como clave de idempotencia. Si la red se cae y su sistema reintenta, Recarga.do reconoce la operación y devuelve la venta original en lugar de recargar otra vez. **2. Nada se da por fallido sin estar seguro.** Un corte de red, un timeout o un error del servidor no significan "no se recargó": significan "no se sabe". La librería los devuelve como `pendiente()`, con el dinero retenido, jamás como fallidos. Declarar fallida una recarga que sí se entregó es lo que lleva a recargar dos veces al mismo cliente. **3. Una recarga rechazada no tumba su aplicación.** Un número mal tecleado o un monto bajo el mínimo llegan como un resultado normal, con un mensaje en español listo para mostrarle al cajero. No como una excepción que hay que capturar. **4. Los reintentos son seguros por sí solos.** Reintenta cortes de red y errores 5xx, siempre con la misma clave. Nunca reintenta un fallo de credenciales: diez intentos fallidos bloquean la cuenta 15 minutos, así que un bucle de reintentos se bloquearía solo. Y usa 40 segundos de espera en las recargas, porque la API espera hasta 30 al proveedor y un timeout corto genera pendientes innecesarios. --- ## Lo que sí sigue siendo responsabilidad suya - **Pasar su número de factura** como cuarto parámetro, generado una vez por venta y guardado antes de llamar. La librería no puede inventarlo por usted. - **Manejar los tres desenlaces.** Si trata `pendiente()` como un fallo y vuelve a vender, cobra dos veces. - **Guardar el `api_secret` en su servidor.** Nunca en una app móvil ni en el JavaScript del navegador. --- ## Equivalencia entre lenguajes Los métodos se llaman igual en los tres. Python usa guion bajo por convención. | PHP y Node | Python | Qué hace | |---|---|---| | `recargar(...)` | `recargar(...)` | Vende una recarga | | `esperarResultado(id)` | `esperar_resultado(id)` | Espera a que se resuelva una pendiente | | `saldo()` / `disponible()` | `saldo()` / `disponible()` | Consulta el saldo | | `servicios()` | `servicios()` | Catálogo | | `categorias()` | `categorias()` | Categorías | | `paquetes(id)` | `paquetes(id)` | Paquetes de datos | | `transaccion(id)` | `transaccion(id)` | Detalle de una operación | | `transacciones([...])` | `transacciones(**filtros)` | Historial | | `recibo(id)` | `recibo(id)` | Recibo para imprimir | | `cuenta()` / `enPruebas()` | `cuenta()` / `en_pruebas()` | Datos de su empresa | Del resultado de una recarga: | PHP | Node | Python | |---|---|---| | `$v->exitosa()` | `v.exitosa()` | `v.exitosa()` | | `$v->pendiente()` | `v.pendiente()` | `v.pendiente()` | | `$v->fallida()` | `v.fallida()` | `v.fallida()` | | `$v->authCode` | `v.authCode` | `v.auth_code` | | `$v->transactionId` | `v.transactionId` | `v.transaction_id` | | `$v->requestId` | `v.requestId` | `v.request_id` | | `$v->mensaje` | `v.mensaje` | `v.mensaje` | | `$v->errorCode` | `v.errorCode` | `v.error_code` | --- ## Ajustes ```php $rd->tiempos(15, 40); // segundos: consultas, recargas $rd->reintentos(2); // 0 los desactiva ``` En Node y Python son propiedades: `rd.timeoutRecarga`, `rd.maxReintentos`, `rd.timeout_recarga`, `rd.max_reintentos`. --- ## Cuándo la librería sí lanza un error Solo cuando el problema no es de la venta sino de la configuración, y ninguna venta podría funcionar hasta arreglarlo: | Código | Qué revisar | |---|---| | `SIN_CREDENCIALES` | Falta la clave o el secreto | | `UNAUTHENTICATED` | Credenciales incorrectas o mal formadas | | `FORBIDDEN` | Token sin permiso, empresa suspendida o IP no autorizada | | `MONTO_INVALIDO` | El monto no es un número o no es mayor que cero | | `REFERENCIA_MUY_LARGA` | La referencia pasa de 128 caracteres | | `SIN_CONEXION` | No se pudo contactar con la API en una **consulta**. En una recarga esto nunca se lanza: llega como `pendiente()` | Todo lo demás —número inválido, saldo insuficiente, monto fuera de rango, rechazo de la telco— llega como un resultado `fallida()` con su mensaje. --- ## C# / .NET `csharp/RecargaDo.cs` — un solo archivo, sin NuGet. Requiere .NET 6 o superior. ```csharp using RecargaDoApi; var rd = new RecargaDo(clave, secreto); var venta = await rd.RecargarAsync(1, "8095551234", 100m, "FACTURA-00123"); if (venta.Exitosa) ImprimirRecibo(venta.AuthCode, venta.TransactionId); else if (venta.Pendiente) MostrarEnVerificacion(venta.TransactionId); else MostrarRechazo(venta.Mensaje); ``` --- ## Vender paquetes y facturas No todos los productos se compran indicando un monto. Los paquetes de datos y las facturas se venden en **dos pasos**: las opciones dependen del número o del contrato consultado, y el precio lo pone el operador, no el comercio. Cada servicio lo declara en el catálogo (`flujo` y `requiere_consulta`). ```php // PHP $c = $rd->consultar(50, '8094964444'); // paso 1 foreach ($c->opciones as $o) { /* el cajero elige */ } $venta = $rd->comprar(50, '8094964444', 'CL-M700', 'FACT-1042'); // paso 2 ``` ```js // Node const c = await rd.consultar(50, '8094964444'); const venta = await rd.comprar(50, '8094964444', c.opciones[0].opcion_id, 'FACT-1042'); ``` ```python # Python c = rd.consultar(50, '8094964444') venta = rd.comprar(50, '8094964444', c['opciones'][0]['opcion_id'], 'FACT-1042') ``` ```csharp // C# var c = await rd.ConsultarAsync(50, "8094964444"); var venta = await rd.ComprarAsync(50, "8094964444", c.Opciones[0].OpcionId, "FACT-1042"); ``` Si intenta vender un paquete indicando un monto, el SDK lanza `REQUIERE_CONSULTA` con el mensaje que explica qué hacer, en vez de dejarle un error críptico de la API. ### Dos campos del resultado que no debe descartar - **`transactionId`** — el número con el que se consulta y se **anula** la operación. Guárdelo siempre junto a la venta. - **`tokenRecarga()`** — en electricidad prepago, los dígitos que el cliente teclea en su medidor. Ese token *es* el producto: imprímalo en el recibo.