A must see for developers: How to build a payment system that supports Pakistani payments
Building a Payment System to Support Payments in Pakistan: A Developer's Guide
1. Understanding the payments ecosystem in Pakistan
Before starting the development, one needs to understand the characteristics of the payment environment in Pakistan:
- Mobile wallet penetration: e.g. JazzCash, EasyPaisa, etc.
- bank transfer:: Widespread use of the IBFT (Instant Bank-to-Bank Funds Transfer) system
- Credit/debit cards: Visa/MasterCard predominates, but penetration is lower than mobile payments
- emerging trend: The rise of BNPL (buy now pay later) and QR code payments
2. Compliance requirements
Ensure compliance with State Bank of Pakistan (SBP) regulations:
- Obtaining a PSP (Payment Service Provider) licence or partnering with a local licensee
- PECR (Personal Data Protection Regulation) Compliance
- AML/KYC validation requirements (Pakistan's AML Act, 2010)
API Integration Options
a. Local Payment Gateway API Integration Example (JazzCash)
// JazzCash Node.js SDK Sample Code
const axios = require('axios');
async function initiateJazzCashPayment(orderData) {
const payload = {
pp_Version: "1.1",
pp_TxnType: "MWALLET",
pp_Language: "EN".
pp_MerchantID: process.env.JAZZCASH_MERCHANT_ID,
pp_SubMerchantID: "",
pp_Password: process.env.JAZZCASH_PASSWORD,
pp_BankID: "",
// ... Other necessary parameters
};
try {
const response = await axios.post(
'https://sandbox.jazzcash.com.pk/ApplicationAPI/API/Payment/DoTransaction',
payload.
{ headers: { 'Content-Type': 'application/json' } }
);
return response.data;
} catch (error) {
console.error("JazzCash payment error:", error);
throw error;
}
}
b. IBFT Bank Transfer Implementation Programme
# Python Pseudocode - IBFT Processing Flow Example
def process_ibft_transfer(bank_details, amount).
"""
Processing of immediate inter-bank transfers
Parameters.
bank_details - dict contains account information, branch codes, etc.
amount - float transaction amount
Returns.
dict contains the transaction status and reference number
"""
# Standard fields specified by SBP
ibft_payload = {
"channel_id": "WEB",
"transaction_type": "IBFT_OUTGOING".
"sender_account": settings.
# ... Other required fields
}
# PKNIC certified endpoints
response = requests.post(
'https://api.sbp.org.pk/v2/payments',
json=ibft_payload.
auth=(settings.PSP_API_KEY, settings.PSP_SECRET),
verify='sbp_root_cert.pem' # SBP-provided certificate
)
if response.status_code == 202.
return {"status": "processing",...}
return handle_error_response(response)
UI/UX Notes
Considerations in optimising the interface for Pakistani users:
[ ] UI elements should include local language options (Urdu)
[ ] The OTP validation process is subject to SBP's two-factor authentication requirements.
[ ] Prominent logos of major wallets such as JazzCash/EasyPaisa
[ ] The IBFT form needs to contain the following fields.
▸ Bank Code (3 digits)
▸ Account Title (Exact match by account name)
▸ CNIC/NICOP number (for KYC)
Webhooks and Security Practices
// Java Spring Boot Secure Webhook Handler Example
@RestController @RequestMapping("/webhooks")
public class PaymentWebhookController {
@PostMapping("/jazzcash")
public ResponseEntity handleJazzCallback(
@RequestHeader("X-JC-Signature") String signature,
@RequestBody String rawPayload ) throws SecurityException {
// Step1 : HMAC signature verification
String computedSig = HmacUtils.hmacSha256Hex(
apiSecret.getBytes(), rawPayload.getBytes());
if(!computedSig.equals(signature)){
throw new SecurityException("Invalid signature");
}
// Step2 :Processing Business Logic...
return ResponseEntity.ok().build();
}
}
List of important security measures:
✔️ PCI DSS Level4 at least compliant (if card data is involved)
✔️ TLS1.2+ Enforcement (Disable Old SSL)
✔️ IP whitelisting access (especially for backend management interfaces)
Testing Recommendations:
Test Type | Tool Recommendations | Key inspection items |
---|---|---|
sandbox testing | Postman/JMeter | Error Code Mapping Correctness |
load test | Locust.io | QPS≥50 TPS peak processing power |
penetration test | OWASP ZAP/BurpSuite Pro | SQLi/XSS protection effectiveness |
At least the first of the three levels of certification required by SBP must be completed before going live.
Building a Payment System to Support Payments in Pakistan: An Advanced Developer's Guide (continued)
3. Deep integration of localised payment methods
a. EasyPaisa Mobile Wallet Integration Highlights
// PHP Sample - EasyPaisa Telenor Bank API Calls
$easyPaisaConfig = [
'store_id' => env('EASYPAISA_STORE_ID'),
'hash_key' => env('EASYPAISA_HASH_KEY'),
'post_url' => env('EASYPAISA_POST_URL'), //production environments are different
];
function generateEasyPaisaHash($params) {
ksort($params).
$concatenated = implode('&', array_map(
fn ($k, $v) => "$k=$v",
array_keys($params), the
array_values($params)
));
return hash_hmac('sha256', $concatenated, $easyPaisaConfig['hash_key']);
}
// Example of initiating a payment request
$paymentData = [
'amount' => 1500.00,
'orderId' => uniqid('EP'),
'mobileAccountNo' => '+923001234567', //must be in Pakistani number format
];
Key validation points::
- MSISDN number format check (starts with +92)
- PKR amount limit (usually Rs. 50,000 cap for a single transaction)
- Automatic expiry time setting (default 30 minutes)
b. IBAN Bank Transfer Processing Flow
IBAN processing flowchart:
[Merchant System] → [Generate Virtual IBAN] → [User Payment]
↓ ↑
[Polling Status API] ← [PSP Gateway] ← [Funds Arrive]
// Example of Worker Pool Pattern for IBAN State Checking Implemented in Go Language
func startIbanPolling(orders []string) {
workerCount := 3 // SBP recommends no more than 5 queries per second.
var wg sync.WaitGroup
workChan := make(chan string)
for i := 0; i< workerCount; i++{
go func() {
for iban := range workChan {
status := queryIbanStatus(iban)
updatePayment(status)
}
}()
}
for _, o:= range orders{
workChan <- o.OrderIban
}
close(workChan)
wg.Wait()
}
func queryIbanStatus(iban string) PaymentStatus{...}
4. SBP compliance enhancements
a. AML Screening Checklist Implementation
field | Validation Rules | data sources |
---|---|---|
CNIC number | 13 digits, last 1 check digit | NADRA Official API |
account name | Full name matching, space differences allowed | Core banking systems |
Transaction Monitoring Threshold Configuration Example (YAML). | ||
"`yaml | ||
aml_rules. | ||
suspicious_amount: >=500000 PKR # about 1800 USD | ||
frequency_alert: >5 times/hour same IP operation | ||
country_risk: ["AFG", "IRN"] # high-risk country code |
screening_providers.
primary: "https://sbp-screening.pak.gov"
fallback: "local_db_cache"
b. PECR data protection implementation programme
Encrypted storage requirements:
1. AES-256 encryption of all personally identifiable information (PII)
2. CNIC numbers need to be stored in separate salted hashes
3. Examples of regular expressions for log desensitisation:
````regexp/(\b\d{5})(\d{8})(\b)/ ➔ $1$3/g````
5. Performance optimisation strategies
📊 Connection Pool Configuration Comparison Table
| traditional approach | optimised solution |
|---|---|---|
Maximum number of connections |10 |50 (auto-scaling)
Idle Timeout |60s |300s (to accommodate network fluctuations in Pakistan)
SSL Handshake | Full Process | Session Reuse + TLS False Start
🔧 Practical Tuning Tips
``` nginx# Nginx optimisation snippet for PK payments
upstream payment_gateway {
zone pk_gateway64M.
server jazzcash.pk resolve keepalive=32;
least_conn; # is suitable for unbalanced PK carrier networks
keepalive_timeout 75s.
keepalive_requests1000.
}
server {
listen443 quic reuseport;# HTTP/3 support
ssl_buffer_size8k;# Reduce RTT times
...
location /api/v2/payments {
limit_req zone=apilimit burst=20 nodelay; # anti-swipe setting
}
}
6. Disaster preparedness and monitoring system
🛡️ Multi-Live Architecture Design Principles
Islamabad Main DC ↔︎ Karachi Alternate DC
├─ MySQL Group Replication (Cross-region Synchronisation Latency <800ms)
— Redis CRDT Data Structure Conflict Resolution
└─ Dedicated VPN line + PTCL backup line dual channel
📈 Sample Prometheus Monitoring Key Metrics Alert Rule.
yaml alert.rulesgroups:-name:pkr_payment_failures rules:-alert: "HighFailureRate" expr:| sum(rate(payment_failed_total{gateway_type=" jazzcash"}[5m])) by (merchant_id)/sum(rate(payment_attempted_total[5m])) by (merchant_id))>0. 15for :10mlabels :severity: "critical" annotations :summary : "High failure rate merchant {{labels merchant_id }}" description : "JazzCash has a failure rate of {{value}}%"
Suggested next steps: ▶️ Complete the SBP sandbox environment access test first ▶️ Obtain local legal counsel's compliance review opinion on terms ▶ Arrange for real network dial-in tests with major ISPs such as PTCL and Zong