conexachain.com ↗
API en producción · pay.conexachain.com

Acepta pagos cripto de ocho redes, con una sola integración.

ConexaPay genera una dirección de pago única por factura, verifica cada transacción directo en la cadena, cobra tu fee del mismo pago, y te avisa por webhook — sin que tengas que correr un nodo de nada.

Ethereum BNB Chain Arbitrum Polygon Conexa Chain USDT / USDC Tron + USDT-Tron (próximamente)
Toda cuenta de comerciante se firma con una wallet — no hay usuario/contraseña. Necesitas la Conexa Wallet: descárgala en conexachain.com, o úsala directo en el navegador desde wallet.conexachain.com — ahí mismo, en Ajustes → ConexaPay, puedes solicitar tu cuenta o activar el sandbox sin escribir una sola línea de código.
Empezar

Cómo funciona

Cada vez que tu sistema crea una factura, ConexaPay reserva una dirección de pago única para esa factura específica — nunca se reutiliza, generada por derivación determinística desde una sola semilla maestra. Tu cliente paga a esa dirección, en la red y moneda que elegiste, y en cuanto la transacción se confirma en la cadena:

Cliente paga Se detecta en la cadena Se descuenta el fee Te llega el neto Webhook firmado
  • Marcamos la factura como pagado
  • Movemos el monto neto (ya con el fee descontado) a tu wallet de liquidación
  • Te avisamos por webhook, con reintentos automáticos si tu servidor no responde

Todo se valida directo en la blockchain — nunca confiamos en lo que diga un cliente sobre si pagó o no. No hay "modo de prueba" simulado: cada factura que crees es real, sobre la red real.

Cada comerciante opera de forma completamente aislada: tus facturas, tu wallet de liquidación, y tus credenciales nunca se mezclan con las de otro comerciante — ni siquiera se puede consultar si una factura ajena existe.
Empezar

Guía rápida

De cero a tu primer pago recibido, en cinco pasos.

0
Consigue una Conexa Wallet

Descárgala en conexachain.com, o entra directo desde el navegador en wallet.conexachain.com — la necesitas para firmar tu solicitud, no hay usuario/contraseña.

1
Solicita tu cuenta de comerciante

Firmas con tu wallet, mandas tu nombre y tu wallet de liquidación — ver Solicitar una cuenta. Desde la wallet misma: Ajustes → ConexaPay.

2
Espera la aprobación

El operador de la plataforma la revisa y aprueba desde su panel.

3
Reclama tus credenciales

Tu llave y secreto se generan en ese momento — guárdalos, el secreto no se vuelve a mostrar.

4
Crea tu primera factura

POST /v1/invoices con la red, la moneda, y el monto — ver Crear una factura.

5
Configura tu webhook (opcional pero recomendado)

Así no dependes de estar consultando el estado tú mismo — ver Webhooks.

No hace falta instalar ningún SDK — toda la API es HTTP estándar, cualquier cliente HTTP de cualquier lenguaje funciona.
Empezar

Así se ve para tu cliente

Con los datos que te devuelve la API (dirección, QR, monto, tiempo restante) puedes armar tu propia pantalla de cobro. Esto es un ejemplo de cómo se vería:

Tienda Ejemplo Esperando pago
49.90 USDT
Arbitrum One
0x71C7656EC7ab88b098defB751B7401B5f6d8976F
Expira en 28:41

Envía exactamente el monto indicado, en Arbitrum One. Esta página se actualiza sola en cuanto detectemos el pago.

Cómo se construye

  1. Creas la factura con POST /v1/invoices
  2. Muestras direccionPago y el resultado de GET /v1/invoices/:id/qr
  3. Consultas GET /v1/invoices/:id/publico cada pocos segundos, o esperas el webhook
  4. Cuando estado cambia a pagado, muestras la confirmación
Empezar

Cómo funciona el fee

