Node.js
Payment Gateway
Integrate Midtrans and Stripe payment gateways in Node.js — create transactions, Snap tokens, payment intents, and verify webhooks.
Midtrans SDK Install: Install the official Midtrans Node.js client and configure with your server key.
terminal
JS
# Install
npm install midtrans-client
# .env
MIDTRANS_SERVER_KEY=SB-Mid-server-xxxxxxxxxxxx
MIDTRANS_CLIENT_KEY=SB-Mid-client-xxxxxxxxxxxx
MIDTRANS_IS_PRODUCTION=false
Midtrans Snap Token: Create a Snap transaction token to initialise the Midtrans payment popup on the frontend.
controllers/paymentController.js
JS
const midtransClient = require('midtrans-client');
const snap = new midtransClient.Snap({
isProduction: process.env.MIDTRANS_IS_PRODUCTION === 'true',
serverKey: process.env.MIDTRANS_SERVER_KEY,
clientKey: process.env.MIDTRANS_CLIENT_KEY,
});
exports.createSnapToken = async (req, res) => {
const { orderId, amount, customerName, customerEmail } = req.body;
const parameter = {
transaction_details: {
order_id: orderId,
gross_amount: amount,
},
customer_details: {
first_name: customerName,
email: customerEmail,
},
};
try {
const transaction = await snap.createTransaction(parameter);
res.json({ token: transaction.token, redirect_url: transaction.redirect_url });
} catch (err) {
res.status(500).json({ message: err.message });
}
};
Midtrans Webhook Notification: Verify and handle payment status notifications sent by Midtrans to your server endpoint.
controllers/paymentController.js
JS
exports.handleNotification = async (req, res) => {
const apiClient = new midtransClient.CoreApi({
isProduction: process.env.MIDTRANS_IS_PRODUCTION === 'true',
serverKey: process.env.MIDTRANS_SERVER_KEY,
});
try {
const statusResponse = await apiClient.transaction.notification(req.body);
const { order_id, transaction_status, fraud_status } = statusResponse;
let orderStatus = 'pending';
if (transaction_status === 'capture') {
orderStatus = fraud_status === 'accept' ? 'paid' : 'fraud';
} else if (transaction_status === 'settlement') {
orderStatus = 'paid';
} else if (['cancel', 'deny', 'expire'].includes(transaction_status)) {
orderStatus = 'cancelled';
}
// Update your order in DB
await Order.findOneAndUpdate({ orderId: order_id }, { status: orderStatus });
res.status(200).json({ message: 'OK' });
} catch (err) {
res.status(500).json({ message: err.message });
}
};
Stripe SDK Install: Install the Stripe Node.js SDK and initialise with your secret key.
terminal
JS
# Install
npm install stripe
# .env
STRIPE_SECRET_KEY=sk_test_xxxxxxxxxxxxxxxxxxxx
STRIPE_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxxxxxxxx
Stripe Payment Intent: Create a PaymentIntent on the server and send the
client_secret to the frontend to complete the payment.controllers/stripeController.js
JS
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
exports.createPaymentIntent = async (req, res) => {
const { amount, currency = 'usd' } = req.body;
try {
const paymentIntent = await stripe.paymentIntents.create({
amount: amount * 100, // Stripe uses cents
currency,
automatic_payment_methods: { enabled: true },
});
res.json({ clientSecret: paymentIntent.client_secret });
} catch (err) {
res.status(500).json({ message: err.message });
}
};
Stripe Webhook Verification: Verify the Stripe webhook signature and handle
payment_intent.succeeded events securely. Requires raw body — use express.raw() for this route.controllers/stripeController.js
JS
exports.handleWebhook = (req, res) => {
const sig = req.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(
req.body, // raw Buffer
sig,
process.env.STRIPE_WEBHOOK_SECRET
);
} catch (err) {
return res.status(400).json({ message: `Webhook Error: ${err.message}` });
}
switch (event.type) {
case 'payment_intent.succeeded':
const intent = event.data.object;
console.log('Payment succeeded:', intent.id);
// fulfill order...
break;
case 'payment_intent.payment_failed':
console.log('Payment failed');
break;
default:
console.log(`Unhandled event type: ${event.type}`);
}
res.json({ received: true });
};
// In routes file — MUST use raw body parser for this route
// app.post('/stripe/webhook', express.raw({ type: 'application/json' }), stripeController.handleWebhook);