DEV Community

Daniel Ioni
Daniel Ioni

Posted on

πŸ” MyZubster Gateway: Deep Dive Guide

πŸ” MyZubster Gateway: Deep Dive Guide

Complete technical guide to the Monero payment engine – from architecture to production
πŸ“Œ What is MyZubster Gateway?

MyZubster Gateway is the payment engine of the MyZubster ecosystem. It handles all interactions with the Monero blockchain:

βœ… Generates unique subaddresses for each order

βœ… Monitors the blockchain for incoming payments (real-time)

βœ… Sends webhooks to the Marketplace when payments are confirmed

βœ… Manages transaction history and payment status
Enter fullscreen mode Exit fullscreen mode

Think of it as the bridge between MyZubster and the Monero network.
🧩 Architecture Overview
text

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ MyZubster Gateway β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ Express │────▢│ MongoDB │────▢│ Monero RPC Client β”‚ β”‚
β”‚ β”‚ Server β”‚ β”‚ (Database) β”‚ β”‚ (monero-javascript)β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”‚ β”‚ β”‚ β”‚
β”‚ β–Ό β–Ό β–Ό β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ Routes β”‚ β”‚ Models β”‚ β”‚ Monero Wallet β”‚ β”‚
β”‚ β”‚ /api/* β”‚ β”‚ (Mongo) β”‚ β”‚ (Stagenet/Mainnet)β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”‚
β”‚ β–Ό β–Ό β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ Webhooks ──────┼──▢│ Marketplace β”‚ β”‚
β”‚ β”‚ (Payment Conf) β”‚ β”‚ (port 4000) β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ› οΈ Tech Stack
Layer Technology Purpose
Server Node.js + Express RESTful API server
Database MongoDB Store payments, transactions, and address mappings
Monero Client monero-javascript Interact with Monero blockchain
Webhooks Axios Notify Marketplace of payment confirmations
Security Helmet, CORS, JWT API authentication and security
πŸš€ How the Gateway Works
The Payment Flow
text

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Buyer β”‚ β”‚ Marketplaceβ”‚ β”‚ Gateway β”‚ β”‚ Monero β”‚
β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ Blockchain β”‚
β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
β”‚ β”‚ β”‚ β”‚
β”‚ 1. Creates β”‚ β”‚ β”‚
β”‚ Order with β”‚ β”‚ β”‚
β”‚ Skill ID β”‚ β”‚ β”‚
│─────────────────▢│ β”‚ β”‚
β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ 2. Request β”‚ β”‚
β”‚ β”‚ Payment β”‚ β”‚
β”‚ β”‚ (POST /initiate) β”‚ β”‚
β”‚ │──────────────────▢│ β”‚
β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ β”‚ 3. Generate β”‚
β”‚ β”‚ β”‚ Subaddress β”‚
β”‚ β”‚ │──────────────────▢│
β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ β”‚ 4. Return β”‚
β”‚ β”‚ β”‚ Subaddress β”‚
β”‚ β”‚ 5. Return │◀──────────────────│
β”‚ 6. Show β”‚ Subaddress β”‚ β”‚
β”‚ Address │◀──────────────────│ β”‚
│◀─────────────────│ β”‚ β”‚
β”‚ β”‚ β”‚ β”‚
β”‚ 7. Send β”‚ β”‚ β”‚
β”‚ Monero to β”‚ β”‚ β”‚
β”‚ Subaddress β”‚ β”‚ β”‚
│─────────────────────────────────────────────────────────▢│
β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ β”‚ 8. Monitor β”‚
β”‚ β”‚ β”‚ Blockchain β”‚
β”‚ β”‚ β”‚ (every 60s) β”‚
β”‚ β”‚ │──────────────────▢│
β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ β”‚ 9. Payment β”‚
β”‚ β”‚ β”‚ Detected β”‚
β”‚ β”‚ │◀──────────────────│
β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ 10. Webhook β”‚ β”‚
β”‚ β”‚ (Payment Conf) β”‚ β”‚
β”‚ │◀──────────────────│ β”‚
β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ 11. Update β”‚ β”‚
β”‚ β”‚ Order Status β”‚ β”‚
β”‚ 12. Order β”‚ to "paid" β”‚ β”‚
β”‚ Completed │◀──────────────────│ β”‚
│◀─────────────────│ β”‚ β”‚
β”‚ β”‚ β”‚ β”‚

πŸ“‚ Complete File Structure
text

gateway/
β”œβ”€β”€ server.js # Entry point
β”œβ”€β”€ .env # Environment variables
β”œβ”€β”€ package.json # Dependencies
β”œβ”€β”€ models/
β”‚ β”œβ”€β”€ index.js # Database connection
β”‚ β”œβ”€β”€ User.js # User model
β”‚ β”œβ”€β”€ Order.js # Order model (tracks payments)
β”‚ β”œβ”€β”€ Skill.js # Skill model
β”‚ β”œβ”€β”€ Payment.js # Payment model (subaddress + status)
β”‚ └── Transaction.js # Transaction history
β”œβ”€β”€ routes/
β”‚ β”œβ”€β”€ auth.js # Authentication routes
β”‚ β”œβ”€β”€ payments.js # Payment endpoints
β”‚ β”œβ”€β”€ webhook.js # Webhook handling
β”‚ └── health.js # Health check
β”œβ”€β”€ services/
β”‚ β”œβ”€β”€ monero.js # Monero RPC client
β”‚ β”œβ”€β”€ paymentMonitor.js # Background payment monitoring
β”‚ └── webhookSender.js # Webhook delivery
β”œβ”€β”€ middleware/
β”‚ β”œβ”€β”€ auth.js # JWT verification
β”‚ └── rateLimiter.js # Rate limiting
β”œβ”€β”€ config/
β”‚ └── database.js # Database configuration
└── tests/
β”œβ”€β”€ payments.test.js
β”œβ”€β”€ webhook.test.js
└── monero.test.js

πŸ”§ Complete Code Walkthrough

  1. Server Configuration (server.js)

File: gateway/server.js
javascript

// server.js - MyZubster Gateway
// Backend principale per la piattaforma MyZubster

const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
const dotenv = require('dotenv');

// Carica le variabili d'ambiente
dotenv.config();

// Inizializza Express
const app = express();
const PORT = process.env.PORT || 3000;

// ============================================
// MIDDLEWARE
// ============================================
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// ============================================
// IMPORT MODELLI
// ============================================
require('./models/User');
require('./models/Order');
require('./models/Skill');
require('./models/Offer');
require('./models/Request');
require('./models/Transaction');
require('./models/Review');

// ============================================
// IMPORT ROUTE
// ============================================
const authRoutes = require('./routes/auth');
const skillRoutes = require('./routes/skills');
const offerRoutes = require('./routes/offers');
const requestRoutes = require('./routes/requests');
const orderRoutes = require('./routes/orders');
const paymentRoutes = require('./routes/payments');
const transactionRoutes = require('./routes/transactions');
const reviewRoutes = require('./routes/reviews');

// ============================================
// ROTTE API
// ============================================
app.use('/api/auth', authRoutes);
app.use('/api/skills', skillRoutes);
app.use('/api/offers', offerRoutes);
app.use('/api/requests', requestRoutes);
app.use('/api/orders', orderRoutes);
app.use('/api/payments', paymentRoutes);
app.use('/api/transactions', transactionRoutes);
app.use('/api/reviews', reviewRoutes);

// ============================================
// ENDPOINT PER GENERARE UN INDIRIZZO MONERO (MOCK)
// ============================================
app.post('/api/payments/initiate', async (req, res) => {
try {
console.log('πŸ“ Richiesta pagamento ricevuta:', req.body);

const { orderId, amount, currency } = req.body;

if (!orderId || !amount) {
  return res.status(400).json({ error: 'orderId e amount obbligatori' });
}

// Mock: genera un indirizzo Monero fittizio per il test
const mockAddress = '8A1B2C3D4E5F6G7H8I9J0K' + Math.random().toString(36).substring(2, 8);

res.json({
  success: true,
  paymentId: 'pay_' + Date.now(),
  moneroAddress: mockAddress,
  moneroAmount: amount,
  addressIndex: Math.floor(Math.random() * 1000),
  network: 'testnet',
  status: 'pending',
  orderId: orderId
});
Enter fullscreen mode Exit fullscreen mode

} catch (error) {
console.error('❌ Errore creazione pagamento:', error);
res.status(500).json({ error: 'Errore creazione pagamento' });
}
});

// ============================================
// ROTTA DI TEST
// ============================================
app.get('/api/health', (req, res) => {
res.json({
status: 'OK',
message: 'MyZubster Gateway is running!',
timestamp: new Date().toISOString(),
uptime: process.uptime()
});
});

// ============================================
// CONNESSIONE AL DATABASE
// ============================================
const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://localhost:27017/myzubster';

mongoose.connect(MONGODB_URI)
.then(() => {
console.log('βœ… Connesso a MongoDB');
console.log(πŸ“¦ Database: ${MONGODB_URI});
})
.catch((err) => {
console.error('❌ Errore connessione MongoDB:', err);
process.exit(1);
});

// ============================================
// AVVIO DEL SERVER
// ============================================
app.listen(PORT, () => {
console.log(πŸš€ Server avviato sulla porta ${PORT});
console.log(🌐 URL: http://localhost:${PORT});
console.log(πŸ” Health check: http://localhost:${PORT}/api/health);
});

// Gestione errori non catturati
process.on('unhandledRejection', (err) => {
console.error('❌ Unhandled Rejection:', err);
});

module.exports = app;

  1. Payment Model (models/Payment.js)

File: gateway/models/Payment.js
javascript

const mongoose = require('mongoose');

const PaymentSchema = new mongoose.Schema({
orderId: {
type: String,
required: true,
index: true
},
subaddress: {
type: String,
required: true,
unique: true,
index: true
},
amount: {
type: Number,
required: true
},
currency: {
type: String,
default: 'USD'
},
moneroAmount: {
type: Number,
required: true
},
addressIndex: {
type: Number,
required: true
},
txHash: {
type: String,
default: null
},
status: {
type: String,
enum: ['pending', 'confirmed', 'expired'],
default: 'pending'
},
confirmations: {
type: Number,
default: 0
},
amountReceived: {
type: Number,
default: 0
},
network: {
type: String,
enum: ['testnet', 'mainnet'],
default: 'testnet'
},
createdAt: {
type: Date,
default: Date.now
},
confirmedAt: {
type: Date,
default: null
},
webhookDelivered: {
type: Boolean,
default: false
}
});

module.exports = mongoose.model('Payment', PaymentSchema);

  1. Payment Routes (routes/payments.js)

File: gateway/routes/payments.js
javascript

const express = require('express');
const router = express.Router();
const Payment = require('../models/Payment');
const { createMoneroClient } = require('../services/monero');
const { sendWebhook } = require('../services/webhookSender');

// ============================================
// POST /api/payments/initiate
// Crea un nuovo pagamento
// ============================================
router.post('/initiate', async (req, res) => {
try {
const { orderId, amount, currency } = req.body;

if (!orderId || !amount) {
  return res.status(400).json({ error: 'orderId e amount obbligatori' });
}

// 1. Connetti al wallet Monero
const wallet = await createMoneroClient();

// 2. Genera un nuovo subaddress
const subaddress = await wallet.createSubaddress();

// 3. Salva il pagamento nel database
const payment = await Payment.create({
  orderId,
  subaddress: subaddress.address,
  amount,
  moneroAmount: amount, // Convert in base al tasso di cambio (semplificato)
  addressIndex: subaddress.index,
  network: process.env.MONERO_NETWORK || 'testnet',
  status: 'pending'
});

// 4. Rispondi al client
res.status(201).json({
  success: true,
  paymentId: payment._id,
  moneroAddress: payment.subaddress,
  moneroAmount: payment.moneroAmount,
  addressIndex: payment.addressIndex,
  network: payment.network,
  status: payment.status
});

// 5. Inizia il monitoraggio del pagamento in background
startPaymentMonitoring(payment._id);
Enter fullscreen mode Exit fullscreen mode

} catch (error) {
console.error('❌ Errore initiate payment:', error);
res.status(500).json({ error: 'Errore creazione pagamento' });
}
});

// ============================================
// GET /api/payments/status/:paymentId
// Verifica lo stato di un pagamento
// ============================================
router.get('/status/:paymentId', async (req, res) => {
try {
const payment = await Payment.findById(req.params.paymentId);
if (!payment) {
return res.status(404).json({ error: 'Pagamento non trovato' });
}

res.json({
  paymentId: payment._id,
  status: payment.status,
  confirmations: payment.confirmations,
  amountReceived: payment.amountReceived,
  txHash: payment.txHash,
  createdAt: payment.createdAt,
  confirmedAt: payment.confirmedAt
});
Enter fullscreen mode Exit fullscreen mode

} catch (error) {
console.error('❌ Errore status payment:', error);
res.status(500).json({ error: 'Errore recupero stato' });
}
});

// ============================================
// POST /api/payments/webhook (test)
// Simula un webhook di pagamento
// ============================================
router.post('/webhook', async (req, res) => {
try {
const { paymentId, txHash, amount } = req.body;

const payment = await Payment.findById(paymentId);
if (!payment) {
  return res.status(404).json({ error: 'Pagamento non trovato' });
}

// Aggiorna il pagamento
payment.status = 'confirmed';
payment.txHash = txHash;
payment.amountReceived = amount || payment.amount;
payment.confirmations = 10;
payment.confirmedAt = new Date();
await payment.save();

// Invia webhook al Marketplace
await sendWebhook(payment.orderId, {
  status: 'confirmed',
  txHash: txHash,
  amount: payment.amountReceived
});

res.json({
  success: true,
  payment: payment
});
Enter fullscreen mode Exit fullscreen mode

} catch (error) {
console.error('❌ Errore webhook:', error);
res.status(500).json({ error: 'Errore webhook' });
}
});

module.exports = router;

  1. Monero Service (services/monero.js)

File: gateway/services/monero.js
javascript

const monerojs = require('monero-javascript');

let walletInstance = null;

const MONERO_RPC_URL = process.env.MONERO_RPC_URL || 'http://localhost:18081';
const MONERO_RPC_USERNAME = process.env.MONERO_RPC_USERNAME || '';
const MONERO_RPC_PASSWORD = process.env.MONERO_RPC_PASSWORD || '';
const MONERO_NETWORK = process.env.MONERO_NETWORK || 'testnet';

// ============================================
// Crea il client Monero
// ============================================
const createMoneroClient = async () => {
if (walletInstance) {
return walletInstance;
}

try {
// In produzione, usa un wallet RPC
// Per test, usa un wallet mock
const wallet = await monerojs.createWalletRpc({
url: MONERO_RPC_URL,
username: MONERO_RPC_USERNAME,
password: MONERO_RPC_PASSWORD,
networkType: MONERO_NETWORK
});

walletInstance = wallet;
return wallet;
Enter fullscreen mode Exit fullscreen mode

} catch (error) {
console.error('❌ Errore connessione Monero RPC:', error);
throw error;
}
};

// ============================================
// Genera un subaddress
// ============================================
const generateSubaddress = async (accountIndex = 0) => {
const wallet = await createMoneroClient();
const subaddress = await wallet.createSubaddress(accountIndex);
return {
address: subaddress.getAddress(),
index: subaddress.getIndex()
};
};

// ============================================
// Verifica i pagamenti per un subaddress
// ============================================
const checkPayment = async (subaddress) => {
const wallet = await createMoneroClient();
const transfers = await wallet.getTransfers({
subaddress: subaddress,
isIncoming: true
});

// Trova l'ultimo trasferimento confermato
const confirmed = transfers
.filter(t => t.isConfirmed())
.sort((a, b) => b.getTimestamp() - a.getTimestamp());

if (confirmed.length === 0) {
return null;
}

const last = confirmed[0];
return {
txHash: last.getTxHash(),
amount: last.getAmount(),
confirmations: last.getNumConfirmations(),
timestamp: last.getTimestamp()
};
};

module.exports = {
createMoneroClient,
generateSubaddress,
checkPayment
};

  1. Payment Monitor (services/paymentMonitor.js)

File: gateway/services/paymentMonitor.js
javascript

const Payment = require('../models/Payment');
const { checkPayment } = require('./monero');
const { sendWebhook } = require('./webhookSender');

// ============================================
// Monitora i pagamenti in background
// ============================================
const paymentMonitors = new Map();

const startPaymentMonitoring = async (paymentId) => {
// Se il monitoraggio Γ¨ giΓ  attivo, non far nulla
if (paymentMonitors.has(paymentId.toString())) {
return;
}

console.log(πŸ” Avvio monitoraggio pagamento ${paymentId});

const monitorInterval = setInterval(async () => {
try {
const payment = await Payment.findById(paymentId);
if (!payment) {
clearInterval(monitorInterval);
paymentMonitors.delete(paymentId.toString());
return;
}

  if (payment.status === 'confirmed') {
    clearInterval(monitorInterval);
    paymentMonitors.delete(paymentId.toString());
    return;
  }

  // Controlla se il pagamento Γ¨ stato ricevuto
  const result = await checkPayment(payment.subaddress);
  if (result && result.confirmations >= 1) {
    // Pagamento confermato!
    payment.status = 'confirmed';
    payment.txHash = result.txHash;
    payment.amountReceived = result.amount;
    payment.confirmations = result.confirmations;
    payment.confirmedAt = new Date();
    await payment.save();

    // Invia webhook al Marketplace
    await sendWebhook(payment.orderId, {
      status: 'confirmed',
      txHash: result.txHash,
      amount: result.amount
    });

    // Ferma il monitoraggio
    clearInterval(monitorInterval);
    paymentMonitors.delete(paymentId.toString());

    console.log(`βœ… Pagamento ${paymentId} confermato!`);
  }
} catch (error) {
  console.error(`❌ Errore monitoraggio pagamento ${paymentId}:`, error);
}
Enter fullscreen mode Exit fullscreen mode

}, 30000); // Controlla ogni 30 secondi

paymentMonitors.set(paymentId.toString(), monitorInterval);
};

// ============================================
// Ferma il monitoraggio di un pagamento
// ============================================
const stopPaymentMonitoring = (paymentId) => {
const interval = paymentMonitors.get(paymentId.toString());
if (interval) {
clearInterval(interval);
paymentMonitors.delete(paymentId.toString());
}
};

module.exports = {
startPaymentMonitoring,
stopPaymentMonitoring
};

  1. Webhook Sender (services/webhookSender.js)

File: gateway/services/webhookSender.js
javascript

const axios = require('axios');
const crypto = require('crypto');

const WEBHOOK_URL = process.env.WEBHOOK_URL || 'http://localhost:4000/webhook/order-update';
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET || 'your_webhook_secret';

// ============================================
// Invia webhook al Marketplace
// ============================================
const sendWebhook = async (orderId, payload) => {
try {
const data = {
orderId,
...payload,
timestamp: new Date().toISOString()
};

// Firma HMAC per autenticazione
const signature = crypto
  .createHmac('sha256', WEBHOOK_SECRET)
  .update(JSON.stringify(data))
  .digest('hex');

const response = await axios.post(WEBHOOK_URL, data, {
  headers: {
    'Content-Type': 'application/json',
    'X-Webhook-Signature': signature
  },
  timeout: 10000
});

console.log(`βœ… Webhook inviato per ordine ${orderId}:`, response.status);
return response.data;
Enter fullscreen mode Exit fullscreen mode

} catch (error) {
console.error(❌ Errore webhook per ordine ${orderId}:, error.message);
throw error;
}
};

module.exports = {
sendWebhook
};

πŸ”§ Environment Variables

File: gateway/.env
env

Server

PORT=3000
NODE_ENV=development

MongoDB

MONGODB_URI=mongodb://localhost:27017/myzubster

Monero RPC

MONERO_RPC_URL=http://localhost:18081
MONERO_RPC_USERNAME=
MONERO_RPC_PASSWORD=
MONERO_NETWORK=testnet

JWT

JWT_SECRET=your_gateway_jwt_secret

Webhook

WEBHOOK_URL=http://localhost:4000/webhook/order-update
WEBHOOK_SECRET=your_webhook_secret

Logging

LOG_LEVEL=info

πŸš€ How to Run the Gateway
1️⃣ Prerequisites
bash

Start MongoDB

docker run -d -p 27017:27017 --name mongodb mongo:6

Or use local MongoDB

mongod --dbpath ~/data/db

2️⃣ Install Dependencies
bash

cd gateway
npm install

3️⃣ Configure Environment
bash

cp .env.example .env

Edit .env with your values

4️⃣ Start Gateway
bash

npm run dev

Expected output:
text

πŸš€ Server avviato sulla porta 3000
βœ… Connesso a MongoDB
πŸ“¦ Database: mongodb://localhost:27017/myzubster

5️⃣ Test Gateway
bash

Health check

curl http://localhost:3000/api/health

Initiate payment

curl -X POST http://localhost:3000/api/payments/initiate \
-H "Content-Type: application/json" \
-d '{"orderId":"123","amount":150,"currency":"USD"}'

πŸ§ͺ Test Commands
Create a Payment
bash

curl -X POST http://localhost:3000/api/payments/initiate \
-H "Content-Type: application/json" \
-d '{"orderId":"test-123","amount":150,"currency":"USD"}'

Check Payment Status
bash

curl http://localhost:3000/api/payments/status/{paymentId}

Simulate Webhook
bash

curl -X POST http://localhost:3000/api/payments/webhook \
-H "Content-Type: application/json" \
-d '{"paymentId":"{paymentId}","txHash":"mock-tx-123","amount":150}'

πŸ“Š Database Schema
Payment Collection
json

{
"_id": "ObjectId(...)",
"orderId": "test-123",
"subaddress": "8A1B2C3D4E5F6G7H8I9J0K",
"amount": 150,
"currency": "USD",
"moneroAmount": 0.0125,
"addressIndex": 0,
"txHash": null,
"status": "pending",
"confirmations": 0,
"amountReceived": 0,
"network": "testnet",
"createdAt": "2026-07-20T10:00:00Z",
"confirmedAt": null,
"webhookDelivered": false
}

πŸ”’ Security Best Practices
Practice Implementation
JWT Authentication All API endpoints require valid JWT token
Webhook Signing HMAC-SHA256 signature verification
Rate Limiting Prevent DoS attacks
Environment Variables No hardcoded secrets
HTTPS Use TLS in production
Input Validation Validate all incoming data
CORS Configure allowed origins
πŸ› Troubleshooting
"Cannot connect to MongoDB"
bash

Check if MongoDB is running

docker ps | grep mongodb

Or

ps aux | grep mongod

Start MongoDB

docker start mongodb

"Cannot connect to Monero RPC"
bash

Check if Monero node is running

curl http://localhost:18081/get_info

Start Monero node (testnet)

monerod --testnet --rpc-bind-port 18081

"Webhook delivery failed"
bash

Check if Marketplace is running

curl http://localhost:4000/health

Check webhook endpoint

curl -X POST http://localhost:4000/webhook/order-update \
-H "Content-Type: application/json" \
-d '{"orderId":"test","status":"test"}'

πŸ“š Resources

Monero RPC Documentation: monero-rpc

monero-javascript: npm

MongoDB Documentation: mongodb.com

MyZubster GitHub: github.com/DanielIoni-creator
Enter fullscreen mode Exit fullscreen mode

βœ… Next Steps

Add real Monero wallet – Connect to a real Monero daemon

Add rate limiting – Protect the API from abuse

Add monitoring – Payment monitoring and alerts

Add retry logic – For failed webhook deliveries

Write tests – Unit and integration tests
Enter fullscreen mode Exit fullscreen mode

Built with ❀️ for privacy, freedom, and decentralization.

Happy coding! πŸš€

Top comments (0)