El fee es un porcentaje del mismo pago, en la misma moneda que usó tu cliente — no se convierte a ninguna otra moneda. Si alguien te paga 100 USDT y tu fee es 0.5%, tú recibes 99.5 USDT, directo en tu wallet de liquidación. No hay ninguna moneda intermedia, ni bóveda que tengas que prefondear.

  • El fee por defecto es configurable por el operador de la plataforma, y puede tener un valor especial distinto para tu cuenta
  • Se descuenta automáticamente en cada factura pagada — nunca tienes que hacer nada para que esto pase
  • El campo montoNeto de cada factura es exactamente lo que llegó a tu wallet; montoFee es lo que se cobró
  • Puedes ver tu fee actual con GET /v1/merchant/me, campo feePct
No necesitas mantener ningún saldo prefondeado para que tus pagos se procesen.

Ejemplo

Cliente paga100.00 USDT
Fee (0.5%)0.50 USDT
Tú recibes (montoNeto)99.50 USDT
Empezar

Redes y monedas soportadas

Consulta GET /v1/networks en cualquier momento para ver la lista actualizada — puede cambiar si el operador habilita o pausa una red.

RedCódigoNativoTokens
EthereumethereumETHUSDT, USDC
BNB Smart ChainbscBNBUSDT, USDC
Arbitrum OnearbitrumETHUSDT, USDC
PolygonpolygonMATICUSDT, USDC
Conexa ChainconexaCONEXACualquier token listado y activado en Market
TrontronTRXUSDT (próximamente)
cURL
Copiar
curl https://pay.conexachain.com/v1/networks
JSON
Copiar
{
  "success": true,
  "data": {
    "ethereum": { "chainId": 1, "nombre": "Ethereum", "nativo": "ETH", "monedas": ["ETH","USDT","USDC"] },
    "arbitrum": { "chainId": 42161, "nombre": "Arbitrum One", "...": "..." }
  }
}
Empezar

Pagar con un token listado en Market

Además de CONEXA, tus clientes pueden pagar con cualquier token que ya esté listado y activo en el Market de la wallet — no solo el token nativo de la cadena. Esto es exclusivo de red: "conexa": cada token vive solo en la cadena donde se listó, así que este método no aplica en Ethereum, BSC, Arbitrum, ni Polygon — ahí solo se paga con el nativo, USDT, o USDC de esa red.

Para poder usarlo como moneda en una factura, el token debe estar en estado aprobado en el listado, y su creador ya debe haberlo activado (configurado precio inicial y liquidez) — mientras tanto, ese símbolo no aparece como opción válida.

Cómo saber qué tokens están disponibles ahora mismo

La lista es dinámica — cambia según lo que se vaya listando y activando. Consulta GET /v1/networks y mira el arreglo monedas de conexa: ahí vas a ver CONEXA más el símbolo de cada token activo en ese momento.

JSON
Copiar
{
  "success": true,
  "data": {
    "conexa": {
      "chainId": 7797,
      "nombre": "Conexa Chain",
      "nativo": "CONEXA",
      "monedas": ["CONEXA", "MITOKEN", "OTROTOKEN"]
    }
  }
}

Crear una factura pagable en un token listado

Exactamente igual que cualquier otra factura — solo cambia el valor de moneda por el símbolo del token.

cURL
Copiar
curl -X POST https://pay.conexachain.com/v1/invoices \
  -u cxpk_tu_llave:cxsk_tu_secreto \
  -H "Content-Type: application/json" \
  -d '{
    "red": "conexa",
    "moneda": "MITOKEN",
    "monto": 250,
    "referencia": "ORDEN-8821"
  }'

Si el símbolo que mandas no está listado, no fue aprobado, o su creador aún no lo activó, la API responde con bad_request — igual que cualquier otra moneda no soportada.

Cuenta de comerciante
POST/v1/merchant-requestsFirma de wallet

Solicitar una cuenta

No hay registro por formulario web con usuario/contraseña — tu identidad es tu propia wallet. Cada solicitud, y cada acción de cuenta, se firma con tu wallet (igual que firmar una transacción), y el servidor recupera la dirección desde la firma misma, nunca confía en lo que le digas.

