Project Setup
npm init -y
npm install express axios crypto dotenv
Environment Variables
PAYCHAINLY_API_KEY=your_key
PAYCHAINLY_WEBHOOK_SECRET=your_secret
PORT=3000
Create a Payment Session
const axios = require('axios');
async function createPaymentSession(orderId, amount) {
const { data } = await axios.post(
'https://paychainly.com/api/v1/addresses/start-session',
{ userId: String(orderId), amount },
{ headers: { 'x-api-key': process.env.PAYCHAINLY_API_KEY } }
);
return data; // { address, sessionId, expiresAt }
}
Webhook Endpoint
const crypto = require('crypto');
const express = require('express');
const app = express();
// MUST use raw body for signature verification
app.use('/webhook', express.raw({ type: 'application/json' }));
app.post('/webhook', (req, res) => {
const sig = req.headers['x-signature'];
const payload = JSON.parse(req.body);
const msg = [payload.event, payload.txHash, payload.fromAddress,
payload.toAddress, payload.amount, payload.blockNumber,
payload.timestamp, payload.userId].join('|');
const expected = crypto
.createHmac('sha256', process.env.PAYCHAINLY_WEBHOOK_SECRET)
.update(msg).digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
return res.status(401).json({ error: 'Invalid signature' });
}
if (payload.event === 'deposit_detected') {
fulfillOrder(payload.userId, payload.amount, payload.txHash);
}
res.json({ ok: true });
});
async function fulfillOrder(userId, amount, txHash) {
// Check for duplicate (replay attack prevention)
const existing = await db.query('SELECT id FROM payments WHERE tx_hash = $1', [txHash]);
if (existing.rows.length > 0) return;
await db.query(
'INSERT INTO payments (order_id, amount, tx_hash, status) VALUES ($1, $2, $3, $4)',
[userId, amount, txHash, 'confirmed']
);
// Your order fulfillment logic here
}