Saltar al contenido principal

Integración con contratos inteligentes en TRON

Esta guía explica cómo aceptar pagos cripto no custodiales en TRON con los contratos inteligentes de pago de Volet.com y TronWeb.

Los clientes conectan sus billeteras, aprueban las transacciones y pagan directamente a tu billetera receptora a través de un contrato inteligente. Los fondos no se acreditan en una cuenta de Volet.com.

Antes de continuar, revisa Integración con contratos inteligentes para ver una descripción general del flujo de pago.

La integración de un vistazo​

  1. Conecta la billetera del cliente y confirma que usa TRON Mainnet.
  2. Configura los contratos de pago y tu billetera receptora.
  3. Crea un pedido en tu backend antes de iniciar el pago.
  4. Llama a previewPayment() para calcular el monto total del pago.
  5. Llama a transfer() para iniciar el pago. En USDT, revisa la autorización de gasto (allowance) del token y solicita la aprobación si hace falta.
  6. Guarda el hash de la transacción de pago (txid) en el pedido.
  7. Verifica la transacción en tu backend con datos solidificados de la blockchain antes de marcar el pedido como pagado.

Antes de empezar​

Necesitarás:

  • Billetera del cliente: TronLink para pagos en el navegador.
  • Biblioteca: TronWeb para interactuar con billeteras y contratos inteligentes de TRON.
  • Billetera del comercio: una dirección de billetera de TRON que controles para recibir los pagos.
  • Backend y base de datos: para crear pedidos, guardar los datos de las transacciones y verificar los pagos.
  • Acceso a la blockchain: un nodo de TRON o un servicio alojado, como TronGrid, para obtener y verificar las transacciones.

Para conectar billeteras móviles, también necesitarás un Project ID de WalletConnect y una integración compatible con billeteras de TRON.

Redes y activos compatibles​

RedActivos compatibles
TRON MainnetTRX, USDT (TRC-20)

TRX y USDT usan contratos procesadores de pago distintos.

Los montos en TRX se expresan en SUN, la unidad mínima de TRX. Un TRX equivale a 1.000.000 SUN.

USDT TRC-20 usa 6 decimales. Un USDT equivale a 1.000.000 unidades base del token.

Los ejemplos de código de abajo contienen las direcciones de los procesadores y tokens de la configuración de la integración.

Las direcciones de los contratos, los decimales de los tokens y los parámetros de las funciones están en la Referencia de contratos inteligentes.

Antes de usar los ejemplos en producción, confirma con Volet.com las direcciones de los procesadores desplegados y la interfaz del contrato.

Paso 1. Conecta la billetera del cliente​

Usa TronLink para conectar la billetera de navegador del cliente.

<button id="connect-tron">Connect TronLink</button>

<script>
let tronWeb;

document.getElementById('connect-tron').addEventListener('click', async () => {
if (!window.tronLink || !window.tronWeb) {
alert('Install and unlock TronLink.');
return;
}

const result = await window.tronLink.request({
method: 'tron_requestAccounts'
});

if (result?.code !== 200) {
throw new Error('Wallet connection rejected');
}

tronWeb = window.tronWeb;

document.getElementById('connect-tron').textContent =
tronWeb.defaultAddress.base58;
});
</script>

El cliente debe conectarse a TRON Mainnet.

Verifica la red activa antes de enviar un pago. No identifiques la red solo por la URL del endpoint RPC, porque las billeteras pueden usar nodos personalizados.

Tu aplicación también debe detectar los cambios de cuenta y de red en la billetera y volver a validar la conexión antes de iniciar una transacción.

Paso 2. Configura los contratos de pago​

Agrega a tu aplicación la ABI del procesador de pagos y las funciones del token.

const PROCESSOR_ABI = [
{
name: 'previewPayment',
type: 'function',
stateMutability: 'view',
inputs: [
{ name: 'amount', type: 'uint256' },
{ name: 'buyerPaysCommission', type: 'bool' },
],
outputs: [
{ name: 'commission', type: 'uint256' },
{ name: 'totalPay', type: 'uint256' },
],
},
{
name: 'transfer',
type: 'function',
stateMutability: 'payable',
inputs: [
{ name: 'seller', type: 'address' },
{ name: 'amount', type: 'uint256' },
{ name: 'buyerPaysCommission', type: 'bool' },
{ name: 'optionalCommissionWallet', type: 'address' },
],
outputs: [],
},
];

