Logging Payments Without Logging Secrets
Safe to log: transaction reference, amount, status, customer email, gateway_response, channel, ip_address. Never log: your Paystack secret key, authorization_code (store encrypted instead), full card numbers (you should never see these), CVV, or the raw Authorization header from your outbound API requests. Mask sensitive values in logs using a sanitize function before writing to any log store.
What to Log and What Never to Log
| Data | Log? | Reason |
|---|---|---|
| Transaction reference | Yes | Core debug identifier |
| Amount (in kobo) | Yes | Needed for reconciliation |
| Status (success/failed) | Yes | Track payment outcomes |
| Customer email | Yes (masked) | Use "u***@gmail.com" format |
| gateway_response | Yes | Debug declined cards |
| channel | Yes | Debug payment method issues |
| ip_address | Yes | Fraud detection |
| Paystack secret key | Never | Full account compromise |
| Authorization header (outbound) | Never | Contains secret key |
| authorization_code | Never in plaintext | Token for charging card again |
| Card number (PAN) | Never | You should never see this anyway |
| CVV | Never | You should never see this |
Sanitizing Logs Before Writing
// logger.js — safe payment event logging
function maskEmail(email) {
if (!email || !email.includes('@')) return email;
var [local, domain] = email.split('@');
return local[0] + '***@' + domain;
}
function sanitizePaystackResponse(data) {
// Only extract the fields you need — never spread the whole object
return {
reference: data.reference,
amount: data.amount,
status: data.status,
gateway_response: data.gateway_response,
channel: data.channel,
ip_address: data.ip_address,
customer_email: maskEmail(data.customer?.email),
// Deliberately excluded: authorization.authorization_code, authorization.bin (full)
};
}
// In your webhook handler
if (event.event === 'charge.success') {
var safeData = sanitizePaystackResponse(event.data);
console.log(JSON.stringify({ event: 'charge.success', ...safeData }));
// Safe — none of the sensitive fields are in here
}
// When making outbound Paystack API calls — never log the full headers object
async function callPaystack(endpoint, body) {
var response = await fetch('https://api.paystack.co' + endpoint, {
method: 'POST',
headers: {
Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});
// Log the response, not the request headers
var data = await response.json();
console.log(JSON.stringify({ endpoint, status: response.status, ref: data.data?.reference }));
return data;
}
Learn More
This guide is part of the Paystack security guide.
Key Takeaways
- ✓Never log your Paystack secret key. If it appears in a log file, rotate it immediately.
- ✓Do not log the raw Authorization header from outbound Paystack API requests — it contains your secret key.
- ✓The authorization_code from a charge is safe to store encrypted in your database, but do not log it in plaintext.
- ✓Log: reference, amount, status, gateway_response, customer email — this is enough for debugging.
- ✓Sanitize request bodies before logging: replace the Authorization header value with "[REDACTED]".
- ✓Use structured logging (JSON) so you can filter out sensitive fields by key, not by regex over text.
Frequently Asked Questions
- What should I do if I find my Paystack secret key in a log file?
- Rotate the key immediately from the Paystack dashboard (Settings → API Keys). Then audit who had access to the log file and for how long. Check your Paystack transaction history for any unexpected API calls. Update your application with the new key before the old one is deactivated. Treat this as a security incident.
- Is it safe to log the customer email address?
- Email addresses are personal data — they should be masked in logs (show only the first character and domain). Full emails in logs create a data privacy risk if the logs are accessed by unauthorized parties, and in some jurisdictions may require you to include log stores in your data processing register.
- Can I store the authorization_code in my database?
- Yes — the authorization_code is a safe token for re-charging a card. Store it encrypted in your database (not in plaintext). It is useless without your Paystack secret key, but encryption adds defense in depth. Do not put it in application logs where it might be exported to third-party log aggregation services.
- My log aggregator (Datadog, Papertrail) collects all console.log output. Is this safe?
- Only if you sanitize before logging. Log aggregators index everything they receive — if your secret key ever appears in console.log output, it is stored in the aggregator's systems. Use the sanitize pattern above and never log raw request objects that include headers or environment variables.
Ready to build real-world apps?
Join the McTaba Labs full-stack marathon (4 months full-time · 6 months part-time). Learn M-Pesa, USSD, and WhatsApp engineering while shipping 8 production apps.
Apply to the McTaba Marathon