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