const TOKEN_ABI = [
{
name: 'balanceOf',
type: 'function',
stateMutability: 'view',
inputs: [{ name: 'account', type: 'address' }],
outputs: [{ type: 'uint256' }],
},
{
name: 'allowance',
type: 'function',
stateMutability: 'view',
inputs: [
{ name: 'owner', type: 'address' },
{ name: 'spender', type: 'address' },
],
outputs: [{ type: 'uint256' }],
},
{
name: 'approve',
type: 'function',
stateMutability: 'nonpayable',
inputs: [
{ name: 'spender', type: 'address' },
{ name: 'amount', type: 'uint256' },
],
outputs: [{ type: 'bool' }],
},
];

const TRON_PROCESSOR_TRX = 'TSK9sgBTjvZa6x2yW1bXNWqvQ57WsVUQ3W';
const TRON_PROCESSOR_USDT = 'TDuE6VueULx5oivQd4MsYr9DEhNMgXNfTm';
const TRON_USDT = 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t';
const TRON_ZERO = 'T9yD14Nj9j7xAB4dbGeiX9h8unkKHxuWwb';

const TRON_FEE_LIMIT = 500_000_000;

El procesador de pagos ofrece dos funciones principales:

  • previewPayment(amount, buyerPaysCommission) calcula la comisión de procesamiento y el monto total que debe pagar el cliente.
  • transfer(seller, amount, buyerPaysCommission, optionalCommissionWallet) ejecuta el pago y distribuye los fondos según la configuración de comisiones del contrato.

El parámetro amount usa las unidades mínimas del activo: SUN para TRX o unidades base del token para USDT.

El parámetro buyerPaysCommission determina quién paga la comisión de procesamiento:

  • true: la comisión se suma al pago del cliente.
  • false: la comisión se descuenta del monto que recibe el comercio.

Usa TRON_ZERO en optionalCommissionWallet si no hay una billetera de comisión de socio.

Obtén siempre el monto total del pago de previewPayment(). No calcules la comisión de procesamiento manualmente.

El TRON_FEE_LIMIT del ejemplo se expresa en SUN y representa un límite máximo de comisión de 500 TRX, no la comisión esperada de la transacción. Antes de usarlo en producción, estima la Energy necesaria y revisa el límite de comisión adecuado.

Las definiciones completas de los parámetros están en la Referencia de contratos inteligentes.

Paso 3. Acepta TRX​

Los pagos en TRX nativo requieren una sola transacción en la billetera.

Usa previewPayment() para calcular la cantidad exacta de SUN que debe enviarse con la transacción.

async function payTrx(tronWeb, seller, humanAmount, buyerPaysCommission = true) {
if (!tronWeb.isAddress(seller)) {
throw new Error('Invalid TRON seller address');
}

const processor = tronWeb.contract(
PROCESSOR_ABI,
TRON_PROCESSOR_TRX
);

const amount = tronWeb.toSun(humanAmount).toString();

const preview = await processor.previewPayment(
amount,
buyerPaysCommission
).call();

return processor.transfer(
seller,
amount,
buyerPaysCommission,
TRON_ZERO
).send({
callValue: preview.totalPay.toString(),
feeLimit: TRON_FEE_LIMIT
});
}

El callValue de la transacción debe ser igual al totalPay que devuelve previewPayment().

El cliente también debe tener suficiente TRX para cubrir los recursos de la blockchain y las comisiones de la transacción.

La función devuelve el hash de la transacción de pago (txid). Guárdalo en el pedido existente y verifica el pago antes de entregar el pedido.

Paso 4. Acepta USDT​

Los pagos en USDT TRC-20 usan un contrato de token y pueden requerir una transacción de aprobación adicional.

Antes de hacer un pago, tu aplicación debe:

  1. Revisar el saldo de USDT del cliente con balanceOf().
  2. Revisar la autorización de gasto del procesador con allowance().
  3. Si la autorización es insuficiente, solicitar la aprobación con approve().
  4. Esperar a que se confirme la aprobación.
  5. Llamar a transfer() para ejecutar el pago.
