Saltar al contenido
Ingresar

API para revendedores

Si tienes tu propia tienda, un bot o un panel, puedes conectarlo con Recargazo: tu sistema consulta el catálogo y compra recargas, gift cards y códigos de forma automática, con el saldo de tu cuenta y a los mismos precios de la tienda. Los precios están siempre en USDT.

Antes de empezar

  1. Crea tu cuenta y carga saldo: la API compra con ese saldo.
  2. En Mi cuenta → API crea una clave. La mostramos una sola vez: guárdala en tu servidor. Puedes tener hasta 3 claves activas y revocar cualquiera cuando quieras.
  3. Envía la clave en cada solicitud, en la cabecera Authorization. Úsala solo desde tu servidor: nunca en una página web ni en una app que se descarga, porque quien la tenga puede gastar tu saldo.
curl https://recargazo.com/api/v1/balance \
  -H "Authorization: Bearer rzk_tu_clave"

Todas las respuestas son JSON. Lo que pediste viene en data; las listas traen además pagination. Los montos son texto con dos decimales ("10.01"), para que ningún redondeo los cambie.

Qué puedes hacer

  • GET /balance: tu saldo disponible.
  • GET /products: el catálogo a la venta, por páginas. Filtros: type (topup, gift_card o game_key), q (nombre), page y per_page (hasta 100). Con include=offers cada producto trae sus paquetes con precio, para copiar el catálogo completo en pocas llamadas.
  • GET /products/{id}: un producto, por su id o por su slug. Trae los datos que pide del comprador (fields) y sus paquetes (offers) con el precio y el stock recién confirmados.
  • POST /products/{id}/validate: pregunta al juego si el jugador existe antes de comprar, en los productos con validates_player: true. Responde valid (con player_name), invalid o unknown.
  • POST /orders: compra un paquete.
  • GET /orders/{id}: un pedido, con sus códigos cuando ya está entregado.
  • GET /orders: tus pedidos, del más nuevo al más viejo. Filtros: reference, status, page y per_page. Las listas no traen códigos.

Comprar

Envía el offer_id del paquete. En las recargas directas agrega en fields los datos que el producto pide (por ejemplo player_id); en gift cards y códigos puedes pedir varias unidades con quantity.

curl -X POST https://recargazo.com/api/v1/orders \
  -H "Authorization: Bearer rzk_tu_clave" \
  -H "Content-Type: application/json" \
  -d '{
    "offer_id": "7c1d1a0e-2b7f-4a53-9d5e-1f0c2b3a4d5e",
    "quantity": 1,
    "fields": { "player_id": "123456789" },
    "reference": "pedido-1001",
    "max_unit_price": "0.85"
  }'

La respuesta es el pedido. El monto se descuenta de tu saldo en ese momento; si el pedido no se puede entregar, vuelve a tu saldo completo.

{
  "data": {
    "id": "0b8f3c52-6c1e-4f0a-9f57-2f6f1f1f7a10",
    "reference": "pedido-1001",
    "source": "api",
    "status": "completed",
    "type": "gift_card",
    "product": "Roblox (US)",
    "offer": "10 USD",
    "quantity": 1,
    "unit_price": "10.01",
    "total": "10.01",
    "currency": "USDT",
    "fields": {},
    "player_name": null,
    "codes": ["XXXX-XXXX-XXXX"],
    "fail_reason": null,
    "created_at": "2026-09-19T15:04:05.000Z",
    "completed_at": "2026-09-19T15:04:09.000Z"
  }
}
  • reference es el número de pedido de tu sistema (hasta 64 letras, números, puntos y guiones). Envíalo siempre: si repites una solicitud con la misma referencia —porque se cortó la conexión y no sabes si llegó—, no compramos de nuevo: respondemos 200 con el pedido que ya existe. Un pedido nuevo responde 201.
  • max_unit_price es opcional: si el precio actual es mayor, el pedido se rechaza con price_changed y no se cobra nada. Sin él, el pedido se compra al precio del momento, que viene en la respuesta.

Estados de un pedido

  • processing: lo estamos entregando. Espera el webhook o vuelve a consultar el pedido.
  • completed: entregado. En gift cards y códigos, codes trae lo que compraste.
  • refunded: no se pudo entregar y el monto volvió a tu saldo. fail_reason dice por qué.
  • review: no pudimos confirmar la entrega y lo está revisando una persona. Termina en completed o en refunded; no lo compres de nuevo mientras tanto.

Los pedidos hechos por la API no envían correos: tu sistema se entera por la respuesta, por el webhook o consultando el pedido. Igual los ves en Mis pedidos.

Errores

Cuando algo no sale bien, el código HTTP lo indica y el cuerpo trae un código fijo para tu programa y un mensaje para quien lea el registro:

