Bonaventure OgetoBy Bonaventure Ogeto|

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

DataLog?Reason
Transaction referenceYesCore debug identifier
Amount (in kobo)YesNeeded for reconciliation
Status (success/failed)YesTrack payment outcomes
Customer emailYes (masked)Use "u***@gmail.com" format
gateway_responseYesDebug declined cards
channelYesDebug payment method issues
ip_addressYesFraud detection
Paystack secret keyNeverFull account compromise
Authorization header (outbound)NeverContains secret key
authorization_codeNever in plaintextToken for charging card again
Card number (PAN)NeverYou should never see this anyway
CVVNeverYou 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

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