Build a paid job board by creating listings in "draft" state, initializing a Paystack payment for the listing fee, and publishing the listing only when the charge.success webhook fires. Support listing tiers (standard, featured, urgent) and optional employer subscription bundles for frequent posters.
Tables: employers (name, email, company, subscription_credits), listings (employer_id, title, description, location, type, salary, tier, status, paystack_ref, published_at, expires_at), listing_tiers (name, price, duration_days, features).
var LISTING_TIERS = {
standard: { price: 1500000, duration: 30, label: 'Standard 30-day listing' }, // NGN 15,000
featured: { price: 3000000, duration: 30, label: 'Featured listing — top placement' }, // NGN 30,000
urgent: { price: 2000000, duration: 14, label: 'Urgent — highlighted for 14 days' }, // NGN 20,000
};
async function createListing(employerEmail, employerId, listingData, tier) {
var tierConfig = LISTING_TIERS[tier];
if (!tierConfig) throw new Error('Invalid listing tier');
// Check if employer has subscription credits
var employer = await db.employers.findById(employerId);
if (employer.subscription_credits > 0) {
await db.employers.decrement(employerId, 'subscription_credits', 1);
return await publishListing(employerId, listingData, tier, null); // no payment needed
}
// Create listing in draft
var reference = 'LISTING-' + Date.now();
var listing = await db.listings.create({
employer_id: employerId, ...listingData,
tier, status: 'draft', paystack_ref: reference,
});
// Initialize payment
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: employerEmail, amount: tierConfig.price, currency: 'NGN', reference,
metadata: { listing_id: listing.id, tier, duration_days: tierConfig.duration },
label: tierConfig.label,
}),
});
return { listing, authorizationUrl: (await res.json()).data.authorization_url };
}
// Webhook: publish listing on payment
if (event.event === 'charge.success') {
var meta = event.data.metadata;
var publishedAt = new Date();
var expiresAt = new Date();
expiresAt.setDate(expiresAt.getDate() + meta.duration_days);
await db.listings.update(meta.listing_id, { status: 'published', published_at: publishedAt, expires_at: expiresAt });
await scheduleExpiryReminder(meta.listing_id, expiresAt);
}
// Employer buys a posting bundle — e.g., 5 posts for the price of 4
async function purchasePostingBundle(employerEmail, employerId, bundleSize) {
var BUNDLES = { 5: 6000000, 10: 10000000 }; // NGN 60k for 5, NGN 100k for 10
var price = BUNDLES[bundleSize];
if (!price) throw new Error('Invalid bundle size');
var reference = 'BUNDLE-' + Date.now();
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: employerEmail, amount: price, currency: 'NGN', reference,
metadata: { employer_id: employerId, bundle_credits: bundleSize, purpose: 'bundle_purchase' },
}),
});
return (await res.json()).data.authorization_url;
}
// Webhook: credit bundle
if (event.event === 'charge.success' && event.data.metadata.purpose === 'bundle_purchase') {
await db.employers.increment(event.data.metadata.employer_id, 'subscription_credits', event.data.metadata.bundle_credits);
}
// Cron: expire listings and send renewal reminders
async function processExpiredListings() {
var today = new Date();
await db.listings.updateWhere({ status: 'published', expires_at_lt: today }, { status: 'expired' });
var expiringSoon = await db.listings.findExpiringSoon(addDays(today, 5));
for (var listing of expiringSoon) {
await sendRenewalReminder(listing.employer_id, listing.id);
}
}
Get weekly developer tips
Join 25,000+ developers. Practical guides, job tips, and new content — straight to your inbox.
No spam. Unsubscribe anytime.
See build a classifieds site with featured listing payments for the same paid listing model applied to a broader classifieds platform.
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