yZubster Monero Integration: From Zero to Hero
Complete guide to integrating Monero payments β from wallet setup to production monitoring
π What is This Guide?
This guide covers everything you need to know about Monero integration in MyZubster:
β
Monero basics β Understanding XMR, wallets, and transactions
β
Wallet setup β Creating and configuring a Monero wallet
β
RPC client β Interacting with the Monero blockchain
β
Subaddress generation β Creating unique addresses per order
β
Payment monitoring β Real-time transaction detection
β
Webhook integration β Notifying the Marketplace
β
Testnet vs Mainnet β Testing and production environments
β
Production considerations β Scaling and security
π§© Monero Architecture Overview
text
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Monero Integration Architecture β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β MyZubster Gateway β β
β β β β
β β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β β
β β β Payment β β Subaddress β β Webhook β β β
β β β Initiation β β Generation β β Sender β β β
β β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β
β βΌ β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Monero RPC Client β β
β β (monero-javascript) β β
β β β β
β β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β β
β β β Wallet β β Transfer β β Monitor β β β
β β β API β β API β β API β β β
β β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β
β βΌ β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Monero Node β β
β β (monerod) β β
β β β β
β β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β β
β β β Blockchain β β Transactionβ β Mempool β β β
β β β Storage β β Pool β β Monitor β β β
β β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β
β βΌ β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Monero Network β β
β β (Testnet/Mainnet) β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
π¦ Step 1: Setting Up Monero Node
1.1 Run Monero Node with Docker
bash
Pull Monero node image
docker pull ghcr.io/tao-o/monero:latest
Run Monero node (testnet)
docker run -d \
--name monero-node \
-p 18081:18081 \
-p 18082:18082 \
-v monero_data:/home/monero/.bitmonero \
ghcr.io/tao-o/monero:latest \
--testnet \
--rpc-bind-ip=0.0.0.0 \
--rpc-bind-port=18081 \
--confirm-external-bind \
--non-interactive
Run Monero node (mainnet)
docker run -d \
--name monero-node \
-p 18081:18081 \
-p 18082:18082 \
-v monero_data:/home/monero/.bitmonero \
ghcr.io/tao-o/monero:latest \
--rpc-bind-ip=0.0.0.0 \
--rpc-bind-port=18081 \
--confirm-external-bind \
--non-interactive
1.2 Check Node Status
bash
Check node status
curl -X POST http://localhost:18081/get_info
Check node sync status
curl -X POST http://localhost:18081/sync_info
Check blockchain height
curl -X POST http://localhost:18081/get_height
1.3 Create Monero Wallet
bash
Create wallet via RPC
curl -X POST http://localhost:18081/create_wallet \
-H "Content-Type: application/json" \
-d '{
"filename": "myzubster_wallet",
"password": "your_wallet_password",
"language": "English"
}'
Open wallet
curl -X POST http://localhost:18081/open_wallet \
-H "Content-Type: application/json" \
-d '{
"filename": "myzubster_wallet",
"password": "your_wallet_password"
}'
Get wallet address
curl -X POST http://localhost:18081/get_address
π οΈ Step 2: Monero RPC Client Implementation
2.1 Monero Service
File: gateway/services/monero.js
javascript
const monerojs = require('monero-javascript');
// ============================================
// CONFIGURATION
// ============================================
const MONERO_CONFIG = {
rpcUrl: process.env.MONERO_RPC_URL || 'http://localhost:18081',
username: process.env.MONERO_RPC_USERNAME || '',
password: process.env.MONERO_RPC_PASSWORD || '',
network: process.env.MONERO_NETWORK || 'testnet',
walletPassword: process.env.MONERO_WALLET_PASSWORD,
walletPath: process.env.MONERO_WALLET_PATH || './monero_wallet',
};
let walletInstance = null;
// ============================================
// CONNECT TO MONERO
// ============================================
const connectMonero = async () => {
if (walletInstance) {
return walletInstance;
}
try {
// Create wallet client
walletInstance = await monerojs.createWalletRpc({
url: MONERO_CONFIG.rpcUrl,
username: MONERO_CONFIG.username,
password: MONERO_CONFIG.password,
network: MONERO_CONFIG.network,
});
// Open wallet
await walletInstance.openWallet({
path: MONERO_CONFIG.walletPath,
password: MONERO_CONFIG.walletPassword,
});
console.log('β
Connected to Monero node');
console.log(`π¦ Network: ${MONERO_CONFIG.network}`);
console.log(`π Wallet: ${MONERO_CONFIG.walletPath}`);
return walletInstance;
} catch (error) {
console.error('β Error connecting to Monero:', error);
throw error;
}
};
// ============================================
// GENERATE SUBADDRESS
// ============================================
const generateSubaddress = async (accountIndex = 0) => {
try {
const wallet = await connectMonero();
const subaddress = await wallet.createSubaddress(accountIndex);
return {
address: subaddress.getAddress(),
index: subaddress.getIndex(),
accountIndex: subaddress.getAccountIndex(),
label: subaddress.getLabel(),
};
} catch (error) {
console.error('β Error generating subaddress:', error);
throw error;
}
};
// ============================================
// GET BALANCE
// ============================================
const getBalance = async () => {
try {
const wallet = await connectMonero();
const balance = await wallet.getBalance();
return {
balance: balance.getBalance(),
unlockedBalance: balance.getUnlockedBalance(),
};
} catch (error) {
console.error('β Error getting balance:', error);
throw error;
}
};
// ============================================
// GET TRANSFERS
// ============================================
const getTransfers = async (subaddress = null) => {
try {
const wallet = await connectMonero();
const transfers = await wallet.getTransfers({
subaddress: subaddress,
isIncoming: true,
});
return transfers.map((transfer) => ({
txHash: transfer.getTxHash(),
amount: transfer.getAmount(),
confirmations: transfer.getNumConfirmations(),
timestamp: transfer.getTimestamp(),
isConfirmed: transfer.isConfirmed(),
}));
} catch (error) {
console.error('β Error getting transfers:', error);
throw error;
}
};
// ============================================
// MONITOR PAYMENT
// ============================================
const monitorPayment = async (subaddress, expectedAmount, maxConfirmations = 10) => {
try {
const wallet = await connectMonero();
// Get recent transfers
const transfers = await wallet.getTransfers({
subaddress: subaddress,
isIncoming: true,
});
// Filter confirmed transfers
const confirmed = transfers
.filter((t) => t.isConfirmed())
.filter((t) => t.getAmount() >= expectedAmount)
.sort((a, b) => b.getNumConfirmations() - a.getNumConfirmations());
if (confirmed.length === 0) {
return null;
}
const last = confirmed[0];
return {
txHash: last.getTxHash(),
amount: last.getAmount(),
confirmations: last.getNumConfirmations(),
timestamp: last.getTimestamp(),
isConfirmed: last.getNumConfirmations() >= maxConfirmations,
};
} catch (error) {
console.error('β Error monitoring payment:', error);
throw error;
}
};
// ============================================
// SEND PAYMENT
// ============================================
const sendPayment = async (toAddress, amount) => {
try {
const wallet = await connectMonero();
const result = await wallet.send({
amount: amount,
address: toAddress,
priority: 1, // 0=default, 1=unimportant, 2=normal, 3=elevated
});
return {
txHash: result.getTxHash(),
amount: result.getAmount(),
fee: result.getFee(),
status: result.getStatus(),
};
} catch (error) {
console.error('β Error sending payment:', error);
throw error;
}
};
// ============================================
// CHECK NETWORK STATUS
// ============================================
const checkNetworkStatus = async () => {
try {
const wallet = await connectMonero();
const status = await wallet.getStatus();
return {
height: status.getHeight(),
blockTime: status.getBlockTime(),
difficulty: status.getDifficulty(),
};
} catch (error) {
console.error('β Error checking network status:', error);
throw error;
}
};
// ============================================
// GET TRANSACTION DETAILS
// ============================================
const getTransactionDetails = async (txHash) => {
try {
const wallet = await connectMonero();
const tx = await wallet.getTx({
txHash: txHash,
});
return {
txHash: tx.getTxHash(),
amount: tx.getAmount(),
fee: tx.getFee(),
confirmations: tx.getNumConfirmations(),
timestamp: tx.getTimestamp(),
isConfirmed: tx.isConfirmed(),
isFailed: tx.isFailed(),
};
} catch (error) {
console.error('β Error getting transaction details:', error);
throw error;
}
};
module.exports = {
connectMonero,
generateSubaddress,
getBalance,
getTransfers,
monitorPayment,
sendPayment,
checkNetworkStatus,
getTransactionDetails,
};
2.2 Payment Service
File: gateway/services/paymentService.js
javascript
const Payment = require('../models/Payment');
const {
generateSubaddress,
monitorPayment,
getBalance,
} = require('./monero');
const { sendWebhook } = require('./webhookSender');
// ============================================
// INITIATE PAYMENT
// ============================================
const initiatePayment = async (orderId, amount, currency) => {
try {
// Generate subaddress
const subaddress = await generateSubaddress();
// Create payment record
const payment = await Payment.create({
orderId: orderId,
subaddress: subaddress.address,
amount: amount,
currency: currency || 'USD',
moneroAmount: await convertToMonero(amount, currency),
addressIndex: subaddress.index,
status: 'pending',
network: process.env.MONERO_NETWORK || 'testnet',
});
// Start monitoring in background
startPaymentMonitoring(payment._id);
return {
paymentId: payment._id,
moneroAddress: payment.subaddress,
moneroAmount: payment.moneroAmount,
addressIndex: payment.addressIndex,
network: payment.network,
status: payment.status,
};
} catch (error) {
console.error('β Error initiating payment:', error);
throw error;
}
};
// ============================================
// CONVERT TO MONERO
// ============================================
const convertToMonero = async (amount, currency) => {
// In production, use an exchange rate API
// For now, use a fixed rate (1 XMR = 150 USD)
const exchangeRate = 150;
return amount / exchangeRate;
};
// ============================================
// PAYMENT MONITORING
// ============================================
const paymentMonitors = new Map();
const startPaymentMonitoring = (paymentId) => {
if (paymentMonitors.has(paymentId.toString())) {
return;
}
console.log(π Starting payment monitor: ${paymentId});
let checkCount = 0;
const maxChecks = 60; // 30 minutes (60 * 30s)
const interval = setInterval(async () => {
checkCount++;
try {
const payment = await Payment.findById(paymentId);
if (!payment) {
clearInterval(interval);
paymentMonitors.delete(paymentId.toString());
return;
}
if (payment.status === 'confirmed' || payment.status === 'expired') {
clearInterval(interval);
paymentMonitors.delete(paymentId.toString());
return;
}
// Check for payment
const result = await monitorPayment(
payment.subaddress,
payment.moneroAmount,
10
);
if (result && result.isConfirmed) {
// Payment confirmed!
payment.status = 'confirmed';
payment.txHash = result.txHash;
payment.amountReceived = result.amount;
payment.confirmations = result.confirmations;
payment.confirmedAt = new Date();
await payment.save();
// Send webhook
await sendWebhook(payment.orderId, {
status: 'confirmed',
txHash: result.txHash,
amount: payment.amount,
moneroAmount: result.amount,
});
console.log(`β
Payment confirmed: ${paymentId}`);
clearInterval(interval);
paymentMonitors.delete(paymentId.toString());
}
// Check for expiry (30 minutes)
if (checkCount >= maxChecks) {
payment.status = 'expired';
await payment.save();
console.log(`β° Payment expired: ${paymentId}`);
clearInterval(interval);
paymentMonitors.delete(paymentId.toString());
}
} catch (error) {
console.error(`β Error monitoring payment ${paymentId}:`, error);
}
}, 30000); // Check every 30 seconds
paymentMonitors.set(paymentId.toString(), interval);
};
// ============================================
// GET PAYMENT STATUS
// ============================================
const getPaymentStatus = async (paymentId) => {
try {
const payment = await Payment.findById(paymentId);
if (!payment) {
throw new Error('Payment not found');
}
return {
paymentId: payment._id,
status: payment.status,
moneroAddress: payment.subaddress,
moneroAmount: payment.moneroAmount,
confirmations: payment.confirmations,
amountReceived: payment.amountReceived,
txHash: payment.txHash,
createdAt: payment.createdAt,
confirmedAt: payment.confirmedAt,
};
} catch (error) {
console.error('β Error getting payment status:', error);
throw error;
}
};
// ============================================
// GET ALL PAYMENTS
// ============================================
const getPayments = async (filters = {}) => {
try {
const where = {};
if (filters.orderId) where.orderId = filters.orderId;
if (filters.status) where.status = filters.status;
if (filters.from) where.createdAt = { $gte: new Date(filters.from) };
if (filters.to) where.createdAt = { ...where.createdAt, $lte: new Date(filters.to) };
const payments = await Payment.find(where)
.sort({ createdAt: -1 })
.limit(filters.limit || 100);
return payments;
} catch (error) {
console.error('β Error getting payments:', error);
throw error;
}
};
module.exports = {
initiatePayment,
convertToMonero,
startPaymentMonitoring,
getPaymentStatus,
getPayments,
};
π Step 3: Payment Monitoring
3.1 Background Monitoring Service
File: gateway/services/monitorService.js
javascript
const Payment = require('../models/Payment');
const { monitorPayment } = require('./monero');
const { sendWebhook } = require('./webhookSender');
// ============================================
// MONITOR BACKGROUND SERVICE
// ============================================
class PaymentMonitorService {
constructor() {
this.isRunning = false;
this.interval = null;
this.intervalTime = 30000; // 30 seconds
}
async start() {
if (this.isRunning) {
console.log('β οΈ Monitor service already running');
return;
}
console.log('π Starting payment monitor service...');
this.isRunning = true;
// Run once immediately
await this.scanPayments();
// Then run periodically
this.interval = setInterval(async () => {
await this.scanPayments();
}, this.intervalTime);
}
async stop() {
console.log('π Stopping payment monitor service...');
this.isRunning = false;
if (this.interval) {
clearInterval(this.interval);
this.interval = null;
}
}
async scanPayments() {
try {
// Get all pending payments
const payments = await Payment.find({
status: 'pending',
createdAt: { $gte: new Date(Date.now() - 30 * 60 * 1000) }, // Last 30 minutes
});
if (payments.length === 0) {
// Uncomment for debugging
// console.log('No pending payments to monitor');
return;
}
console.log(`π Scanning ${payments.length} pending payments...`);
for (const payment of payments) {
try {
const result = await monitorPayment(
payment.subaddress,
payment.moneroAmount,
10
);
if (result && result.isConfirmed) {
// Payment confirmed
payment.status = 'confirmed';
payment.txHash = result.txHash;
payment.amountReceived = result.amount;
payment.confirmations = result.confirmations;
payment.confirmedAt = new Date();
await payment.save();
// Send webhook
await sendWebhook(payment.orderId, {
status: 'confirmed',
txHash: result.txHash,
amount: payment.amount,
moneroAmount: result.amount,
});
console.log(`β
Payment confirmed: ${payment._id} (${payment.orderId})`);
}
// Check for expiry (30 minutes)
const ageMinutes = (Date.now() - payment.createdAt.getTime()) / 60000;
if (ageMinutes >= 30) {
payment.status = 'expired';
await payment.save();
console.log(`β° Payment expired: ${payment._id} (${payment.orderId})`);
}
} catch (error) {
console.error(`β Error processing payment ${payment._id}:`, error);
}
}
} catch (error) {
console.error('β Error scanning payments:', error);
}
}
}
// Singleton instance
const monitorService = new PaymentMonitorService();
module.exports = monitorService;
3.2 Start Monitor Service
File: gateway/server.js (Update)
javascript
// ... existing code ...
// Start payment monitor service
const monitorService = require('./services/monitorService');
// Start after database connection
mongoose.connect(MONGODB_URI)
.then(() => {
console.log('β
Connesso a MongoDB');
console.log(π¦ Database: ${MONGODB_URI});
// Start monitor service
monitorService.start();
})
.catch((err) => {
console.error('β Errore connessione MongoDB:', err);
process.exit(1);
});
// Graceful shutdown
process.on('SIGTERM', async () => {
console.log('π Received SIGTERM signal');
await monitorService.stop();
process.exit(0);
});
process.on('SIGINT', async () => {
console.log('π Received SIGINT signal');
await monitorService.stop();
process.exit(0);
});
π‘ Step 4: Webhook Integration
4.1 Webhook Sender
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';
const MAX_RETRIES = 5;
const RETRY_DELAY = 5000;
// ============================================
// SEND WEBHOOK
// ============================================
const sendWebhook = async (orderId, payload) => {
const data = {
orderId: orderId,
...payload,
timestamp: new Date().toISOString(),
};
// Generate HMAC signature
const signature = crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(JSON.stringify(data))
.digest('hex');
const headers = {
'Content-Type': 'application/json',
'X-Webhook-Signature': signature,
'X-Webhook-Timestamp': data.timestamp,
};
// Attempt delivery with retries
let attempt = 0;
let lastError = null;
while (attempt < MAX_RETRIES) {
try {
const response = await axios.post(WEBHOOK_URL, data, {
headers: headers,
timeout: 10000,
});
console.log(`β
Webhook delivered: ${orderId} (attempt ${attempt + 1})`);
return response.data;
} catch (error) {
attempt++;
lastError = error;
console.warn(`β οΈ Webhook attempt ${attempt}/${MAX_RETRIES} failed: ${error.message}`);
if (attempt < MAX_RETRIES) {
await new Promise(resolve => setTimeout(resolve, RETRY_DELAY * attempt));
}
}
}
console.error(β Webhook delivery failed after ${MAX_RETRIES} attempts: ${orderId});
throw lastError;
};
module.exports = {
sendWebhook,
};
4.2 Webhook Receiver (Marketplace)
File: marketplace/routes/webhook.js
javascript
const express = require('express');
const router = express.Router();
const crypto = require('crypto');
const { Order } = require('../models');
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET || 'your_webhook_secret';
// ============================================
// VERIFY WEBHOOK SIGNATURE
// ============================================
const verifyWebhookSignature = (req, res, next) => {
try {
const signature = req.headers['x-webhook-signature'];
const timestamp = req.headers['x-webhook-timestamp'];
const payload = req.body;
if (!signature || !timestamp) {
return res.status(401).json({ error: 'Missing signature headers' });
}
// Verify timestamp (prevent replay attacks)
const now = Date.now();
const webhookTime = new Date(timestamp).getTime();
if (Math.abs(now - webhookTime) > 300000) { // 5 minutes
return res.status(401).json({ error: 'Webhook timestamp expired' });
}
// Verify signature
const expectedSignature = crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(JSON.stringify(payload))
.digest('hex');
if (signature !== expectedSignature) {
return res.status(401).json({ error: 'Invalid webhook signature' });
}
next();
} catch (error) {
console.error('β Webhook verification error:', error);
res.status(500).json({ error: 'Webhook verification failed' });
}
};
// ============================================
// WEBHOOK ENDPOINT
// ============================================
router.post('/order-update', verifyWebhookSignature, async (req, res) => {
try {
const { orderId, status, txHash, amount, moneroAmount } = req.body;
if (!orderId || !status) {
return res.status(400).json({ error: 'Missing required fields' });
}
console.log(`π₯ Webhook received: order ${orderId}, status ${status}`);
// Find order
const order = await Order.findByPk(orderId);
if (!order) {
return res.status(404).json({ error: 'Order not found' });
}
// Update order based on status
if (status === 'confirmed') {
order.status = 'paid';
order.moneroTxHash = txHash;
order.paidAt = new Date();
} else if (status === 'expired') {
order.status = 'cancelled';
order.cancelledAt = new Date();
}
await order.save();
console.log(`β
Order ${orderId} updated to ${order.status}`);
res.json({
success: true,
order: {
id: order.id,
status: order.status,
updatedAt: order.updatedAt,
},
});
} catch (error) {
console.error('β Webhook error:', error);
res.status(500).json({ error: error.message });
}
});
module.exports = router;
π§ͺ Step 5: Testing
5.1 Test Payment Flow
bash
1. Initiate payment
curl -X POST http://localhost:3000/api/payments/initiate \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"orderId":1,"amount":150,"currency":"USD"}'
Response:
{
"paymentId": "pay_123456",
"moneroAddress": "8A1B2C3D4E5F6G7H8I9J0K",
"moneroAmount": 0.0125,
"status": "pending"
}
2. Check payment status
curl -X GET http://localhost:3000/api/payments/status/pay_123456 \
-H "Authorization: Bearer $TOKEN"
3. Simulate payment (for testing)
curl -X POST http://localhost:3000/api/payments/webhook \
-H "Content-Type: application/json" \
-d '{"paymentId":"pay_123456","txHash":"mock_tx_hash","amount":150}'
4. Check order status (Marketplace)
curl -X GET http://localhost:4000/api/orders/1 \
-H "Authorization: Bearer $BUYER_TOKEN"
5.2 Test Webhook
bash
1. Send test webhook to Marketplace
curl -X POST http://localhost:4000/webhook/order-update \
-H "Content-Type: application/json" \
-H "X-Webhook-Signature: test_signature" \
-H "X-Webhook-Timestamp: $(date -Iseconds)" \
-d '{"orderId":1,"status":"confirmed","txHash":"mock_tx_hash"}'
2. Check order status
curl -X GET http://localhost:4000/api/orders/1 \
-H "Authorization: Bearer $BUYER_TOKEN"
π§ Step 6: Environment Variables
File: .env (Gateway)
env
Monero Configuration
MONERO_RPC_URL=http://localhost:18081
MONERO_RPC_USERNAME=
MONERO_RPC_PASSWORD=
MONERO_NETWORK=testnet
MONERO_WALLET_PASSWORD=your_wallet_password
MONERO_WALLET_PATH=./monero_wallet
Payment Configuration
PAYMENT_EXPIRY_MINUTES=30
PAYMENT_MONITOR_INTERVAL=30
MIN_CONFIRMATIONS=10
Webhook Configuration
WEBHOOK_URL=http://localhost:4000/webhook/order-update
WEBHOOK_SECRET=your_webhook_secret
WEBHOOK_MAX_RETRIES=5
WEBHOOK_RETRY_DELAY=5000
Rate Limiting
PAYMENT_RATE_LIMIT=100
PAYMENT_RATE_WINDOW=900000
π Troubleshooting
"Cannot connect to Monero node"
bash
Check if node is running
docker ps | grep monero
Check node logs
docker logs monero-node
Test RPC connection
curl -X POST http://localhost:18081/get_info
"Wallet not found"
bash
Create wallet
curl -X POST http://localhost:18081/create_wallet \
-H "Content-Type: application/json" \
-d '{"filename":"myzubster_wallet","password":"password","language":"English"}'
"Payment not detected"
bash
Check balance
curl -X POST http://localhost:18081/get_balance
Check transfers
curl -X POST http://localhost:18081/get_transfers \
-H "Content-Type: application/json" \
-d '{"in": true}'
"Webhook delivery failed"
bash
Check 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 Documentation: docs.getmonero.org
monero-javascript: npmjs.com/package/monero-javascript
Monero RPC Reference: docs.getmonero.org/interacting/monero-wallet-rpc-reference
π Connect with Me
π Blog & Articles: DEV.to - Daniel Ioni
π¦ X (Twitter): @myzubster
πΌ LinkedIn: Daniel Ioni
π GitHub: DanielIoni-creator
π΅ TikTok: @h4x0r_23
β Star the project on GitHub β it helps a lot!
Built with β€οΈ for privacy, freedom, and decentralization.
Happy integrating! π
β
Instructions for Publishing
Copy all the content above
Go to dev.to/new
Set the editor to Markdown mode
Paste the content
Title: MyZubster Monero Integration: From Zero to Hero
Tags: monero, blockchain, cryptocurrency, opensource
Publish!
Top comments (0)