/**
* Crownline Global — Node.js (Express) Integration Example
*
* This is a minimal working example showing how to:
* 1. Create a payment
* 2. Check a payment's status
* 3. Receive and verify webhook notifications
*
* Install dependencies first:
* npm install express node-fetch
*
* Run:
* CROWNLINE_API_KEY=your_api_key node server.js
*/
const express = require('express');
const crypto = require('crypto');
const fetch = require('node-fetch');
const app = express();
app.use(express.json());
const API_BASE_URL = 'https://api.crownlineglobal.co.uk';
const API_KEY = process.env.CROWNLINE_API_KEY;
// ---------------------------------------------------------------------
// 1. Create a payment (call this when your customer clicks "Pay")
// ---------------------------------------------------------------------
async function createPayment(orderId, amount, customer) {
const response = await fetch(`${API_BASE_URL}/api/payments/create`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
merchantOrderId: orderId,
amount: amount,
countryCode: 'IDN',
currency: 'Rp',
language: 'ID',
paymentMethodCode: 'BNIVA',
notificationUrl: 'https://yourshop.com/webhooks/payment',
customerName: customer.name,
customerEmail: customer.email,
customerPhone: customer.phone,
productName: `Order #${orderId}`,
}),
});
const data = await response.json();
if (data.code !== 'SUCCESS') {
throw new Error(`Payment creation failed: ${data.error || data.message}`);
}
return data;
}
// Example usage in a checkout route:
app.post('/checkout', async (req, res) => {
try {
const payment = await createPayment('ORDER-1001', 150000, {
name: 'Jane Doe',
email: 'jane@example.com',
phone: '081234567890',
});
// Show payment.paymentInfo.content to your customer
res.json(payment);
} catch (err) {
res.status(500).json({ error: err.message });
}
});
// ---------------------------------------------------------------------
// 2. Check payment status (poll this if you haven't received a webhook yet)
// ---------------------------------------------------------------------
async function checkPaymentStatus(orderId) {
const response = await fetch(
`${API_BASE_URL}/api/payments/status?merchantOrderId=${orderId}`,
{ headers: { 'Authorization': `Bearer ${API_KEY}` } }
);
return response.json();
}
app.get('/orders/:orderId/status', async (req, res) => {
const status = await checkPaymentStatus(req.params.orderId);
res.json(status);
});
// ---------------------------------------------------------------------
// 3. Receive and verify webhook notifications
// ---------------------------------------------------------------------
// IMPORTANT: we need the RAW request body to verify the signature,
// so we use express.raw() for this specific route instead of express.json().
app.post(
'/webhooks/payment',
express.raw({ type: 'application/json' }),
(req, res) => {
const signature = req.headers['x-payfac-signature'];
const rawBody = req.body; // Buffer
const expectedSignature = crypto
.createHmac('sha256', API_KEY)
.update(rawBody)
.digest('hex');
if (signature !== expectedSignature) {
console.warn('Webhook signature mismatch — rejecting');
return res.status(401).send('invalid signature');
}
const notification = JSON.parse(rawBody.toString());
console.log('Payment update received:', notification);
// notification.status will be "SUCCESS", "PENDING", or "FAILED"
// notification.paidAmount is the ACTUAL amount paid — use this,
// not notification.amount, to confirm the order was paid in full.
if (notification.status === 'SUCCESS') {
// TODO: mark the order as paid in your own database
}
res.sendStatus(200);
}
);
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Listening on port ${PORT}`));