// require('dotenv').config();
const express = require('express');
const axios = require('axios');
const bodyParser = require('body-parser');
const app = express();
const PORT = 7860;
// ==========================================
// CONFIGURATION
// ==========================================
// REPLACE THIS WITH YOUR PAYSTACK SECRET KEY
const PAYSTACK_SECRET_KEY = 'sk_test_4aa42b4105d33d191eda94d543a2a82ad3c420c7';
// const PAYSTACK_SECRET_KEY = 'sk_live_31c44ef2b81a591d771202b7ffcbcaab765e977e';
// MOCK DATABASE
// In a real app, you would save these details to MongoDB, Postgres, etc.
let mockUserDB = {
email: null,
customer_code: null,
authorization_code: null, // This is the "Token" to charge the card later
last_log: "Server started. Waiting for user input..."
};
// Middleware
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
// Helper to log to console and "DB"
const log = (msg) => {
const timestamp = new Date().toLocaleTimeString();
console.log(`[${timestamp}] ${msg}`);
mockUserDB.last_log = `[${timestamp}] ${msg}`;
};
// ==========================================
// ROUTES
// ==========================================
// 1. Render the Embedded HTML Interface
app.get('/', (req, res) => {
// We check if we have a saved token to adjust the UI state
const isAuthorized = !!mockUserDB.authorization_code;
const html = `
Paystack Card Tokenization Demo
💳 Paystack Recurring Charge Demo
Status: ${isAuthorized ? '✅ Card Authorized' : '❌ No Card stored'}
Customer Email: ${mockUserDB.email || 'N/A'}
Auth Code: ${mockUserDB.authorization_code || 'N/A'}
${!isAuthorized ? `
Step 1: Authorize Card
We will charge a tiny amount (e.g., NGN 50) to authorize the card.
` : `
Step 2: Charge Saved Card
The card is tokenized. You can now charge it anytime without user interaction.
Charge NGN 100.00 Now
Reset Demo
`}
Server Logs
${mockUserDB.last_log}
`;
res.send(html);
});
// 2. Initialize Transaction (Get Redirect URL)
app.post('/initialize', async (req, res) => {
const { email } = req.body;
// Amount is in Kobo (or lowest currency unit). 5000 kobo = 50 Naira.
// Paystack requires a small amount to verify the card (which can be refunded later).
const amount = 5000;
log(`Initializing transaction for ${email}...`);
try {
const response = await axios.post('https://api.paystack.co/transaction/initialize', {
email: email,
amount: amount,
// Important: This is where Paystack returns the user after payment
callback_url: `https://pepguy-authorize-card.hf.space/callback`,
channels: ['card'] // Force card payment to ensure we get an auth token
}, {
headers: { Authorization: `Bearer ${PAYSTACK_SECRET_KEY}` }
});
const authUrl = response.data.data.authorization_url;
log(`Redirecting user to Paystack: ${authUrl}`);
// Save email temporarily
mockUserDB.email = email;
// Redirect user to Paystack secure page
res.redirect(authUrl);
} catch (error) {
log(`Error initializing: ${error.response ? error.response.data.message : error.message}`);
res.send("Error initializing transaction. Check console.");
}
});
// 3. Callback (Handle Return from Paystack)
app.get('/callback', async (req, res) => {
const reference = req.query.reference; // Paystack sends this in the URL
log(`Callback received. Verifying reference: ${reference}...`);
try {
// Verify the transaction to get the Authorization Code
const response = await axios.get(`https://api.paystack.co/transaction/verify/${reference}`, {
headers: { Authorization: `Bearer ${PAYSTACK_SECRET_KEY}` }
});
const data = response.data.data;
if (data.status === 'success') {
// SUCCESS! We now have the Authorization Code (The Token)
const authCode = data.authorization.authorization_code;
const customerCode = data.customer.customer_code;
const cardType = data.authorization.card_type;
const last4 = data.authorization.last4;
// Save to "Database"
mockUserDB.authorization_code = authCode;
mockUserDB.customer_code = customerCode;
mockUserDB.email = data.customer.email;
log(`SUCCESS! Card authorized. Type: ${cardType} *${last4}. Auth Code: ${authCode}`);
} else {
log(`Transaction failed or incomplete.`);
}
} catch (error) {
log(`Verification failed: ${error.message}`);
}
// Redirect back to our main page
res.redirect('/');
});
// 4. Charge the Saved Card (Recurring Charge)
app.post('/charge-token', async (req, res) => {
if (!mockUserDB.authorization_code) {
return res.status(400).json({ message: "No card authorized yet." });
}
// Amount to charge (e.g., 100 Naira = 10000 kobo)
const chargeAmount = 10000;
log(`Attempting to charge saved card (Auth: ${mockUserDB.authorization_code}) amount: ${chargeAmount}...`);
try {
const response = await axios.post('https://api.paystack.co/transaction/charge_authorization', {
email: mockUserDB.email,
amount: chargeAmount,
authorization_code: mockUserDB.authorization_code
}, {
headers: { Authorization: `Bearer ${PAYSTACK_SECRET_KEY}` }
});
const data = response.data.data;
if (response.data.status) {
log(`CHARGE SUCCESSFUL! Ref: ${data.reference} - Status: ${data.status}`);
res.json({ message: "Charge Successful! check logs." });
} else {
log(`Charge Failed: ${response.data.message}`);
res.json({ message: "Charge Failed." });
}
} catch (error) {
const errMsg = error.response ? error.response.data.message : error.message;
log(`API Error during charge: ${errMsg}`);
res.status(500).json({ message: `Error: ${errMsg}` });
}
});
// Utility: Get Logs for Frontend
app.get('/logs', (req, res) => {
res.json({ log: mockUserDB.last_log });
});
// Utility: Reset Demo
app.get('/reset', (req, res) => {
mockUserDB = { email: null, customer_code: null, authorization_code: null, last_log: "System Reset." };
res.redirect('/');
});
// Start Server
app.listen(PORT, () => {
console.log(`Server running at http://localhost:${PORT}`);
console.log(`Make sure to set your PAYSTACK_SECRET_KEY in code or .env`);
});