¿No tienes una Conexa Wallet todavía? Descárgala en conexachain.com, o entra por el navegador en wallet.conexachain.com. Desde ahí, en Ajustes → ConexaPay, puedes hacer todo esto con unos toques, sin escribir código.
ParámetroDescripción
direccionrequeridoTu dirección de wallet
mensajerequeridoEl texto exacto que firmaste, incluyendo ts: con la marca de tiempo
firmarequeridoLa firma de ese mensaje, con tu wallet
nombrerequeridoNombre de tu negocio
walletLiquidacionrequeridoDirección donde quieres recibir tus pagos ya netos
webhookUrlopcionalPuedes configurarlo después también
El mensaje firmado expira a los 5 minutos — evita que una firma capturada se pueda reusar más tarde.
Node.js Python PHP
Copiar
// Con ethers.js -- tu propio signer de wallet
const mensaje = `Solicitar comerciante ts:${Date.now()}`;
const firma = await signer.signMessage(mensaje);

await fetch('https://pay.conexachain.com/v1/merchant-requests', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    direccion: await signer.getAddress(),
    mensaje, firma,
    nombre: 'Mi Negocio',
    walletLiquidacion: '0xTuWalletAqui...',
  })
});
# Con web3.py -- tu propia cuenta local
from eth_account.messages import encode_defunct
import time, requests

mensaje = f"Solicitar comerciante ts:{int(time.time()*1000)}"
firma = account.sign_message(encode_defunct(text=mensaje)).signature.hex()

requests.post("https://pay.conexachain.com/v1/merchant-requests", json={
    "direccion": account.address,
    "mensaje": mensaje, "firma": firma,
    "nombre": "Mi Negocio",
    "walletLiquidacion": "0xTuWalletAqui...",
})
// Con web3.php o similar para firmar
$mensaje = "Solicitar comerciante ts:" . (time() * 1000);
$firma = $wallet->signMessage($mensaje);

$ch = curl_init("https://pay.conexachain.com/v1/merchant-requests");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Content-Type: application/json"]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
    "direccion" => $direccion, "mensaje" => $mensaje, "firma" => $firma,
    "nombre" => "Mi Negocio", "walletLiquidacion" => "0xTuWalletAqui...",
]));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$respuesta = curl_exec($ch);
Cuenta de comerciante
POST/v1/merchant-requests/statusFirma de wallet

Consultar el estado de tu solicitud

Mismo patrón de firma que solicitar. El campo estado de la respuesta puede ser pendiente, aprobado, o rechazado.

JSON
Copiar
{
  "success": true,
  "data": {
    "id": "req_a1b2c3d4",
    "estado": "aprobado",
    "merchantId": null,
    "notasAdmin": ""
  }
}

merchantId en null significa: ya te aprobaron, pero todavía no has reclamado tus credenciales.

Cuenta de comerciante
POST/v1/merchant-requests/claimFirma de wallet

Reclamar tus credenciales

Una vez aprobada tu solicitud, tu llave y tu secreto se generan justo en este momento — nunca antes. Esta es la única vez que verás el secreto en texto plano; guárdalo de inmediato, en un gestor de contraseñas o similar.

Si pierdes el secreto, no hay forma de recuperarlo — solo de generar uno nuevo (ver Perdí mis credenciales), que invalida el anterior de inmediato.
JSON
Copiar
{
  "success": true,
  "data": {
    "merchantId": "merch_9f8e7d6c",
    "apiKey": "cxpk_...",
    "apiSecret": "cxsk_..."
  }
}
Cuenta de comerciante
POST/v1/merchant-requests/rotarFirma de wallet

Perdí mis credenciales

Si ya reclamaste antes pero perdiste el secreto (o es un dispositivo nuevo), puedes generar unas credenciales nuevas tú mismo, sin depender del operador — con la misma firma de wallet que usaste para solicitar la cuenta. El secreto anterior se invalida al instante.

Sandbox (pruebas)

Dos formas de probar, sin dinero real

Antes de pasar a producción, ConexaPay te da dos maneras de probar tu integración — puedes usar una, o las dos, según lo que necesites verificar.

