Recibe y envía USDT y USDC
Asigna a cada cliente una dirección de depósito de stablecoins, cobra en USDT o USDC en el checkout y envíalos a billeteras externas. Cada moneda se liquida en sí misma: nunca se convierte a bolívares ni a la otra moneda.
Cómo funciona USDT
- USDT tiene su propio saldo (
GET /v1/crypto/balance), separado de tu saldo en VES. 1 USDT se trata como 1 USD. - Los USDT que recibes quedan retenidos 24 horas antes de poder enviarlos en un payout: el saldo los reporta como
held(heldUsdt) connextReleaseAt, yavailablelos excluye. Ver Saldos, retenciones y payouts. - Recibe con una dirección de depósito fija por cliente, o con el método
usdten sesiones de checkout y enlaces de pago. - Envía con
POST /v1/crypto/payouts. Los payouts dentro de tus límites salen automáticamente; los más grandes se revisan primero. - Cada movimiento se notifica con webhooks firmados:
payment.completedal recibir,payout.completed/payout.failedal enviar. - ¿Quieres USDT a partir de los bolívares que cobras? Convierte tu saldo en VES con
POST /v1/conversions: ver Convertir VES a USDT.
Precios
| Movimiento | Comisión |
|---|---|
| USDT recibido (depósitos y checkout) | 2,5% + 0,50 USDT, descontado del monto recibido (nunca más que el monto) |
| Payout de USDT a una dirección externa | La comisión de red de la red elegida + 0,25 USDT (p. ej. 1,00 USDT en BEP20, Polygon y Solana; ver GET /v1/crypto/networks) |
| Payout de USDT a una de tus propias direcciones de depósito | Gratis: se liquida dentro de VEXPay, no se envía nada en la cadena |
Ejemplo: un cliente envía 40,00 USDT → comisión 1,50 → se acreditan 38,50 USDT.
Redes
Lista las redes habilitadas, su comisión de payout y si un payout de cierto monto se puede enviar ahora. Prefiere las redes más baratas (BEP20, Polygon) cuando tu cliente pueda usarlas; TRC20 es la más cara para enviar.
curl "${API_URL}/v1/crypto/networks?amountUsdt=50.00" \
--header "x-api-key: ${VEXPAY_API_KEY}"| network | Cadena |
|---|---|
| TRC20 | Tron |
| BEP20 | BNB Smart Chain |
| POLYGON | Polygon |
| SOL | Solana |
| TON | TON (requiere tag / memo) |
| ARB1 | Arbitrum One |
Paso 01
Direcciones de depósito
customerRef es tu id del cliente. Llamar de nuevo para el mismo cliente y red devuelve la misma dirección, así que puedes llamarlo cada vez que muestres la pantalla de depósito.
curl -X POST "${API_URL}/v1/crypto/deposit-addresses" \
--header "x-api-key: ${VEXPAY_API_KEY}" \
--header "content-type: application/json" \
--data '{ "customerRef": "user_123", "network": "BEP20" }'const address = await vexpay.crypto.depositAddresses.create({
customerRef: 'user_123',
network: 'BEP20',
});
// address.address, address.tag (solo TON)address = vexpay.crypto.deposit_addresses.create({"customerRef": "user_123", "network": "BEP20"})Cuando llega USDT recibes payment.completed con method: "USDT", customerRef, amountUsdt, feeUsdt, network y txHashes. Acredita a tu cliente amountUsdt − feeUsdt, una sola vez por paymentId.
Paso 02
USDT en el checkout
Agrega usdt a methods al crear una sesión de checkout (o deja methods vacío para ofrecer todo lo que acepta tu cuenta). El comprador elige una red en la página alojada y paga el monto exacto; la sesión pasa a paid cuando llega el pago.
curl -X POST "${API_URL}/v1/checkout/sessions" \
--header "x-api-key: ${VEXPAY_API_KEY}" \
--header "content-type: application/json" \
--data '{ "amountUsd": 25.00, "methods": ["usdt", "c2p"], "reference": "order-1042" }'El webhook payment.completed de un pago de sesión incluye checkoutSession: { id, reference, metadata }: identifica tu pedido con checkoutSession.reference (el externalRef del pago pertenece a la sesión). El plugin de WooCommerce de VEXPay ya ofrece USDT así cuando está habilitado en tu cuenta.
Paso 03
Payouts de USDT
El monto + la comisión se reservan de tu saldo USDT disponible de inmediato (los USDT que siguen en la retención de 24 h no se pueden enviar). idempotencyKey es obligatorio: repetir la misma solicitud devuelve el mismo payout (200); reutilizar la clave con otros datos responde 409 idempotency_key_reused.
curl -X POST "${API_URL}/v1/crypto/payouts" \
--header "x-api-key: ${VEXPAY_API_KEY}" \
--header "content-type: application/json" \
--data '{
"network": "POLYGON",
"address": "0x9f3c4e1b2a7d6c5e4f3a2b1c0d9e8f7a6b5c4d3e",
"amountUsdt": "50.00",
"customerRef": "user_123",
"idempotencyKey": "withdrawal_8812"
}'const payout = await vexpay.crypto.payouts.create({
network: 'POLYGON',
address: '0x9f3c…',
amountUsdt: '50.00',
idempotencyKey: 'withdrawal_8812',
});| status | Significado |
|---|---|
| pending | Reservado; en revisión (sobre tus límites) o esperando capacidad en la red |
| processing | Enviado a la red |
| completed | Entregado: txHash está presente (internal: true si se liquidó dentro de VEXPay) |
| failed | No se envió: el monto + la comisión vuelven a tu saldo; ver failureReason |
Errores: 422 insufficient_balance (con held, heldUsdt y nextReleaseAt cuando parte de tu saldo sigue retenido), 422 amount_below_minimum, 400 invalid_address, 400 unsupported_network.
Paso 04
USDC
USDC funciona exactamente igual que USDT: mismos endpoints, comisiones, webhooks y revisión de payouts. Envía currency: "USDC" (en el body) o ?currency=USDC (en la query); sin ese parámetro, todos los endpoints siguen trabajando en USDT, sin cambios.
- Redes: solo POLYGON y BASE. BASE solo admite USDC; una solicitud de USDT en BASE responde 400
unsupported_network. - Saldo separado: USDC tiene su propio saldo (
GET /v1/crypto/balance?currency=USDC). Los payouts de USDC nunca descuentan de USDT, ni los de USDT de USDC.GET /v1/crypto/balancesdevuelve ambos. - Se habilita por separado: USDC debe activarse en tu cuenta por su cuenta. Tener USDT no habilita USDC; mientras no esté activo, las solicitudes de USDC responden 403
method_not_allowed. - Misma comisión de cobro que USDT (2,5% + 0,50, cobrada en USDC). Los payouts de USDC cuestan 0,75 USDC en BASE y Polygon (0,50 de comisión de red + 0,25);
GET /v1/crypto/networks?currency=USDClos lista. - Los límites de payout son compartidos: los payouts de USDT y USDC cuentan juntos, 1:1 en USD, contra los mismos límites por payout y diarios.
- Cualquier otro valor de
currencyresponde 400unsupported_currency.
curl -X POST "${API_URL}/v1/crypto/deposit-addresses" \
--header "x-api-key: ${VEXPAY_API_KEY}" \
--header "content-type: application/json" \
--data '{ "customerRef": "user_123", "network": "POLYGON", "currency": "USDC" }'curl -X POST "${API_URL}/v1/crypto/payouts" \
--header "x-api-key: ${VEXPAY_API_KEY}" \
--header "content-type: application/json" \
--data '{
"currency": "USDC",
"network": "BASE",
"address": "0x9f3c4e1b2a7d6c5e4f3a2b1c0d9e8f7a6b5c4d3e",
"amount": "50.00",
"idempotencyKey": "withdrawal_8813"
}'const usdc = await vexpay.crypto.balance.retrieve({ currency: 'USDC' });
const all = await vexpay.crypto.balances.list();
await vexpay.crypto.payouts.create({
currency: 'USDC',
network: 'BASE',
address: '0x9f3c…',
amount: '50.00',
idempotencyKey: 'withdrawal_8813',
});Usa amount para los payouts de USDC. amountUsdt solo se acepta como alias en payouts de USDT; enviar ambos con valores distintos responde 400 invalid_data. Todas las respuestas cripto incluyen ahora currency, amount y fee para ambas monedas; los campos *Usdt solo se devuelven para USDT, con su significado de siempre. La currency de un payout forma parte de la verificación de idempotencia: reutilizar un idempotencyKey con otra moneda responde 409 idempotency_key_reused.
En el checkout, agrega usdc a methods (o deja methods vacío). Cuando ambas stablecoins están disponibles, el comprador elige la moneda y luego la red. Un link o sesión limitado a ["usdt"] nunca ofrece USDC.
Webhooks
| Evento | Cuándo | Campos clave |
|---|---|---|
| payment.completed | USDT o USDC recibido (dirección de depósito o checkout) | method y currency ("USDT" / "USDC"), amount, fee, network, txHashes, customerRef, underpaid; USDT además amountUsdt, feeUsdt |
| payout.completed | Payout entregado | payoutId, currency, amount, fee, txHash, internal, idempotencyKey; USDT además amountUsdt, feeUsdt |
| payout.failed | Payout no enviado; reembolsado | payoutId, currency, failureReason, idempotencyKey |
Pruebas
Con una clave de prueba, la página alojada de USDT muestra botones de prueba (confirmar / cancelar / incompleto) para simular cada resultado sin fondos reales. Los pagos de prueba nunca acreditan una cuenta en vivo. Los retiros de prueba se completan al instante con payout.completed (sin txHash): no se envía nada on-chain y no aplican límites ni aprobación.