async function payTronUsdt(tronWeb, seller, amountBaseUnits, buyerPaysCommission = true) {
if (!tronWeb.isAddress(seller)) {
throw new Error('Invalid TRON seller address');
}

if (!/^\d+$/.test(amountBaseUnits) || BigInt(amountBaseUnits) <= 0n) {
throw new Error('USDT amount must be a positive integer in base units');
}

const processor = tronWeb.contract(
PROCESSOR_ABI,
TRON_PROCESSOR_USDT
);

const token = tronWeb.contract(
TOKEN_ABI,
TRON_USDT
);

const owner = tronWeb.defaultAddress.base58;

const preview = await processor.previewPayment(
amountBaseUnits,
buyerPaysCommission
).call();

const total = BigInt(preview.totalPay.toString());

const balance = BigInt(
(await token.balanceOf(owner).call()).toString()
);

if (balance < total) {
throw new Error('Insufficient USDT');
}

const allowance = BigInt(
(await token.allowance(owner, TRON_PROCESSOR_USDT).call()).toString()
);

if (allowance < total) {
const approvalTxid = await token.approve(
TRON_PROCESSOR_USDT,
total.toString()
).send({ feeLimit: TRON_FEE_LIMIT });

return {
state: 'approval_pending',
approvalTxid
};
}

const paymentTxid = await processor.transfer(
seller,
amountBaseUnits,
buyerPaysCommission,
TRON_ZERO
).send({ feeLimit: TRON_FEE_LIMIT });

return {
state: 'payment_pending',
paymentTxid
};
}

Requisitos de aprobación de USDT​

Según la autorización existente, un pago en USDT puede requerir:

Autorización existenteTransacciones necesarias
SuficienteSolo el pago
InsuficienteAprobación + pago

Si se requiere una aprobación, la función devuelve approval_pending con el hash de la transacción de aprobación.

Espera a que se confirme la aprobación antes de volver a llamar a la función de pago. Cuando la autorización sea suficiente, la función envía el pago y devuelve payment_pending con el hash de la transacción de pago.

Importante:

  • Una transacción approve() no es un pago.
  • El cliente necesita suficiente TRX para cubrir los costos de Energy y Bandwidth, incluso cuando paga en USDT.
  • Los montos en USDT deben expresarse como enteros en unidades base del token. Evita cálculos de punto flotante como Math.round(parseFloat(amount) * 1e6).
  • Rastrea las transacciones de aprobación por separado de la transacción de pago final.

Paso 5. Guarda la transacción de pago​

Tu backend debe crear un pedido antes de que el cliente envíe un pago.

Cada pedido debe contener los valores esperados de:

  • ID del pedido
  • Red blockchain
  • Activo y monto
  • Billetera receptora del comercio (seller)
  • Contrato procesador de pagos aprobado
  • Billetera del comprador, si tu página de pago lo requiere
  • Configuración de la comisión de procesamiento (buyerPaysCommission)

Después de que el cliente envíe el pago:

  1. Obtén el hash de la transacción de pago (txid) que devuelve la billetera.
  2. Envía el hash a tu backend.
  3. Guárdalo junto con el ID del pedido y la red blockchain.
  4. Inicia la verificación del pago en el servidor.

Cada transacción de pago debe estar asociada a un solo pedido como máximo. Aplícalo con una restricción única (network, txid) en tu base de datos.

La función transfer() del contrato inteligente no acepta un ID de pedido. Tu aplicación debe mantener la relación entre el pedido y su transacción de pago.

Si un cliente reintenta un pago, conserva los intentos de pago por separado en lugar de sobrescribir registros de transacciones que aún pueden estar pendientes.

Paso 6. Verifica el pago​

La verificación del pago es obligatoria antes de completar un pedido.

Un hash de transacción devuelto solo indica que se envió una transacción. No prueba que el comercio haya recibido el pago correcto.