Simulación puraSandbox on-chain real
Qué pruebaTu código: cómo manejas la respuesta de la API y el webhookEl proceso completo, con una transacción real en la cadena
Red/monedaCualquiera de las soportadasSolo Conexa Chain, con el token oficial de pruebas
Cómo se "paga"Llamas un endpoint que marca la factura pagadaMandas de verdad la ficha de prueba a la dirección
Necesita fichasNoSí — del grifo, gratis
El sandbox se activa al instante, sin que el operador tenga que aprobar nada — no hay dinero real de por medio.
Sandbox (pruebas)
POST/v1/sandbox/activarFirma de wallet

Activar sandbox

Mismo patrón de firma que solicitar una cuenta real, pero instantáneo. Tus credenciales llevan el prefijo cxpk_test_ / cxsk_test_ — inconfundibles con las de producción.

Node.js
Copiar
const mensaje = `Activar sandbox ts:${Date.now()}`;
const firma = await signer.signMessage(mensaje);

const r = await fetch('https://pay.conexachain.com/v1/sandbox/activar', {
  method: 'POST', headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ direccion: await signer.getAddress(), mensaje, firma, nombre: 'Mi Negocio (prueba)' })
});
const { apiKey, apiSecret } = (await r.json()).data; // guardalos, el secreto no se repite

Si pierdes el secreto, POST /v1/sandbox/rotar (mismo patrón de firma) genera uno nuevo.

Sandbox (pruebas)
POST/v1/invoices/:id/simular-pagoRequiere llave de prueba

Simulación pura

Con tus credenciales de sandbox, crea una factura exactamente igual que en producción — red y moneda pueden ser cualquiera de las soportadas. En vez de esperar un pago real, llama este endpoint para marcarla pagada al instante. El fee se calcula con la misma fórmula que producción, y tu webhook se dispara idéntico (con "sandbox": true en los datos, para que nunca lo confundas con un pago real).

cURL
Copiar
curl -X POST https://pay.conexachain.com/v1/invoices/inv_xxxxx/simular-pago \
  -u cxpk_test_...:cxsk_test_...

Opcionalmente manda {"montoRecibido": 30} en el cuerpo para simular un pago parcial y ver cómo reacciona tu código a eso.

Sandbox (pruebas)

Sandbox on-chain real

Cuando creas una factura con red: "conexa" y como moneda usas el símbolo del token oficial de pruebas (consulta cuál es con GET /v1/sandbox/token-prueba), la factura recibe una dirección real — el mismo vigilante que procesa pagos de producción la detecta, cobra el fee, hace el barrido, y dispara tu webhook. Todo real, excepto que el token no tiene valor de mercado.

Estas facturas no se pueden simular — solo se pagan de verdad, mandando la ficha a la dirección.
Sandbox (pruebas)
POST/v1/sandbox/token-prueba/solicitarFirma de wallet

El grifo de fichas

Pide fichas de prueba gratis, directo a tu wallet — mismo patrón de firma. Hay un tiempo de espera entre solicitudes por wallet (consultalo en GET /v1/sandbox/token-prueba, campo cooldownHoras).

Autenticación

Llave + secreto

Una vez que tienes tus credenciales, el resto de la API usa HTTP Basic Auth — tu llave pública y tu llave secreta, codificadas en base64, en la cabecera Authorization. Es el mismo estándar que usa cualquier librería HTTP de cualquier lenguaje, sin nada especial que instalar.

cURL Node.js Python PHP Ruby Go
Copiar
curl https://pay.conexachain.com/v1/merchant/me \
  -u cxpk_tu_llave_publica:cxsk_tu_llave_secreta
const auth = Buffer.from(`${apiKey}:${apiSecret}`).toString('base64');

const res = await fetch('https://pay.conexachain.com/v1/merchant/me', {
  headers: { 'Authorization': `Basic ${auth}` }
});
import requests

