Gateway off-site para Drupal Commerce que permite pagos con USDC/USDT en StableChain y Celo usando direcciones HD wallet (BIP-44) y sidecar Node.js.
- Drupal 10+ con Commerce 2.x
- Docker compose (Wodby o similar)
- Node.js 20+ para el sidecar (incluido en compose.yml como
stablepay-listener)
-
Habilitar el módulo:
make drush "en commerce_stablepay" -
Configurar el gateway en Drupal:
- Ir a
/admin/commerce/config/payment-gateways - Agregar nuevo gateway → "StablePay Crypto"
- Configurar RPC URLs y direcciones de contratos USDC/USDT
- Ir a
-
Configurar webhook secret para asegurar que solo el sidecar puede notificar pagos:
openssl rand -hex 32
Agregar el resultado en ambas ubicaciones:
En
settings.php:putenv('STABLEPAY_WEBHOOK_SECRET=tu-secret-aqui');
En el
.envdel sidecar (misma raíz de Drupal):WEBHOOK_SECRET=tu-secret-aqui
El sidecar envía este secret como header
X-Webhook-Secretal notificar pagos. El módulo lo verifica antes de procesar cualquier notificación.
Si el sidecor corre como servicio systemd en el mismo server:
-
Configurar la URL del sidecar en
settings.php:putenv('STABLEPAY_SIDECAR_URL=http://127.0.0.1:3002');
-
Configurar
.envdel sidecar (en la raíz de Drupal):DRUPAL_BASE_URL=http://127.0.0.1 STABLEPAY_MASTER_XPUB=xpub... STABLEPAY_DB_PATH=./pending.db PORT=3002
-
Instalar el servicio systemd (ver
node/stablepay-listener.service):cp node/stablepay-listener.service /etc/systemd/system/ systemctl daemon-reload systemctl enable --now stablepay-listener
El gateway usa XPUB (watch-only) para derivar direcciones. El sidecar deriva addresses desde el xpub sin tener acceso a la private key.
const { ethers } = require('ethers');
const mnemonic = ethers.Mnemonic.fromPhrase("tu mnemonic aqui");
const accountNode = ethers.HDNodeWallet.fromMnemonic(mnemonic, "m/44'/60'/0'/0");
console.log(accountNode.neuter().extendedKey);STABLEPAY_MASTER_XPUB=xpub6DyUKdwoLWmUJ4Tn9Bbsdtx7B5Ws18mEN19e5HT52ikE53FiUheSQXrZUNPovqfyKmw4579A1Mm3GXXKM39N64uooBfJ4tNAzFsEbodRTx4| Network | Chain ID | Gas Token | Tokens |
|---|---|---|---|
| StableChain | 988 |
USDT | USDT |
| Celo | 42220 |
CELO (CIP-64) | USDC, USDT |
| Token | Network | Address |
|---|---|---|
| USDC | Celo | 0xcebA9300f2b948710d2653dD7B07f33A8B32118C |
| USDT | Celo | 0x48065fbbe25f71c9282ddf5e1cd6d6a887483d5e |
| USDT | StableChain | 0x779Ded0c9e1022225f8E0630b35a9b54bE713736 |
El sidecar (node/index.js) es un servidor Express que corre en el contenedor stablepay-listener y se encarga de monitorear la blockchain, gestionar direcciones HD wallet y notificar a Drupal.
Al arrancar, el sidecar:
- Inicializa la DB — sql.js persistida en
/data/pending.db(volumen Dockerstablepay_data) - Lee
STABLEPAY_MASTER_XPUBde.env - Obtiene configuración desde Drupal vía
GET /stablepay/payment/config(networks, notify_url) - Si hay xpub configurado, restaura direcciones pendientes desde la DB y reanuda el monitoreo
- Inicia monitoreo WebSocket + polling para las networks configuradas
- WebSocket: se suscribe a eventos
Transferdel contrato ERC-20 en cada network. - Polling (cada 15s): fallback universal. Verifica balance vía
eth_call, busca logs para txHash, notifica a Drupal.
| Método | Ruta | Descripción |
|---|---|---|
POST |
/derive |
Deriva address para una orden y la registra para monitoreo |
GET |
/balance/:orderId |
Consulta balance on-chain (stateless) |
GET |
/get-tx/:orderId |
Busca último txHash de Transfer hacia la address derivada |
GET |
/health |
Estado del sidecar |
Cuando el sidecar detecta un pago (WS o polling), hace un POST a {notify_url}:
{ "order_id": 123, "tx_hash": "0x...", "amount": "12.50", "currency": "USDC" }- Usuario selecciona StablePay en checkout → redirige a
/stablepay/payment/{order} build()deriva dirección HD única vía sidecar y la pasa al template + JS- JS llama a
deriveAddress()para registrar monitoreo - Usuario envía USDC/USDT a la dirección desde su wallet
- Sidecar detecta el pago (WS o polling) y notifica a Drupal vía webhook
onNotify()crea paymentcompletedy transiciona orden
Los fondos se barran desde las addresses derivadas usando la CLI tool stablepay-sweep. El sidecar NO tiene acceso a la private key.
cd tools/stablepay-sweep
# Scan addresses por balance
node index.js scan --from 215 --to 230
# Sweep por order ID
node index.js sweep-api --id 228
# Sweep rango de orders
node index.js sweep-api --from 218 --to 228
⚠️ Seguridad: No ejecutes la sweep tool en el servidor de producción ni en ningún equipo permanentemente conectado a internet. La herramienta necesita acceso a la frase mnemónica de 12/24 palabras de la billetera (ej. MetaMask, TrustWallet) para derivar las private keys y barrer fondos. Ejecútala en una máquina offline/air-gapped (sin conexión a internet) y mantén la frase asegurada fuera del server.