Payment Service Provider Integration
Payment service provider integration connects your cash and voucher assistance platform to the financial infrastructure that delivers transfers to beneficiaries. This task covers the technical integration process from initial API configuration through production deployment, enabling your CVA platform to submit payment instructions and receive transaction status updates. Complete this task after selecting your PSP and CVA platform, and before processing live transfers.
Prerequisites
Gather the following before beginning integration work. Missing prerequisites cause the majority of integration delays.
Contractual and administrative
| Requirement | Detail | How to verify |
|---|---|---|
| PSP contract executed | Signed agreement covering transaction fees, settlement terms, liability | Contract copy with authorised signatures |
| Merchant account active | PSP has provisioned your organisation’s merchant account | PSP confirmation email with merchant ID |
| API access approved | PSP has enabled API access for your merchant account | API credentials issued (not necessarily activated) |
| Compliance documentation submitted | KYC/AML documentation accepted by PSP | PSP compliance approval confirmation |
| Test environment provisioned | Sandbox or UAT environment available | Test environment URL and credentials |
Technical access
You need the following credentials and access levels:
PSP credentials:
- API key or client ID for authentication
- API secret or client secret (store securely, never in code repositories)
- Merchant ID or account identifier
- Webhook signing secret (if PSP uses webhook signatures)
CVA platform access:
- Administrator access to CVA platform
- Permission to configure external integrations
- Access to platform API settings or integration module
Infrastructure access:
- Ability to configure outbound firewall rules (if applicable)
- DNS management access (for webhook endpoints)
- SSL certificate management (for webhook HTTPS endpoints)
Technical environment
Your environment must meet these requirements:
CVA Platform Requirements:- API integration module enabled- Webhook endpoint capability (HTTPS with valid certificate)- Secure credential storage (environment variables or secrets manager)
Network Requirements:- Outbound HTTPS (TCP 443) to PSP API endpoints- Inbound HTTPS (TCP 443) for webhook callbacks- Static IP or IP range (if PSP requires IP allowlisting)
Development Tools:- API testing tool (Postman, Insomnia, or curl)- JSON parser for response inspection- Log access for debuggingVerify your CVA platform version supports the PSP you are integrating. Check the platform’s integration documentation for supported PSPs and minimum version requirements.
Integration architecture
Before beginning configuration, understand the data flow between systems:
+-------------------------------------------------------------------+| CVA PLATFORM || || +------------------+ +------------------+ || | Beneficiary | | Payment | || | Registry +---->| Batch | || | (ID, account) | | Generator | || +------------------+ +--------+---------+ || | || +--------v---------+ || | PSP Integration | || | Module | || +--------+---------+ || | |+------------------------------------+------------------------------+ | (1) Payment | (4) Status Request | Update v+-------------------------------------------------------------------+| PSP API GATEWAY || || +------------------+ +------------------+ || | Authentication | | Rate Limiting | || | (API key/OAuth) | | (per merchant) | || +------------------+ +------------------+ || |+-------------------------------------------------------------------+ | (2) Process | (3) Delivery Payment | Confirmation v+-------------------------------------------------------------------+| PAYMENT RAILS || || +------------+ +------------+ +------------+ +------------+ || | Mobile | | Bank | | Agent | | Card | || | Money | | Transfer | | Network | | Network | || +------------+ +------------+ +------------+ +------------+ || |+-------------------------------------------------------------------+ | v +---------+----------+ | BENEFICIARY | | (receives funds) | +--------------------+The integration handles four primary flows: payment submission (1), payment processing (2), delivery confirmation from payment rails (3), and status updates back to your platform (4). Your configuration must address each flow.
Procedure
Phase 1: Test environment configuration
Configure PSP API credentials in your test environment. Store credentials in environment variables or your platform’s secrets manager, never in configuration files committed to version control.
For environment variable configuration:
# Set in deployment environment, not in code export PSP_API_KEY="test_key_abc123def456" export PSP_API_SECRET="test_secret_xyz789" export PSP_MERCHANT_ID="MERCHANT_TEST_001" export PSP_API_URL="https://sandbox.psp-example.com/api/v2"For CVA platforms with built-in credential storage, navigate to Settings > Integrations > Payment Providers and enter credentials in the designated fields. Enable the “Test Mode” or “Sandbox” toggle.
Configure the API endpoint URL for the test environment. PSPs provide separate URLs for sandbox and production:
Environment Typical URL pattern Sandbox https://sandbox.provider.com/api/v2UAT https://uat.provider.com/api/v2Production https://api.provider.com/api/v2Enter the sandbox URL in your CVA platform’s PSP configuration. Verify the URL responds:
curl -I https://sandbox.psp-example.com/api/v2/health # Expected: HTTP/2 200Test authentication by requesting an access token or making an authenticated test call. The method depends on your PSP’s authentication scheme.
For API key authentication:
curl -X GET "https://sandbox.psp-example.com/api/v2/account/balance" \ -H "Authorization: Bearer test_key_abc123def456" \ -H "X-Merchant-ID: MERCHANT_TEST_001"For OAuth 2.0 client credentials:
curl -X POST "https://sandbox.psp-example.com/oauth/token" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=client_credentials" \ -d "client_id=test_key_abc123def456" \ -d "client_secret=test_secret_xyz789"Expected response (OAuth):
{ "access_token": "eyJhbGciOiJSUzI1NiIs...", "token_type": "Bearer", "expires_in": 3600 }Store the token expiry time and implement token refresh before expiry. Most OAuth tokens expire in 1 hour (3600 seconds).
- Configure field mapping between your CVA platform and the PSP API. Your platform stores beneficiary data in its schema; the PSP expects data in its schema. Map fields explicitly:
CVA Platform Field PSP API Field -------------------------------------------------- beneficiary.id recipient.external_id beneficiary.full_name recipient.name beneficiary.phone_number recipient.mobile_number beneficiary.account_number recipient.account transfer.amount transaction.amount transfer.currency transaction.currency transfer.reference transaction.reference programme.id metadata.programme_idMost CVA platforms provide a field mapping interface. Configure each required field. Mark mandatory PSP fields that lack CVA platform equivalents and determine how to populate them (static values, derived values, or platform customisation).
- Configure webhook endpoint for status callbacks. The PSP sends transaction status updates to a URL you specify. Create or identify the webhook endpoint in your CVA platform:
Webhook URL: https://cva.example.org/api/webhooks/psp-status
Requirements: - HTTPS with valid SSL certificate (not self-signed) - Responds within 5 seconds (PSP timeout threshold) - Returns HTTP 200 for successful receipt - Publicly accessible (not behind VPN)Register the webhook URL with the PSP:
curl -X POST "https://sandbox.psp-example.com/api/v2/webhooks" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "url": "https://cva.example.org/api/webhooks/psp-status", "events": ["payment.completed", "payment.failed", "payment.reversed"], "secret": "webhook_signing_secret_abc123" }'The PSP returns a webhook ID. Store this for future management:
{ "id": "whk_abc123", "url": "https://cva.example.org/api/webhooks/psp-status", "status": "active", "events": ["payment.completed", "payment.failed", "payment.reversed"] }- Configure webhook signature verification. PSPs sign webhook payloads to prevent spoofing. Implement verification in your webhook handler:
import hmac import hashlib
def verify_webhook_signature(payload, signature, secret): """Verify PSP webhook signature using HMAC-SHA256.""" expected = hmac.new( secret.encode('utf-8'), payload.encode('utf-8'), hashlib.sha256 ).hexdigest() return hmac.compare_digest(expected, signature)
# In webhook handler: signature = request.headers.get('X-PSP-Signature') if not verify_webhook_signature(request.body, signature, WEBHOOK_SECRET): return Response(status=401) # Reject unsigned/invalid webhooksNever process webhooks without signature verification in production.
Phase 2: Test transactions
Create test beneficiaries in your CVA platform using PSP-provided test credentials. PSPs provide test phone numbers and account numbers that simulate successful and failed transactions:
Test identifier Expected behaviour +255700000001 Success, instant +255700000002 Success, delayed (30 seconds) +255700000003 Failure, insufficient balance +255700000004 Failure, invalid account +255700000005 Failure, timeout Create beneficiary records using these test identifiers. Ensure each test scenario has a corresponding beneficiary.
Submit a single test payment to verify end-to-end flow. Select one test beneficiary and create a transfer:
curl -X POST "https://sandbox.psp-example.com/api/v2/payments" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: test-payment-001" \ -d '{ "recipient": { "external_id": "BEN-001", "name": "Test Beneficiary One", "mobile_number": "+255700000001" }, "transaction": { "amount": 50000, "currency": "TZS", "reference": "CVA-2024-001-001" }, "metadata": { "programme_id": "PROG-001", "distribution_id": "DIST-001" } }'Expected response:
{ "id": "pay_abc123", "status": "pending", "recipient": { "external_id": "BEN-001", "mobile_number": "+255700000001" }, "transaction": { "amount": 50000, "currency": "TZS", "reference": "CVA-2024-001-001" }, "created_at": "2024-11-16T10:30:00Z" }Note the payment ID (pay_abc123) for status tracking.
- Verify webhook receipt. Within 30 seconds of submission, your webhook endpoint should receive a status callback:
{ "event": "payment.completed", "payment_id": "pay_abc123", "status": "completed", "completed_at": "2024-11-16T10:30:15Z", "receipt": { "provider_reference": "MPESA-ABC123XYZ", "channel": "mobile_money" } }Check your webhook logs to confirm receipt. If no webhook arrives within 60 seconds, verify webhook configuration and check PSP webhook delivery logs (most PSPs provide a webhook history view).
- Verify status synchronisation. Confirm your CVA platform updated the transfer status based on the webhook:
-- Check transfer status in CVA platform database SELECT transfer_id, status, psp_reference, updated_at FROM transfers WHERE psp_payment_id = 'pay_abc123';
-- Expected result: -- transfer_id | status | psp_reference | updated_at -- TRF-001 | completed | MPESA-ABC123XYZ | 2024-11-16 10:30:16If using the CVA platform interface, navigate to the transfer record and verify status shows “Completed” with the PSP reference number.
- Test failure scenarios using PSP test credentials that simulate failures. Submit payments to test accounts configured for failure:
# Test insufficient balance failure curl -X POST "https://sandbox.psp-example.com/api/v2/payments" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: test-payment-fail-001" \ -d '{ "recipient": { "external_id": "BEN-003", "mobile_number": "+255700000003" }, "transaction": { "amount": 50000, "currency": "TZS", "reference": "CVA-2024-001-003" } }'Expected webhook:
{ "event": "payment.failed", "payment_id": "pay_def456", "status": "failed", "failure_reason": "insufficient_balance", "failure_message": "Provider account has insufficient balance" }Verify your CVA platform correctly records the failure status and reason.
- Test batch payment submission. Most CVA distributions involve multiple payments submitted together. Create a batch of 10 test payments covering success and failure scenarios:
curl -X POST "https://sandbox.psp-example.com/api/v2/batches" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: test-batch-001" \ -d '{ "batch_reference": "DIST-2024-001", "payments": [ { "external_id": "BEN-001", "mobile_number": "+255700000001", "amount": 50000, "currency": "TZS" }, { "external_id": "BEN-002", "mobile_number": "+255700000002", "amount": 50000, "currency": "TZS" } // ... additional payments ] }'Expected response:
{ "batch_id": "bat_xyz789", "status": "processing", "total_payments": 10, "total_amount": 500000, "currency": "TZS" }Monitor batch completion through webhooks or polling the batch status endpoint.
Phase 3: Production configuration
Complete PSP production onboarding. Request production API credentials from your PSP account manager. Production credentials are separate from test credentials and require additional verification:
- Confirm bank account for settlement
- Complete any outstanding compliance documentation
- Sign production environment terms (if separate from main contract)
- Request IP allowlisting for your production servers (if required)
Production credential issuance takes 1 to 5 business days depending on the PSP.
Configure production credentials in your CVA platform. Use a separate configuration profile for production, distinct from test configuration:
# Production environment variables export PSP_API_KEY="prod_key_secure123" export PSP_API_SECRET="prod_secret_secure456" export PSP_MERCHANT_ID="MERCHANT_PROD_001" export PSP_API_URL="https://api.psp-example.com/api/v2" export PSP_ENVIRONMENT="production"In platforms with built-in credential storage, disable “Test Mode” or “Sandbox” toggle before entering production credentials.
Credential security
Production API credentials must never appear in code repositories, logs, or error messages. Use secrets management systems and audit credential access. Rotate credentials immediately if exposure is suspected.
- Configure production webhook endpoint. Create a separate webhook registration for production, or update your existing registration to use production URLs:
curl -X POST "https://api.psp-example.com/api/v2/webhooks" \ -H "Authorization: Bearer $PROD_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "url": "https://cva.example.org/api/webhooks/psp-status", "events": ["payment.completed", "payment.failed", "payment.reversed"], "secret": "prod_webhook_secret_secure789" }'Use a different webhook signing secret for production than for test environments.
Configure transaction limits and controls. Set limits in your CVA platform to match PSP and programme requirements:
Limit type Configuration Example value Single transaction maximum PSP configuration 1,000,000 TZS Daily volume limit CVA platform 50,000,000 TZS Batch size maximum PSP configuration 500 payments Retry attempts CVA platform 3 attempts Retry delay CVA platform 300 seconds Configure alerts when approaching limits (80% threshold recommended).
Configure error handling and retry logic. Define how your integration handles transient failures:
Retry Policy Configuration:
Retryable errors: - HTTP 429 (rate limited): Retry after Retry-After header value - HTTP 500-503 (server error): Retry with exponential backoff - Timeout: Retry with exponential backoff - Network error: Retry with exponential backoff
Non-retryable errors: - HTTP 400 (bad request): Log and flag for review - HTTP 401 (unauthorized): Alert, do not retry - HTTP 404 (not found): Log and flag for review - Business logic failures: Log, do not retry automatically
Exponential backoff schedule: - Attempt 1: Immediate - Attempt 2: 30 seconds - Attempt 3: 120 seconds - Attempt 4: 480 seconds (8 minutes) - Maximum attempts: 4Configure these settings in your CVA platform’s integration module or implement in custom integration code.
- Verify idempotency handling. PSP integrations must handle duplicate submissions safely. Test that resubmitting a payment with the same idempotency key returns the original result without creating a duplicate:
# First submission curl -X POST "https://api.psp-example.com/api/v2/payments" \ -H "Authorization: Bearer $PROD_ACCESS_TOKEN" \ -H "Idempotency-Key: unique-key-12345" \ -d '{"recipient": {...}, "transaction": {...}}' # Returns: {"id": "pay_new123", "status": "pending"}
# Duplicate submission with same idempotency key curl -X POST "https://api.psp-example.com/api/v2/payments" \ -H "Authorization: Bearer $PROD_ACCESS_TOKEN" \ -H "Idempotency-Key: unique-key-12345" \ -d '{"recipient": {...}, "transaction": {...}}' # Returns: {"id": "pay_new123", "status": "pending"} # Same payment, not duplicateImplement idempotency key generation in your CVA platform using a combination of transfer ID and submission timestamp.
Phase 4: Go-live
Complete pre-go-live checklist. Verify all items before processing live payments:
Category Item Verified Credentials Production API credentials configured ☐ Credentials Webhook signing secret configured ☐ Credentials Credentials stored in secrets manager ☐ Network Outbound connectivity to production API ☐ Network Webhook endpoint accessible from PSP ☐ Network SSL certificate valid and not expiring within 30 days ☐ Configuration Field mapping verified ☐ Configuration Transaction limits configured ☐ Configuration Retry policy configured ☐ Testing Single payment success tested ☐ Testing Batch payment tested ☐ Testing Failure handling tested ☐ Testing Webhook receipt verified ☐ Monitoring Transaction alerts configured ☐ Monitoring Error rate alerts configured ☐ Documentation Integration documented ☐ Documentation Runbook created for common issues ☐ Process a small live batch. Before full distribution, process a limited batch of 5 to 10 real payments to verify production behaviour:
- Select beneficiaries with confirmed contact details
- Use minimum viable transfer amount (per programme rules)
- Monitor each payment through completion
- Verify webhook receipt for each payment
- Confirm beneficiary receipt (phone call or field verification)
Document any discrepancies between test and production behaviour.
Monitor the initial distribution closely. For the first full distribution, assign staff to monitor:
- Payment submission success rate
- Webhook receipt rate and latency
- Error rates by error type
- Batch completion time
- Beneficiary complaints or queries
Maintain direct communication with PSP support during the first distribution.
Transaction monitoring
After go-live, implement ongoing transaction monitoring to detect issues before they affect beneficiaries.
Status reconciliation
Run daily reconciliation between your CVA platform and PSP records. Query payments submitted in the last 24 hours and compare status:
-- CVA platform: payments awaiting PSP confirmationSELECT transfer_id, psp_payment_id, amount, submitted_atFROM transfersWHERE status = 'pending' AND submitted_at < NOW() - INTERVAL '4 hours';
-- These payments should have received status updates-- Investigate any pending longer than 4 hoursFor payments without status updates, query the PSP API directly:
curl -X GET "https://api.psp-example.com/api/v2/payments/pay_abc123" \ -H "Authorization: Bearer $ACCESS_TOKEN"If the PSP shows a different status than your platform, investigate webhook delivery failures.
Error rate monitoring
Track error rates to detect systemic issues:
Alert thresholds:- Error rate > 5% of batch: Investigate immediately- Error rate > 2% of batch: Review within 4 hours- Any authentication error: Alert immediately- Any rate limit error: Reduce submission rateConfigure alerts in your monitoring system:
# Example Prometheus alert rulegroups: - name: psp_integration rules: - alert: HighPaymentErrorRate expr: | rate(psp_payments_failed_total[5m]) / rate(psp_payments_submitted_total[5m]) > 0.05 for: 5m labels: severity: critical annotations: summary: "PSP payment error rate exceeds 5%"Reconciliation workflow
+-------------------------------------------------------------------+| DAILY RECONCILIATION |+-------------------------------------------------------------------+| || +------------------+ || | CVA Platform | || | Export +----+ || | (submitted, | | || | completed, | | || | failed) | | || +------------------+ | || | +------------------+ || +--->| Reconciliation | || | | Engine | || +------------------+ | | | || | PSP Report +----+ | - Match by ID | || | Download | | - Compare status | || | (all transactions| | - Flag mismatches| || | for period) | +--------+---------+ || +------------------+ | || | || +-------------+-------------+ || | | | || v v v || +------+-----+ +-----+------+ +----+-------+ || | Matched | | CVA Only | | PSP Only | || | (verify | | (webhook | | (missing | || | amounts) | | failure) | | in CVA) | || +------------+ +------------+ +------------+ || |+-------------------------------------------------------------------+Mismatches require investigation:
- CVA only (no PSP record): Submission failed silently; verify with PSP support
- PSP only (no CVA record): Duplicate submission or data corruption; investigate CVA logs
- Status mismatch: Webhook delivery failure; manually update CVA platform
- Amount mismatch: Serious error; halt processing and investigate
Error handling
Transient errors
Transient errors resolve with retry. Implement automatic retry with backoff:
+-------------------------------------------------------------------+| ERROR HANDLING FLOW |+-------------------------------------------------------------------+| || +------------------+ || | Payment | || | Submission | || +--------+---------+ || | || v || +--------+---------+ || | HTTP Response? | || +--------+---------+ || | || +-------------------+-------------------+ || | | | || v v v || +------+------+ +------+------+ +------+------+ || | 2xx | | 4xx | | 5xx/Timeout | || | Success | | Client Error| | Server Error| || +------+------+ +------+------+ +------+------+ || | | | || v v v || +------+------+ +------+------+ +------+------+ || | Update | | 400: Log | | Retry with | || | status to | | and | | exponential | || | pending | | review | | backoff | || +-------------+ | 401: Alert | +------+------+ || | immed. | | || | 429: Wait | v || | retry | +------+------+ || +-------------+ | Max retries | || | exceeded? | || +------+------+ || | || +----------------+--------+ || | | || v v || +------+------+ +-------+-----+ || | No: Retry | | Yes: Flag | || | after delay | | for manual | || +-------------+ | review | || +-------------+ || |+-------------------------------------------------------------------+Permanent errors
Permanent errors require manual intervention. Common permanent errors:
| Error code | PSP message | Cause | Resolution |
|---|---|---|---|
invalid_account | Account does not exist | Beneficiary phone/account number incorrect | Verify with beneficiary, update record |
account_blocked | Account is blocked | Beneficiary account suspended by provider | Contact beneficiary for alternative |
amount_below_minimum | Amount below minimum | Transfer amount less than PSP minimum | Adjust amount or combine with other transfer |
amount_above_maximum | Amount exceeds limit | Transfer amount exceeds PSP or regulatory limit | Split into multiple transfers |
duplicate_reference | Reference already used | Reference number reused | Generate new reference |
invalid_currency | Currency not supported | Currency not enabled for your merchant account | Contact PSP to enable currency |
Verification
After completing integration, verify the full flow works correctly:
Single payment verification
# Submit paymentPAYMENT_ID=$(curl -s -X POST "https://api.psp-example.com/api/v2/payments" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: verify-$(date +%s)" \ -d '{ "recipient": {"external_id": "VERIFY-001", "mobile_number": "+255700000001"}, "transaction": {"amount": 50000, "currency": "TZS", "reference": "VERIFY-001"} }' | jq -r '.id')
echo "Payment ID: $PAYMENT_ID"
# Wait for processingsleep 30
# Check statuscurl -s "https://api.psp-example.com/api/v2/payments/$PAYMENT_ID" \ -H "Authorization: Bearer $ACCESS_TOKEN" | jq '.status'
# Expected: "completed"Webhook verification
Check webhook logs for received callbacks:
# In CVA platform logs or webhook logging servicegrep "payment.completed" /var/log/cva/webhooks.log | tail -5
# Expected: Recent entries with payment IDs matching submitted paymentsCVA platform status verification
Confirm the CVA platform reflects current PSP status:
SELECT t.transfer_id, t.status, t.psp_payment_id, t.psp_reference, t.submitted_at, t.completed_atFROM transfers tWHERE t.submitted_at > NOW() - INTERVAL '1 hour'ORDER BY t.submitted_at DESCLIMIT 10;
-- All completed payments should show:-- status = 'completed'-- psp_reference populated-- completed_at populatedEnd-to-end timing verification
Measure processing time to establish baseline:
Timing benchmarks (typical):- Submission to PSP acknowledgment: < 2 seconds- PSP acknowledgment to completion webhook: 5-60 seconds (mobile money)- PSP acknowledgment to completion webhook: 1-24 hours (bank transfer)- Total end-to-end (mobile money): < 2 minutesIf timing exceeds these benchmarks consistently, investigate network latency or PSP performance.
Troubleshooting
| Symptom | Cause | Resolution |
|---|---|---|
401 Unauthorized on all requests | Invalid or expired API credentials | Verify credentials match PSP portal; regenerate if necessary |
401 Unauthorized after period of success | OAuth token expired and not refreshed | Implement token refresh before expiry; check refresh logic |
403 Forbidden | IP not allowlisted, or account suspended | Check IP allowlist with PSP; contact PSP support |
429 Too Many Requests | Rate limit exceeded | Reduce submission rate; implement backoff; check Retry-After header |
400 Bad Request with field validation error | Field mapping incorrect or data invalid | Check error response for specific field; verify mapping |
| Webhook not received | Firewall blocking PSP, webhook URL incorrect, or SSL issue | Test webhook URL accessibility; check SSL certificate; verify URL in PSP config |
| Webhook received but signature invalid | Wrong signing secret configured | Verify signing secret matches PSP webhook configuration |
| Payment stuck in pending | PSP processing delay or missed webhook | Query PSP API directly for status; check webhook logs |
| Duplicate payments created | Idempotency key not sent or not unique | Verify idempotency key header; ensure key generation creates unique values |
| Status mismatch between CVA and PSP | Webhook processing failed | Compare records; manually update CVA platform; investigate webhook handler errors |
| Connection timeout on submission | Network issue or PSP availability | Check network connectivity; verify PSP status page; implement retry |
| SSL certificate error | Expired certificate or hostname mismatch | Renew certificate; verify hostname matches certificate |
| Amount rejected as too low | Below PSP minimum transaction amount | Check PSP minimum limits; adjust transfer amount |
| Amount rejected as too high | Exceeds PSP or regulatory limit | Check limits; split into multiple payments |
| Currency rejected | Currency not enabled for merchant account | Contact PSP to enable currency; verify currency code format |
| Beneficiary account not found | Phone number format incorrect | Verify phone number format matches PSP requirements (with/without country code) |
Diagnostic commands
Check PSP API availability:
curl -w "\nHTTP Status: %{http_code}\nTime: %{time_total}s\n" \ -o /dev/null -s \ "https://api.psp-example.com/api/v2/health"Test authentication:
curl -s -X GET "https://api.psp-example.com/api/v2/account" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ | jq '{merchant_id, status, balance}'Check webhook endpoint accessibility (from external network):
curl -w "\nHTTP Status: %{http_code}\n" \ -X POST "https://cva.example.org/api/webhooks/psp-status" \ -H "Content-Type: application/json" \ -d '{"test": true}'# Should return 200 or 401 (if signature required), not connection errorReview recent webhook deliveries in PSP portal or via API:
curl -s "https://api.psp-example.com/api/v2/webhooks/whk_abc123/deliveries?limit=10" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ | jq '.[] | {id, status, response_code, delivered_at}'Rollback
If production issues require reverting to previous state:
- Disable the PSP integration in CVA platform configuration (toggle off or remove credentials)
- Document all payments submitted since go-live with their current status
- Coordinate with PSP support to halt processing of pending payments if possible
- Restore previous integration configuration if reverting to different PSP
- Communicate with programme teams about payment status and timeline
Do not delete payment records; mark them for reconciliation review.