r = requests.get(
    "https://pay.conexachain.com/v1/merchant/me",
    auth=("cxpk_tu_llave_publica", "cxsk_tu_llave_secreta")
)
$ch = curl_init("https://pay.conexachain.com/v1/merchant/me");
curl_setopt($ch, CURLOPT_USERPWD, "cxpk_tu_llave:cxsk_tu_secreto");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$respuesta = curl_exec($ch);
require 'net/http'

uri = URI("https://pay.conexachain.com/v1/merchant/me")
req = Net::HTTP::Get.new(uri)
req.basic_auth("cxpk_tu_llave", "cxsk_tu_secreto")
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
req, _ := http.NewRequest("GET", "https://pay.conexachain.com/v1/merchant/me", nil)
req.SetBasicAuth("cxpk_tu_llave", "cxsk_tu_secreto")
resp, _ := http.DefaultClient.Do(req)
Autenticación
GET/v1/merchant/meRequiere llave

Ver mi cuenta

Devuelve tus datos públicos.

merchantIdTu identificador único
nombreNombre de tu negocio
apiKeyTu llave pública
walletLiquidacionDirección donde recibes tus pagos
webhookUrlTu URL de webhook configurada (o null)
feePctTu fee especial, si tienes uno asignado (o null = usa el estándar de la plataforma)
activoSi tu cuenta puede seguir procesando pagos
creadoEnTimestamp de creación (ms)
Autenticación
PATCH/v1/merchant/webhookRequiere llave

Configurar mi webhook

cURL
Copiar
curl -X PATCH https://pay.conexachain.com/v1/merchant/webhook \
  -u cxpk_...:cxsk_... \
  -H "Content-Type: application/json" \
  -d '{"webhookUrl": "https://tunegocio.com/webhook"}'
Facturas

El objeto Factura

Esta es la forma completa que verás en todas las respuestas de facturas — creación, consulta, listado, y en el webhook.

facturaIdIdentificador único, ej. inv_a1b2c3d4
redLa red elegida
monedaLa moneda elegida
montoSolicitadoLo que pediste al crearla
direccionPagoLa dirección única generada para esta factura
estadopendiente · pagado · expirado
montoRecibidoLo que realmente llegó (null hasta que se pague)
montoNetoLo que te llegó a ti, ya con el fee descontado
montoFeeLo que se cobró de fee, mismo activo que el pago
txHashHash de la transacción de pago del cliente
referenciaTu identificador propio, si lo mandaste
metadataCualquier dato adicional que hayas guardado
creadoEn / expiraEn / pagadoEnTimestamps en milisegundos
Facturas
POST/v1/invoicesRequiere llave

Crear una factura

Genera una dirección de pago única. La factura expira sola si nadie paga dentro del tiempo configurado (30 minutos por defecto).

ParámetroTipoDescripción
redrequeridostringUna de las redes soportadas — ver Redes y monedas
monedarequeridostringLa moneda nativa de esa red, o un token soportado
montorequeridonumberMonto exacto a cobrar, en la moneda elegida
referenciaopcionalstringTu propio identificador — se te devuelve tal cual en el webhook
metadataopcionalobjectCualquier dato adicional tuyo, se guarda junto a la factura
cURL Node.js Python PHP Ruby Go
Copiar
curl -X POST https://pay.conexachain.com/v1/invoices \
  -u cxpk_tu_llave:cxsk_tu_secreto \
  -H "Content-Type: application/json" \
  -d '{
    "red": "arbitrum",
    "moneda": "USDT",
    "monto": 49.90,
    "referencia": "ORDEN-4471"
  }'
