Provider Setup
Before processing payments, each merchant must configure their provider credentials. Credentials are encrypted at rest using AES-256 (Fernet) and cached in Redis for 15 minutes.
Setting Credentials
Stripe
| Field | Required | Description |
|---|---|---|
secret_key |
✅ | Stripe secret key (sk_live_ or sk_test_) |
webhook_secret |
❌ | Stripe webhook signing secret |
M-Pesa
{
"provider": "mpesa",
"mpesa": {
"env": "sandbox",
"consumer_key": "your_consumer_key",
"consumer_secret": "your_consumer_secret",
"passkey": "your_passkey",
"shortcode": "174379",
"initiator": "apiop37",
"security_credential": "encrypted_credential...",
"result_url": "https://yourdomain.com/v1/webhooks/mpesa/transactionstatus/result",
"timeout_url": "https://yourdomain.com/v1/webhooks/mpesa/transactionstatus/timeout"
}
}
| Field | Required | Description |
|---|---|---|
consumer_key |
✅ | From Daraja My Apps |
consumer_secret |
✅ | From Daraja My Apps |
passkey |
✅ | Lipa Na M-Pesa passkey |
shortcode |
✅ | Paybill or Till number |
env |
❌ | sandbox (default) or live |
initiator |
❌ | API user for reversals/status queries |
security_credential |
❌ | Encrypted password for initiator |
result_url |
❌ | Callback URL for async results |
timeout_url |
❌ | Callback URL for timeouts |
PayPal
{
"provider": "paypal",
"paypal": {
"env": "sandbox",
"client_id": "your_client_id",
"client_secret": "your_client_secret"
}
}
| Field | Required | Description |
|---|---|---|
client_id |
✅ | PayPal app client ID |
client_secret |
✅ | PayPal app secret |
env |
❌ | sandbox (default) or live |
Response
Sensitive fields are masked — only the last 4 characters are shown:
{
"success": true,
"data": {
"id": "uuid-...",
"provider": "stripe",
"is_active": true,
"config_summary": {
"secret_key": "****4x7K",
"webhook_secret": "****test"
},
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-01T00:00:00Z"
}
}
Listing & Deleting
# List all configured providers
GET /v1/merchant/providers
# Remove a provider
DELETE /v1/merchant/providers/stripe
What happens if not configured?
Any payment request to an unconfigured provider returns 422:
{
"success": false,
"errors": [{
"code": "VALIDATION_ERROR",
"message": "No stripe credentials configured. Set them via PUT /v1/merchant/providers with provider='stripe'."
}]
}
Security
flowchart LR
Merchant["Merchant<br/>PUT /v1/merchant/providers"] -->|HTTPS| API["API Server"]
API -->|"Fernet encrypt"| DB[(PostgreSQL<br/>encrypted blob)]
API -->|"Fernet encrypt"| Redis[(Redis cache<br/>encrypted, 15min TTL)]
Payment["Payment Request"] --> Cache{"Redis<br/>cache hit?"}
Cache -->|Hit| Decrypt["Fernet decrypt"]
Cache -->|Miss| DBRead["DB read → decrypt → warm cache"]
Decrypt --> Provider["Provider API call"]
DBRead --> Provider