In Strapi: create a custom route POST /api/payments/initialize that reads the product price from your Product content type, calls Paystack POST /transaction/initialize, and returns the authorization_url. Create POST /api/payments/webhook that validates HMAC and updates your Order content type to status "paid". Store PAYSTACK_SECRET_KEY in .env.
// src/api/payment/routes/payment.js (Strapi v4)
module.exports = {
routes: [
{ method: 'POST', path: '/payments/initialize', handler: 'payment.initialize', config: { auth: false } },
{ method: 'POST', path: '/payments/webhook', handler: 'payment.webhook', config: { auth: false } },
],
};
// src/api/payment/controllers/payment.js (Strapi v4)
const crypto = require('crypto');
module.exports = {
async initialize(ctx) {
var { productId, email } = ctx.request.body;
// Fetch price from Strapi Product content type
var product = await strapi.entityService.findOne('api::product.product', productId, { fields: ['name', 'price'] });
if (!product) return ctx.notFound('Product not found');
var reference = 'order_' + productId + '_' + Date.now();
// Create order record in Strapi
await strapi.entityService.create('api::order.order', {
data: { reference, product: productId, customer_email: email, status: 'pending' },
});
var res = await fetch('https://api.paystack.co/transaction/initialize', {
method: 'POST',
headers: { Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ email, amount: Math.round(product.price * 100), reference, currency: 'NGN' }),
});
var data = await res.json();
ctx.body = { url: data.data.authorization_url };
},
async webhook(ctx) {
var rawBody = ctx.request.rawBody; // requires Strapi raw body config
var signature = ctx.request.headers['x-paystack-signature'];
var expected = crypto.createHmac('sha512', process.env.PAYSTACK_WEBHOOK_SECRET).update(rawBody).digest('hex');
if (expected !== signature) return ctx.unauthorized();
var event = ctx.request.body;
if (event.event === 'charge.success') {
var orders = await strapi.entityService.findMany('api::order.order', {
filters: { reference: event.data.reference },
});
if (orders.length > 0) {
await strapi.entityService.update('api::order.order', orders[0].id, { data: { status: 'paid' } });
}
}
ctx.status = 200;
},
};
Get weekly developer tips
Join 25,000+ developers. Practical guides, job tips, and new content — straight to your inbox.
No spam. Unsubscribe anytime.
See the Paystack with headless CMS checkout guide for the full architecture.
KES 2,999
The definitive guide to building payment systems with Paystack — from your first transaction to production-grade payment infrastructure across Africa. 9 modules, 47 lessons. Free preview available.
Start Paystack Course