const factura = await fetch('https://pay.conexachain.com/v1/invoices', {
  method: 'POST',
  headers: {
    'Authorization': `Basic ${auth}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    red: 'arbitrum', moneda: 'USDT', monto: 49.90,
    referencia: 'ORDEN-4471'
  })
}).then(r => r.json());
r = requests.post(
    "https://pay.conexachain.com/v1/invoices",
    auth=("cxpk_tu_llave", "cxsk_tu_secreto"),
    json={"red": "arbitrum", "moneda": "USDT", "monto": 49.90,
          "referencia": "ORDEN-4471"}
)
$ch = curl_init("https://pay.conexachain.com/v1/invoices");
curl_setopt($ch, CURLOPT_USERPWD, "cxpk_tu_llave:cxsk_tu_secreto");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Content-Type: application/json"]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
    "red" => "arbitrum", "moneda" => "USDT", "monto" => 49.90,
    "referencia" => "ORDEN-4471",
]));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$respuesta = curl_exec($ch);
require 'net/http'
require 'json'

uri = URI("https://pay.conexachain.com/v1/invoices")
req = Net::HTTP::Post.new(uri, "Content-Type" => "application/json")
req.basic_auth("cxpk_tu_llave", "cxsk_tu_secreto")
req.body = { red: "arbitrum", moneda: "USDT", monto: 49.90, referencia: "ORDEN-4471" }.to_json
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
body, _ := json.Marshal(map[string]interface{}{
    "red": "arbitrum", "moneda": "USDT", "monto": 49.90, "referencia": "ORDEN-4471",
})
req, _ := http.NewRequest("POST", "https://pay.conexachain.com/v1/invoices", bytes.NewBuffer(body))
req.SetBasicAuth("cxpk_tu_llave", "cxsk_tu_secreto")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)

Respuesta

JSON
Copiar
{
  "success": true,
  "data": {
    "facturaId": "inv_a1b2c3d4e5f6",
    "red": "arbitrum",
    "moneda": "USDT",
    "montoSolicitado": 49.9,
    "direccionPago": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
    "estado": "pendiente",
    "montoRecibido": null,
    "montoNeto": null,
    "montoFee": null,
    "referencia": "ORDEN-4471",
    "creadoEn": 1786150000000,
    "expiraEn": 1786151800000
  }
}
Facturas
GET/v1/invoices/:idRequiere llave

Consultar una factura

Usa esto para hacer polling de respaldo — aunque el webhook es la forma principal de enterarte, siempre puedes preguntar el estado directo. Si la factura no es tuya, o no existe, la respuesta es idéntica en ambos casos (404) — para no filtrar de quién es cada factura.

cURL
Copiar
curl https://pay.conexachain.com/v1/invoices/inv_a1b2c3d4e5f6 \
  -u cxpk_tu_llave:cxsk_tu_secreto
Facturas
GET/v1/invoicesRequiere llave

Listar tus facturas

Devuelve solo tus propias facturas, nunca las de otro comerciante.

ParámetroDescripción
estadoopcionalFiltra por pendiente, pagado, o expirado
limiteopcionalCuántas devolver, por defecto 50
cURL
Copiar
curl "https://pay.conexachain.com/v1/invoices?estado=pagado&limite=20" \
  -u cxpk_tu_llave:cxsk_tu_secreto
Facturas
GET/v1/invoices/:id/publicoPúblico

Estado público de una factura

Sin autenticación, a propósito — para que tu página de checkout pueda mostrarle el estado al comprador (que no tiene, ni debe tener, tus credenciales de API). Devuelve solo lo esencial: red, moneda, monto, dirección de pago, estado, y cuándo expira.

Facturas
GET/v1/invoices/:id/qrPúblico

Código QR de la factura

Devuelve un PNG en base64 con la dirección de pago — listo para mostrar en una pantalla de checkout o una factura impresa.

HTML
Copiar
<!-- La respuesta trae "qr" como data URL, listo para usar -->
<img src="data:image/png;base64,iVBORw0KG..." alt="Pagar factura" />
Eventos

Webhooks

Configura tu URL desde PATCH /v1/merchant/webhook. En cuanto una factura se paga, te mandamos un POST con el evento.

Cuerpo del evento

JSON
Copiar
{
  "evento": "factura.pagada",
  "datos": {
    "facturaId": "inv_a1b2c3d4e5f6",
    "red": "arbitrum",
    "moneda": "USDT",
    "montoSolicitado": 49.9,
    "montoRecibido": 49.9,
    "montoNeto": 49.65,
    "montoFee": 0.25,
    "txHashPago": "0x8f3a...c21e",
    "txBarrido": "0x2b7c...9a01",
    "referencia": "ORDEN-4471"
  },
  "timestamp": 1786150200000
}
Eventos

Eventos disponibles

EventoCuándo se dispara
factura.pagadaUna factura se confirmó pagada en la cadena y ya se barrió a tu wallet

Este es el único evento disponible hoy — el diseño del sistema ya soporta agregar más adelante sin romper lo existente.

Eventos

Verificar la firma del webhook

Cada entrega incluye la cabecera X-ConexaPay-Signature — un HMAC-SHA256 del cuerpo exacto, firmado con tu webhookSecret.

Nunca proceses un webhook sin verificar la firma primero — cualquiera podría mandarte un POST falso a esa URL simulando un pago.
Node.js Python PHP
Copiar
const crypto = require('crypto');

function esFirmaValida(cuerpoCrudo, firmaRecibida, webhookSecret) {
  const esperada = crypto
    .createHmac('sha256', webhookSecret)
    .update(cuerpoCrudo)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(firmaRecibida), Buffer.from(esperada)
  );
}
import hmac, hashlib

def es_firma_valida(cuerpo_crudo, firma_recibida, webhook_secret):
    esperada = hmac.new(
        webhook_secret.encode(), cuerpo_crudo, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(firma_recibida, esperada)
function esFirmaValida($cuerpoCrudo, $firmaRecibida, $webhookSecret) {
    $esperada = hash_hmac('sha256', $cuerpoCrudo, $webhookSecret);
    return hash_equals($esperada, $firmaRecibida);
}
Eventos

Reintentos

Si tu servidor no responde con un código 2xx, reintentamos hasta 6 veces, con espera creciente:

IntentoEspera
1Inmediato
21 minuto
35 minutos
430 minutos
52 horas
66 horas

Después del último intento, el evento queda registrado como agotado — la factura sigue pagada y consultable por la API en todo momento, aunque el webhook no se haya podido entregar.

Referencia

Manejo de errores

Toda respuesta con error sigue el mismo formato:

JSON
Copiar
{
  "success": false,
  "error": {
    "code": "unauthorized",
    "message": "Credenciales invalidas"
  }
}
HTTPCódigoSignifica
401unauthorizedLlave/secreto inválidos, o firma inválida/expirada (más de 5 min)
404not_foundLa factura no existe, o no es tuya
400bad_requestFaltan parámetros, algún valor es inválido, o la acción no aplica en el estado actual
Referencia

Seguridad

  • Toda validación de pagos ocurre en la cadena — nunca se confía en un aviso del cliente
  • Cada factura tiene una dirección de pago que nunca se reutiliza
  • Tu llave secreta nunca se guarda en texto plano de nuestro lado — solo su huella
  • Tus datos están completamente aislados de los de cualquier otro comerciante
  • Tu identidad de comerciante está atada a tu wallet, no a una contraseña
  • Los webhooks van firmados con HMAC
Referencia

Preguntas frecuentes

¿Qué pasa si mi cliente paga menos de lo pedido?
La factura queda pendiente hasta que llegue el monto completo, o hasta que expire — no hay pagos parciales aceptados automáticamente hoy.
¿Puedo cambiar mi wallet de liquidación?
Sí, contacta al operador de la plataforma para actualizarla.
¿Hay ambiente de pruebas (sandbox)?
No por ahora — cada factura que crees es real, sobre la red real. Ten esto en cuenta al integrar.
¿Qué pasa si mi servidor de webhook está caído?
Reintentamos automáticamente hasta 6 veces en las siguientes horas -- ver Reintentos. Mientras tanto, la factura sigue consultable normal por la API.
¿Cómo sé cuál es mi fee actual?
Consulta GET /v1/merchant/me, campo feePct. Si es null, usas el estándar de la plataforma.
Referencia

Qué sigue

Tron y USDT sobre Tron están en desarrollo — arquitecturalmente distinto a las redes EVM (usa su propio SDK), así que llega en su propia fase, no mezclado con lo que ya existe.