Security and Compliance
Related docs:
Payment Architecture·SDLC/REQUIREMENTS·Roles & Permissions·Fraud Detection
1. PCI-DSS SAQ A Compliance
Pakashop qualifies for PCI-DSS SAQ A (the most reduced scope) because it never collects, processes, transmits, or stores raw card data.
| Principle | Implementation |
|---|---|
| No raw card data | Card payments redirect to Flutterwave's hosted payment page; Pakashop never sees card numbers, CVV, or expiry |
| No card data in logs | ComplianceLogger scrubs PANs matching /\b\d{13,19}\b/ from all log lines |
| Encrypted transport | All API traffic over TLS 1.2+ (Cloudflare + Nginx with Cloudflare Origin Certificate) |
| Scope boundary | Flutterwave is the Cardholder Data Environment (CDE); Pakashop is out of scope |
Prohibited code patterns:
// NEVER do this — collecting card data on Pakashop
const { cardNumber, cvv, expiry } = req.body;
// Correct — redirect to hosted page
const { redirectUrl } = await FlutterwaveAdapter.createPaymentLink(order);
res.json({ type: 'redirect', redirectUrl });
2. Bank of Zambia (BoZ) NPS Act
Pakashop operates as a marketplace (not a payment service provider), so it does not require a BoZ payment service licence. However, it must comply with the NPS Act through its licensed partners.
2.1 Delayed Settlement (Escrow-Avoidance Model)
Pakashop does not hold customer funds. Instead:
- PawaPay or Flutterwave (both BoZ-licensed) collects and holds the funds.
- Pakashop instructs disbursement only after delivery confirmation (PIN + digital signature).
- The
SettlementLedgertracks each order item throughHELD → RELEASABLE → PAID_OUT.
This model avoids the need for a BoZ escrow licence while still providing buyer protection.
2.2 Licensed Providers
| Provider | Licence Type | Authority |
|---|---|---|
| PawaPay | Mobile Money Operator / Payment Aggregator | Bank of Zambia |
| Flutterwave | Payment Service Provider | Bank of Zambia (via ZambiaPayments licence) |
3. Zambia Data Protection Act 2021
3.1 Consent
- Mobile money payments require an explicit consent checkbox: "I confirm that the mobile money wallet is registered in my name and I agree to Pakashop's Terms of Service."
- Consent is logged with a timestamp in
Order.consentGivenAt. - Marketing emails require a separate opt-in; transactional emails do not.
3.2 Data Minimisation
- Phone numbers in logs are masked:
+26097*****56. - Email addresses in logs are masked:
jo***@example.com. - Full PAN patterns are scrubbed by regex before any log write.
- The
ComplianceLoggerenforces all of the above centrally.
// ComplianceLogger.scrub() strips PII automatically
logger.info({ orderId, phone: complianceLogger.maskPhone(phone) }, 'Payment initiated');
3.3 Data Processor Agreements (DPAs)
Data processor agreements must be in place with:
- PawaPay (phone numbers shared for payment processing)
- Flutterwave (email, phone shared for card payment and KYC)
- Cloudinary (product images and KYC document storage)
- Sightengine (images analysed for moderation)
- Resend (email addresses for transactional email)
- Twilio (phone numbers for SMS/WhatsApp)
- Middleware.io (logs and traces for observability)
3.4 Breach Notification
Per the Zambia DPA 2021, the Data Controller (Pakashop) must notify the Data Protection Commission within 72 hours of discovering a personal data breach. The incident response process is documented in SDLC/MAINTENANCE.md.
3.5 Data Subject Rights
Pakashop must provide mechanisms for:
- Access: User can request their personal data via account settings.
- Rectification: User can update profile data.
- Erasure: Account deletion removes personal data; anonymises order history for audit trail.
- Portability: Data export in JSON format (planned feature).
4. General Security Measures
4.1 Authentication and Authorisation
| Mechanism | Implementation |
|---|---|
| Authentication | JWT-based; accessToken (24h TTL) + refreshToken (30d) |
| Session storage | Redis session:{sessionId} with rolling expiry |
| Role enforcement | requireRoles('PLATFORM_ADMIN') middleware on all admin routes |
| OAuth | Google OAuth via OAuthService.js; no passwords stored for OAuth users |
| MFA | Mandatory for elevated roles; TOTP (speakeasy), email OTP, SMS (Twilio) |
4.2 Multi-Factor Authentication (MFA)
Pakashop enforces a strict, robust Multi-Factor Authentication (MFA) system to secure accounts, particularly partner and administrative roles.
4.2.1 Role-Based Enforcement Rules
- Elevated Roles (Mandatory): Users with the role of
PLATFORM_ADMIN,SHOP_OWNER,DELIVERY_AGENT,MODERATOR,FRAUD_ANALYST,FINANCE_ADMIN, orFLEET_MANAGERmust enroll in and verify at least one MFA channel. Upon initial registration or login, if MFA is not enabled, the system intercepts the session and issues a restrictedmfa_setuptoken with a 30-minute expiration. The user cannot access protected resources or issue session cookies until MFA setup is completed. - Standard Roles (Optional): Users with the role of
CUSTOMER,SERVICE_PROVIDER, orSUPPORT_AGENTcan optionally enroll in MFA via their profile configuration.
4.2.2 Supported Channels
The system supports three fully redundant verification methods:
-
Authenticator App (TOTP):
- Engine: Powered by
speakeasy(standard RFC 6238 TOTP) andqrcodefor visual provisioning. - Security: base32 secrets are encrypted using
aes-256-cbcwith a SHA-256 derived key (viaTOTP_ENCRYPTION_KEYorJWT_SECRET) prior to database storage. - Timing: 30-second steps with 1-step clock drift window allowed for latency protection.
- Engine: Powered by
-
Email Verification:
- Engine: Transactional emails containing 6-digit cryptographically secure numeric codes.
- Database: Tracked in the
EmailOtpdatabase table with limited retry attempts and a 10-minute validity duration.
-
SMS / WhatsApp:
- Engine: Powered by Twilio Verify service.
- Resilience: Integrates strict try-catch handlers to catch Twilio outages or invalid configuration gracefully, raising a custom
SMS_SERVICE_UNAVAILABLEerror instead of breaking the main API gateway or auth pipeline.
4.2.3 Challenge Tokens and Intermediate State
Partial authorization sessions do not receive cookies. Instead, the backend operates a secure intermediate state machine:
- Challenge Token: A short-lived JWT token (5-minute expiration) with the
mfa_loginclaim and user metadata. - Multi-Method Selection: If a user has multiple active verified channels, they are presented with a method selection screen where they can choose to receive their OTP code.
- Verification Routing: The POST
/api/auth/2fa/verify-loginendpoint decodes the challenge token, validates the code against the specific method selected, and initiates the final cookie-based Express session on success.
4.3 Transport Security
- All traffic served over TLS 1.2+ enforced by Cloudflare (Full Strict mode).
Strict-Transport-Security(HSTS) header set with 1-yearmax-age.- Cloudflare WAF blocks OWASP Top 10 attack patterns (SQLi, XSS, path traversal).
4.4 Input Validation
- All request bodies validated using
express-validatoror Prisma type constraints. - File upload types validated server-side (
multer+ MIME type check) before Cloudinary upload. - Payment amounts are always re-calculated server-side from order items; client-supplied totals are rejected.
4.5 Rate Limiting
| Endpoint class | Limit | Window |
|---|---|---|
| General API | 1,000 req | 15 min |
| Authentication | 20 req | 15 min |
| Payment initiation | 20 req | 15 min |
| Webhook ingestion | Unlimited (IP-whitelisted from gateway IPs) |
Rate limiting backed by Redis (express-rate-limit with ioredis store).
4.6 CSRF Protection
- The Express API uses
SameSite=Strictcookies for session tokens. - API calls from the Next.js frontend include the
Authorization: Bearer <token>header; no cookie-based auth on the API layer.
4.7 Logging Without PII
All logs route through the ComplianceLogger which applies the following scrubbing rules before writing:
| Data type | Masking rule |
|---|---|
| Phone number | +26097*****56 (keep first 5, last 2 digits) |
| Email address | jo***@example.com (mask local part after first 2 chars) |
| Credit/debit card PAN | Replaced with [CARD-REDACTED] |
| National ID (NRC) | 12****/10/1 (mask first segment; last 4 digits) |
4.8 Webhook Security
| Provider | Verification method |
|---|---|
| PawaPay | HMAC-SHA256 over raw request body; secret from PAWAPAY_WEBHOOK_SECRET |
| Flutterwave | verif-hash header compared to FLUTTERWAVE_SECRET_HASH |
Webhook endpoints use express.raw() (not express.json()) so the raw bytes are preserved for HMAC verification before parsing.
4.9 Dependency Security
npm auditruns in CI on every pull request.- Dependabot alerts are reviewed weekly.
- Node.js version pinned in
.nvmrc; updated quarterly. - OWASP ZAP baseline scan runs weekly on staging.
4.10 API Gateway Dual-Key Security
All requests to the gateway must include one of:
| Header | Value | Used By |
|---|---|---|
x-pakashop-key | Client API key | Frontend, mobile apps, external integrations |
x-internal-key | Internal service key | Microservices calling each other |
// Gateway middleware
const validateGatewayKey = (req, res, next) => {
const clientKey = req.headers['x-pakashop-key'];
const internalKey = req.headers['x-internal-key'];
if (clientKey === process.env.PAKASHOP_CLIENT_KEY) {
req.keyType = 'external';
return next();
}
if (internalKey === process.env.INTERNAL_API_KEY) {
req.keyType = 'internal';
return next();
}
return res.status(401).json({
success: false,
code: 'GATEWAY_001',
message: 'Invalid or missing API key'
});
};
5. Fraud Detection
Pakashop implements a dedicated fraud detection layer via the pakashop-fraud microservice (port 3006).
5.1 Real-Time Rules Engine
The fraud service evaluates every payment initiation in parallel:
| Rule | Threshold | Action |
|---|---|---|
| Velocity | >5 payments/hour from same IP | Flag for review |
| Amount Anomaly | >3x average order value | Flag for review |
| Self-Dealing | Buyer and seller same account | Block immediately |
| New Device | First payment from new device | Elevated scrutiny |
| Geographic | Payment from unexpected location | Flag for review |
5.2 Risk Scoring
Composite risk score (0-100) based on weighted rule triggers:
- Score < 30: Pass automatically
- Score 30-70: Flag for admin review (payment blocked pending review)
- Score > 70: Block immediately
5.3 Admin Review Queue
Blocked payments appear in the fraud analyst dashboard:
FRAUD_ANALYSTandPLATFORM_ADMINroles can review- Approve: payment proceeds normally
- Reject: order cancelled, customer notified
- All actions logged with
traceIdcorrelation
See fraud-detection.md for full details.
6. Content Moderation
All uploaded images pass through the pakashop-moderation microservice (Python, port 3110) which calls Sightengine for automated NSFW/violence detection.
- Flagged images are hidden from public display pending admin review.
MODERATORandPLATFORM_ADMINroles can approve/reject/escalate flagged content.- KYC documents are hidden by default until explicitly approved.
See content-moderation.md for full details.
7. Role-Based Access Control (RBAC)
Pakashop implements 12 roles with granular permissions:
| Role | Description | MFA Required |
|---|---|---|
CUSTOMER | End consumer | Optional |
SHOP_OWNER | Approved vendor | Mandatory |
SERVICE_PROVIDER | Service vendor | Optional |
DELIVERY_AGENT | Delivery partner | Mandatory |
PLATFORM_ADMIN | Full admin | Mandatory |
MODERATOR | Content moderator | Mandatory |
FRAUD_ANALYST | Fraud reviewer | Mandatory |
FINANCE_ADMIN | Financial ops | Mandatory |
SUPPORT_AGENT | Customer support | Optional |
SELLER | Pending vendor | N/A |
FLEET_MANAGER | Courier admin | Mandatory |
SYSTEM | Automated processes | N/A |
See roles-permissions.md for the full permissions matrix.
For internal use only. Do not distribute outside Pakashop engineering.