Data Protection Duties When Storing Verification Results
Store only the verification result (verified or not), the timestamp, and a reference to the Paystack customer. Do not store raw BVN numbers unless legally required. Encrypt personal data at rest. Set retention periods (typically 2 to 7 years for financial records). Log consent before every verification. Give users the right to access and request deletion of their data, subject to regulatory retention requirements.
What Data You Collect Through Identity APIs
Each Paystack identity API returns different data. Know what you are collecting.
Resolve Account Number returns: the account holder name, account number (echoed back), and bank ID. The account holder name is personal data. The account number is sensitive personal data in the financial context.
Resolve Card BIN returns: card brand, issuing bank, card type, and country. This data is not personally identifiable on its own. The first 6 digits of a card cannot identify a specific person.
Validate Customer (BVN) returns: a match or mismatch result via webhook. If you submitted the BVN to make the call, you temporarily handled the BVN number, which is highly sensitive personal data in Nigeria.
The personal data you collect triggers obligations under the Nigeria Data Protection Act (NDPA, successor to NDPR), the Kenya Data Protection Act, and similar laws in Ghana and South Africa.
Data Minimization: Store Only What You Need
Data minimization is a core principle in every African data protection law. Collect and store only the data you need for the purpose you stated.
For account resolution: Store the resolved name (you need it for name matching) and the timestamp (you need it for audit trails). You do not need to store the raw bank ID unless you use it for something specific.
For BVN verification: Store the verification result (verified or not verified) and the timestamp. Do not store the BVN number itself. After the verification call completes and you record the result, discard the BVN from your application memory.
// good-storage.js
// DO: Store the result
await db.query(
'INSERT INTO identity_verifications (user_id, type, verified, verified_at) VALUES ($1, $2, $3, NOW())',
[userId, 'bvn', true]
);
// DO NOT: Store the raw BVN
// await db.query('INSERT INTO users (bvn) VALUES ($1)', [bvnNumber]); // Bad practice
If you must store the BVN (some regulatory requirements may demand it): encrypt it with a dedicated encryption key separate from your database encryption. Restrict access to the decryption key. Log every access to the encrypted BVN field. Set a retention period and delete it when the period expires.
Encrypting Verification Data at Rest
All personal data from identity verifications should be encrypted at rest in your database.
// encrypt-field.js
var crypto = require('crypto');
var ENCRYPTION_KEY = process.env.IDENTITY_ENCRYPTION_KEY; // 32-byte key
var IV_LENGTH = 16;
function encrypt(text) {
var iv = crypto.randomBytes(IV_LENGTH);
var cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(ENCRYPTION_KEY, 'hex'), iv);
var encrypted = cipher.update(text);
encrypted = Buffer.concat([encrypted, cipher.final()]);
return iv.toString('hex') + ':' + encrypted.toString('hex');
}
function decrypt(text) {
var parts = text.split(':');
var iv = Buffer.from(parts[0], 'hex');
var encrypted = Buffer.from(parts[1], 'hex');
var decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(ENCRYPTION_KEY, 'hex'), iv);
var decrypted = decipher.update(encrypted);
decrypted = Buffer.concat([decrypted, decipher.final()]);
return decrypted.toString();
}
// Usage
var encryptedName = encrypt('OKAFOR JOHN DOE');
await db.query(
'UPDATE account_resolution_cache SET account_name_encrypted = $1 WHERE id = $2',
[encryptedName, cacheId]
);
Use a separate encryption key for identity data. Do not reuse your general application encryption key. If your application key is compromised, your identity data stays protected (and vice versa).
Database-level encryption is also an option. PostgreSQL Transparent Data Encryption (TDE) and similar features encrypt the entire database at rest. This is simpler but less granular than field-level encryption.
Logging Consent
Before calling any identity API, log that the user consented to the verification.
-- consent_log table
CREATE TABLE verification_consent_log (
id SERIAL PRIMARY KEY,
user_id INTEGER NOT NULL,
verification_type TEXT NOT NULL,
consent_text TEXT NOT NULL,
consented_at TIMESTAMP NOT NULL DEFAULT NOW(),
ip_address INET,
user_agent TEXT
);
-- Example consent texts
-- 'I agree to verify my bank account details with Paystack for payout purposes.'
-- 'I agree to verify my identity (BVN) for enhanced account verification.'
What constitutes valid consent:
- The user was told what data would be checked and why.
- The user took an explicit action (clicked a button, submitted a form).
- The consent was not bundled with unrelated actions.
- The user could decline without being blocked from the entire product (only from the features requiring verification).
Store the exact consent text shown to the user. If you change the consent wording later, old records should reflect what the user actually agreed to at the time.
Setting Retention Policies
Do not keep identity data forever. Set retention periods based on regulatory requirements and delete data when the period expires.
Financial record retention: Most African jurisdictions require financial institutions to keep records for 5 to 7 years. If you are not a financial institution, your requirements may be shorter. Consult your legal team.
Verification results: Keep for the duration of the customer relationship plus the required retention period (typically 2 to 5 years after the last transaction).
Cached account names: Keep for 30 days for caching purposes. After 30 days, the cache entry expires and will be refreshed on the next lookup.
Consent records: Keep as long as the data they relate to exists, plus 1 year. If someone asks "when did I consent to BVN verification?" you need the answer.
// cleanup-job.js
// Run monthly
async function cleanupExpiredData() {
// Delete expired resolution cache
var deleted = await db.query(
'DELETE FROM account_resolution_cache WHERE expires_at < NOW() RETURNING id'
);
console.log('Deleted ' + deleted.rowCount + ' expired cache entries');
// Delete verification records past retention
var retentionYears = 5;
var deletedVerifications = await db.query(
'DELETE FROM identity_verifications ' +
'WHERE verified_at < NOW() - INTERVAL '' + retentionYears + ' years' ' +
'AND user_id NOT IN (SELECT user_id FROM active_users) ' +
'RETURNING id'
);
console.log('Deleted ' + deletedVerifications.rowCount + ' expired verifications');
}
Honouring Data Subject Rights
Under the Kenya DPA, Nigeria NDPA, and GDPR (if you serve EU residents), users have specific rights over their personal data.
Right to access. If a user asks "what identity data do you have about me?" you must be able to answer. Build an endpoint or admin tool that compiles all verification data for a given user.
Right to rectification. If verification data is wrong (misspelled name, wrong verification result), the user can request correction. You may need to re-run the verification to confirm the correct data.
Right to deletion. A user can request deletion of their personal data. However, if regulatory requirements mandate that you keep financial records for a certain period, you can decline deletion until the retention period expires. Explain this to the user clearly.
Right to know the purpose. Users can ask why you collected their identity data and what you are doing with it. Your answer should match what you told them at consent time.
// data-export.js
async function exportUserIdentityData(userId) {
var verifications = await db.query(
'SELECT verification_type, verified, verified_at FROM identity_verifications WHERE user_id = $1',
[userId]
);
var consents = await db.query(
'SELECT verification_type, consent_text, consented_at FROM verification_consent_log WHERE user_id = $1',
[userId]
);
var cached = await db.query(
'SELECT account_number, bank_code, account_name, resolved_at ' +
'FROM account_resolution_cache WHERE account_number IN ' +
'(SELECT bank_account_number FROM kyc_verifications WHERE user_id = $1)',
[userId]
);
return {
verifications: verifications.rows,
consents: consents.rows,
cached_resolutions: cached.rows,
};
}
Purpose Limitation
Data collected for identity verification must only be used for identity verification. This is called purpose limitation, and every major data protection law enforces it.
What you can do: Use the resolved account name for name matching in payout flows. Use the verification result to determine KYC tier and transaction limits. Use the data for fraud detection directly related to payment processing.
What you cannot do: Use resolved account names to build marketing profiles. Use BVN verification data for credit scoring unless the user explicitly consented to that specific purpose. Share verification data with third parties for purposes unrelated to payment processing.
If you want to use identity data for a new purpose (say, credit scoring), you need fresh consent from the user for that specific purpose. The original consent for payment verification does not cover credit scoring.
What to Do If Verification Data Is Breached
If your database is compromised and identity verification data is exposed, you have legal obligations.
Nigeria NDPA: Report the breach to the Nigeria Data Protection Commission (NDPC) within 72 hours. Notify affected users if the breach poses a high risk to their rights.
Kenya DPA: Report to the Office of the Data Protection Commissioner (ODPC) within 72 hours. Notify affected data subjects.
Practical steps:
- Contain the breach: identify how the data was accessed and close the vulnerability.
- Assess the impact: how many users are affected? What data was exposed?
- Notify the regulator within 72 hours with details of the breach.
- Notify affected users with clear language: what happened, what data was exposed, what you are doing about it, and what they should do.
- If BVN data was exposed, advise users to contact their banks and monitor their accounts for suspicious activity.
This is why data minimization matters. If you only stored verification results (pass/fail) and not raw BVNs, a breach exposes far less sensitive data.
Key Takeaways
- ✓Apply data minimization: store the verification result (pass/fail), not the raw identity data. You do not need the BVN itself after verification.
- ✓Encrypt personal data at rest. Account names, verification results, and customer codes should be in encrypted database columns or an encrypted database.
- ✓Set retention periods. Financial regulations typically require 2 to 7 years of record keeping. After the retention period, delete the data.
- ✓Log consent before every identity check. Record when the user consented, what they consented to, and how they were informed.
- ✓Honour data subject access requests. Users have the right to know what data you hold about them and to request corrections.
- ✓Keep verification data separate from marketing data. Identity information collected for KYC should never be used for advertising.
Frequently Asked Questions
- Do I need to register as a data controller to store verification results?
- In Nigeria, organizations that process personal data are required to file a compliance audit with the NDPC. In Kenya, you may need to register with the ODPC depending on the scale and nature of your data processing. Consult your legal team for your specific situation.
- Can I store verification data in a cloud database outside Africa?
- The Kenya DPA requires that personal data of Kenyan citizens be processed in Kenya, with some exceptions. The Nigeria NDPA has cross-border transfer provisions that require adequate protection in the destination country. Check the specific requirements for your target markets before choosing a database region.
- How long should I keep BVN verification results?
- Keep the verification result (pass or fail) for the duration of the customer relationship plus your regulatory retention period, typically 5 to 7 years for financial records. If you stored the BVN number itself, delete it as soon as verification is complete unless regulation specifically requires you to retain it.
- What if a user asks me to delete their data but I need it for regulatory compliance?
- You can retain data required by law for the regulatory retention period, even if the user requests deletion. Inform the user that their data will be deleted after the retention period expires. You should still delete any data that is not required by regulation.
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