{
  "error": {
    "code": "insufficient_funds",
    "message": "Saldo insuficiente. Recarga tu saldo para completar la compra."
  }
}
  • 400 invalid_request: falta un dato o tiene un formato que no es.
  • 401 unauthorized: falta la clave, no existe o fue revocada.
  • 402 insufficient_funds: no alcanza el saldo.
  • 403 account_blocked: la cuenta está suspendida.
  • 404 not_found: ese producto o ese pedido no existe (o no es tuyo).
  • 409 product_unavailable, out_of_stock, price_changed, reference_conflict: el paquete ya no se vende, se agotó, subió de precio, o esa referencia ya la usaste en un pedido de otro paquete.
  • 422 invalid_fields, invalid_player, invalid_quantity: faltan datos del jugador, el juego no reconoce ese jugador, o la cantidad está fuera de lo que el paquete permite.
  • 429 rate_limited: demasiadas solicitudes. La cabecera Retry-After dice cuántos segundos esperar.
  • 503 store_paused, price_unconfirmed: la tienda está en mantenimiento o no pudimos confirmar el precio. Reintenta más tarde con la misma reference.

Límites por cuenta: 300 consultas, 60 pedidos y 30 verificaciones de jugador por minuto. Si necesitas más, escríbenos.

Webhooks

En lugar de preguntar una y otra vez por un pedido, guarda en Mi cuenta → API la dirección https:// de tu servidor y te avisamos ahí cuando un pedido hecho por la API cambie:

  • order.completed: se entregó (con sus códigos).
  • order.refunded: no se pudo entregar y el monto volvió a tu saldo.
  • order.review: pasó a revisión manual.
  • ping: la prueba que envías desde tu cuenta.
{
  "id": "evt_5d0c1b9e-3f0a-4d53-8a55-0c9d0e6a7b21",
  "type": "order.completed",
  "created_at": "2026-09-19T15:04:09.000Z",
  "data": {
    "order": { "id": "0b8f3c52-…", "reference": "pedido-1001", "status": "completed", "codes": ["XXXX-XXXX-XXXX"], "…": "…" }
  }
}

Cada aviso es un POST con JSON y estas cabeceras: X-Recargazo-Event (el tipo), X-Recargazo-Delivery (el id del aviso) y X-Recargazo-Signature (la firma). Responde con un código 2xx en menos de 10 segundos. Si tu servidor no responde o responde otra cosa, reintentamos hasta 8 veces con esperas cada vez más largas (1, 4, 9, 16… minutos, unas dos horas y media en total). Los reintentos llevan el mismo id, y dos avisos pueden llegar en otro orden: guíate por data.order.status. No seguimos redirecciones.

Como los avisos llevan los códigos de tus pedidos, cuando la dirección cambia te avisamos por correo.

Comprobar la firma

Cualquiera puede enviar un POST a tu dirección; la firma prueba que el aviso es nuestro. La cabecera se ve así: t=1789830249,v1=5f2c…. t es la hora del envío (segundos Unix) y v1 es el HMAC-SHA256, en hexadecimal, del texto t + "." + cuerpo con el secreto de tu webhook (whsec_…). Calcula lo mismo en tu servidor, compara, y rechaza los avisos con más de 5 minutos.

import crypto from "node:crypto";
import express from "express";

const app = express();
const SECRET = process.env.RECARGAZO_WEBHOOK_SECRET;

// La firma cubre el cuerpo tal como llegó: léelo sin convertirlo.
app.post("/webhooks/recargazo", express.raw({ type: "application/json" }), (req, res) => {
  const header = req.get("X-Recargazo-Signature") ?? "";
  const parts = Object.fromEntries(header.split(",").map((part) => part.split("=")));
  const expected = crypto.createHmac("sha256", SECRET).update(parts.t + "." + req.body).digest("hex");

  const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
  const valid =
    typeof parts.v1 === "string" &&
    parts.v1.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));
  if (!fresh || !valid) return res.sendStatus(400);

  const event = JSON.parse(req.body);
  // event.id se repite en los reintentos: si ya lo procesaste, responde 200 y no hagas nada más.
  if (event.type === "order.completed") entregarAlCliente(event.data.order);
  res.sendStatus(200);
});
<?php
$secret = getenv('RECARGAZO_WEBHOOK_SECRET');
$body = file_get_contents('php://input'); // el cuerpo tal como llegó
$header = $_SERVER['HTTP_X_RECARGAZO_SIGNATURE'] ?? '';
parse_str(str_replace(',', '&', $header), $parts);

$expected = hash_hmac('sha256', ($parts['t'] ?? '') . '.' . $body, $secret);
$fresh = abs(time() - (int) ($parts['t'] ?? 0)) < 300;
if (!$fresh || !hash_equals($expected, $parts['v1'] ?? '')) {
    http_response_code(400);
    exit;
}

$event = json_decode($body, true);
// $event['id'] se repite en los reintentos: si ya lo procesaste, responde 200 y no hagas nada más.
if ($event['type'] === 'order.completed') {
    entregar_al_cliente($event['data']['order']);
}
http_response_code(200);

Cuida tu clave

  • Guárdala como una contraseña: en variables de entorno de tu servidor, nunca en el código ni en el navegador.
  • Si crees que alguien más la tiene, revócala en Mi cuenta → API: deja de funcionar al instante.
  • Usa una clave distinta para cada sistema: así puedes cortar uno sin detener los demás.
  • Si te llega un correo de que cambió la dirección de tu webhook y no fuiste tú, cambia tu contraseña, quita el webhook y revoca tus claves.

¿No encontraste tu respuesta?

Escríbenos desde tu cuenta: te respondemos ahí y te avisamos por correo. Si es sobre un pedido o una recarga, empieza desde ese pedido o recarga para que veamos los detalles.