Tu backend debe, por su cuenta:

  1. Obtener la transacción y el recibo de ejecución de la blockchain.
  2. Confirmar la ejecución exitosa con un recibo solidificado de TRON.
  3. Verificar que la transacción usó el procesador de pagos esperado.
  4. Comparar el activo, la billetera del comercio, el monto y la llamada al contrato con el pedido.
  5. Confirmar que el comercio recibió los fondos esperados.
  6. Asegurarse de que la transacción no se haya usado ya para otro pedido.
  7. Marcar el pedido como pagado y entregarlo una sola vez.

Para obtener datos solidificados de la transacción, usa el endpoint del SolidityNode de TRON:

POST https://api.trongrid.io/walletsolidity/gettransactioninfobyid

Comprueba que receipt.result === 'SUCCESS' y luego verifica el cuerpo de la transacción y los datos reales del pago contra el pedido.

Un recibo exitoso no basta por sí solo. Una transacción puede ejecutarse correctamente sin pagar al comercio correcto o el monto correcto.

Si el servicio blockchain no está disponible temporalmente o la transacción aún no está solidificada, deja el pedido pendiente y vuelve a intentar la verificación.

Sigue Verificación de pagos y seguimiento de pedidos para la implementación completa en el backend, los requisitos de confirmación en TRON y la lista de verificación para producción.

También puedes consultar TRON confirmation semantics.

Opcional: admite billeteras móviles​

Para admitir billeteras móviles mediante WalletConnect, usa una integración compatible con billeteras de TRON, como @tronweb3/walletconnect-tron, junto con TronWeb.

Esto requiere un Project ID de WalletConnect y una integración capaz de crear transacciones de TRON, solicitar firmas a las billeteras móviles y difundir las transacciones firmadas.

El ejemplo para navegador que se describe abajo admite TronLink. No implementa el flujo de billeteras móviles con WalletConnect.

Ejemplo de integración​

El ejemplo example-tron.html muestra:

  • La conexión de TronLink.
  • El cálculo de los montos de pago con previewPayment().
  • El envío de pagos en TRX y USDT TRC-20.
  • La visualización de los hashes de transacción y el seguimiento de su estado.

Puedes abrir el ejemplo en el navegador o descargar el archivo HTML para estudiar y adaptar el código:

Las extensiones de billetera del navegador, como TronLink, pueden no funcionar correctamente con páginas abiertas directamente desde el disco (file://). Sirve el archivo descargado desde un servidor web local. Por ejemplo, ejecuta npx serve en la carpeta del archivo y abre la URL de localhost que muestra.

Prueba el ejemplo​

  1. Abre el ejemplo en vivo o sirve el archivo descargado localmente en un navegador con TronLink instalado.
  2. Selecciona Mainnet en TronLink y luego haz clic en Connect TronLink. La demo te avisa si la billetera está conectada a otra red.
  3. Selecciona el método de pago: TRX o USDT TRC-20.
  4. Ingresa la billetera receptora de TRON en Seller address.
  5. Ingresa el monto del pago y elige quién paga la comisión de procesamiento.
  6. Haz clic en Preview para ver la comisión de procesamiento y el monto total del pago. Luego haz clic en Pay y confirma la transacción en TronLink. Los pagos en USDT pueden requerir antes una aprobación del token aparte.
  7. El pedido y el hash de la transacción aparecen en la tabla Orders. La demo revisa el estado de la transacción durante un máximo de 5 minutos.

Antes de pagar:

  • Usa dos billeteras distintas. La dirección del vendedor debe ser diferente de la billetera que paga. De lo contrario, el contrato rechaza el pago con SellerIsBuyer.
  • Las transacciones usan fondos reales. El ejemplo viene con 1 TRX precargado. Revisa el monto del pago, la comisión de procesamiento y el costo de los recursos de la red TRON (Energy y Bandwidth) antes de confirmar.

Limitaciones de la demo: este ejemplo guarda el historial de transacciones en el localStorage del navegador y no es una página de pago de producción. Revisa el último recibo de la transacción con getTransactionInfo(), lo que no garantiza la solidificación en TRON. La ventana de seguimiento de 5 minutos es solo un límite de la demo, no un plazo de confirmación del pago. Implementa el proceso completo de verificación de pagos antes de aceptar pedidos de clientes.