Introduction
Zurich Pay provides a simple, powerful, and secure API that allows businesses in Tanzania to accept mobile money payments instantly. Our RESTful API follows industry standards and returns JSON responses.
✨ Key Features
- Accept payments via M-Pesa, TigoPesa, Airtel Money, Halopesa
- Instant STK push notifications to customers
- Real-time transaction status updates
- Webhook notifications for payment events
- Wallet system with internal transfers
- Bank-grade security with 256-bit encryption
⚠️ Important Note
All API requests must be made over HTTPS. Calls made over plain HTTP will fail. You must always include your API key in the Authorization header.
Quick Start
Get up and running with Zurich Pay in minutes. Follow these simple steps:
2. Get Your API Keys
After registration, find your API keys in the dashboard
- API Key: Your public identifier
- API Secret: Keep this secure!
3. Make Your First Payment
Send an STK push to a customer's phone
POST /v1/stk-push
{
"phone": "255712345678",
"amount": 1000,
"reference": "INV-001"
}
Authentication
All API requests require authentication using your API key. Include it in the Authorization header as a Bearer token.
Authorization: Bearer YOUR_API_KEY_HERE
curl -X GET https://api.zurichpay.co.tz/v1/balance \
-H "Authorization: Bearer zp_live_abc123def456"
| Environment | API Key Prefix | Purpose |
|---|---|---|
sandbox |
zp_test_* |
Testing and development (no real money) |
production |
zp_live_* |
Live transactions (real money) |
🔐 Security Best Practices
- Never expose your API key in client-side code
- Store API keys in environment variables
- Use different keys for development and production
- Rotate keys periodically
- Enable IP whitelisting for additional security
Base URL
All API endpoints are relative to the base URL below. Use the appropriate URL based on your environment.
| Environment | Base URL |
|---|---|
| Sandbox | https://sandbox.api.zurichpay.co.tz/v1 |
| Production | https://api.zurichpay.co.tz/v1 |
All endpoints in this documentation use the production URL. Replace with sandbox URL for testing.
Rate Limits
To ensure fair usage and system stability, API requests are rate-limited based on your plan.
| Plan | Rate Limit | Burst Limit |
|---|---|---|
| Starter (Free) | 60 requests per minute | 10 requests per second |
| Business | 300 requests per minute | 50 requests per second |
| Enterprise | Custom limits | Custom |
Rate Limit Headers
Each response includes headers showing your current rate limit status:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 58
X-RateLimit-Reset: 45
STK Push Payment
Initiate an STK push to the customer's phone. They will receive a prompt to enter their PIN and complete the payment.
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
phone |
string | Yes | Customer phone number (format: 255XXXXXXXXX or 07XXXXXXXX) |
amount |
integer | Yes | Amount in TZS (minimum 200, maximum 5,000,000) |
reference |
string | Yes | Your unique transaction reference (max 50 chars) |
customer[firstname] |
string | No | Customer's first name |
customer[lastname] |
string | No | Customer's last name |
customer[email] |
string | No | Customer's email address |
metadata |
object | No | Additional data to attach to transaction (max 10 key-value pairs) |
webhook_url |
string | No | Override your default webhook URL for this transaction |
Example Request
{
"phone": "255712345678",
"amount": 5000,
"reference": "INV-2024-001",
"customer": {
"firstname": "John",
"lastname": "Doe",
"email": "john@example.com"
},
"metadata": {
"order_id": "ORD-12345",
"product": "Premium Plan"
},
"webhook_url": "https://your-site.com/webhook"
}
Success Response
{
"status": "success",
"code": 201,
"message": "STK push initiated successfully",
"data": {
"reference": "TXN20240319123456",
"transaction_uuid": "txn_65f9a1b2c3d4e5",
"provider_reference": "ws_CO_123456789",
"status": "pending",
"amount": {
"value": 5000,
"currency": "TZS"
},
"phone": "255712345678",
"created_at": "2024-03-19T12:00:00+03:00"
}
}
Error Response
{
"status": "error",
"code": 400,
"error_code": "validation_error",
"message": "Invalid phone number format",
"errors": [
"Phone number must be in format 255XXXXXXXXX or 07XXXXXXXX"
]
}
Check Transaction Status
Check the status of a transaction using its reference or transaction ID.
curl -X GET https://api.zurichpay.co.tz/v1/transaction/TXN20240319123456 \
-H "Authorization: Bearer zp_live_abc123def456"
{
"status": "success",
"code": 200,
"data": {
"reference": "TXN20240319123456",
"transaction_uuid": "txn_65f9a1b2c3d4e5",
"amount": 5000,
"phone": "255712345678",
"status": "completed",
"provider_reference": "ws_CO_123456789",
"created_at": "2024-03-19T12:00:00+03:00",
"completed_at": "2024-03-19T12:05:00+03:00"
}
}
| Status | Description |
|---|---|
| pending | Transaction initiated, waiting for customer confirmation |
| processing | Payment being processed by provider |
| completed | Payment successful |
| failed | Payment failed |
Webhooks
Zurich Pay can send real-time notifications to your webhook URL when transaction statuses change. Configure your webhook URL in your dashboard.
📡 Webhook Configuration
Set your webhook URL in the dashboard: Settings → Webhook URL
Your webhook endpoint should return a 200 status code to acknowledge receipt.
Webhook Headers
| Header | Description |
|---|---|
X-Webhook-Event |
Event type (payment.completed, payment.failed, etc.) |
X-Webhook-Timestamp |
Unix timestamp when webhook was sent |
X-Webhook-Signature |
HMAC-SHA256 signature for verification |
Payment Completed Webhook
{
"event": "payment.completed",
"data": {
"reference": "TXN20240319123456",
"transaction_uuid": "txn_65f9a1b2c3d4e5",
"amount": {
"value": 5000,
"currency": "TZS"
},
"phone": "255712345678",
"provider_reference": "ws_CO_123456789",
"metadata": {
"order_id": "ORD-12345"
}
},
"timestamp": "2024-03-19T12:05:01+03:00"
}
Verify Webhook Signature (PHP)
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'];
$secret = 'your_webhook_secret';
$expected = hash_hmac('sha256', $payload, $secret);
if (hash_equals($expected, $signature)) {
// Webhook is valid, process it
$data = json_decode($payload, true);
// Update your database
} else {
// Invalid signature, reject
http_response_code(401);
exit();
}
Get Wallet Balance
Retrieve your current wallet balance.
{
"status": "success",
"code": 200,
"data": {
"balance": 150000,
"currency": "TZS",
"last_updated": "2024-03-19T12:00:00+03:00"
}
}
Send Money to Another User
Transfer funds from your wallet to another Zurich Pay user instantly.
| Parameter | Type | Required | Description |
|---|---|---|---|
recipient_email |
string | Yes | Email of the recipient user |
amount |
integer | Yes | Amount to transfer (minimum 200) |
reference |
string | No | Your reference for this transfer |
{
"recipient_email": "business@example.com",
"amount": 10000,
"reference": "TRF-2024-001"
}
PHP Integration Example
Complete example of integrating Zurich Pay in PHP using cURL.
<?php
// Zurich Pay PHP Integration Example
class ZurichPayClient {
private $api_key;
private $base_url;
public function __construct($api_key, $environment = 'production') {
$this->api_key = $api_key;
$this->base_url = $environment === 'production'
? 'https://api.zurichpay.co.tz/v1'
: 'https://sandbox.api.zurichpay.co.tz/v1';
}
/**
* Send STK Push to customer
*/
public function stkPush($phone, $amount, $reference, $customer = [], $metadata = []) {
$url = $this->base_url . '/stk-push';
$data = [
'phone' => $phone,
'amount' => $amount,
'reference' => $reference,
'customer' => $customer,
'metadata' => $metadata
];
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $this->api_key,
'Content-Type: application/json'
]);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return json_decode($response, true);
}
/**
* Check transaction status
*/
public function checkStatus($reference) {
$url = $this->base_url . '/transaction/' . $reference;
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $this->api_key
]);
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
/**
* Get wallet balance
*/
public function getBalance() {
$url = $this->base_url . '/balance';
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $this->api_key
]);
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
}
// Usage Example
try {
$client = new ZurichPayClient('zp_live_abc123def456', 'production');
// Send STK Push
$result = $client->stkPush(
'255712345678',
5000,
'INV-' . time(),
[
'firstname' => 'John',
'lastname' => 'Doe',
'email' => 'john@example.com'
],
[
'order_id' => 'ORD-12345'
]
);
if ($result['status'] === 'success') {
echo "Payment initiated: " . $result['data']['reference'];
// Check status after a few seconds
sleep(5);
$status = $client->checkStatus($result['data']['reference']);
echo "Payment status: " . $status['data']['status'];
}
} catch (Exception $e) {
echo "Error: " . $e->getMessage();
}
?>
JavaScript Integration Example
Using Fetch API in browser or Node.js.
// Zurich Pay JavaScript Client
class ZurichPayClient {
constructor(apiKey, environment = 'production') {
this.apiKey = apiKey;
this.baseUrl = environment === 'production'
? 'https://api.zurichpay.co.tz/v1'
: 'https://sandbox.api.zurichpay.co.tz/v1';
}
async stkPush(phone, amount, reference, customer = {}, metadata = {}) {
const response = await fetch(`${this.baseUrl}/stk-push`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
phone,
amount,
reference,
customer,
metadata
})
});
return await response.json();
}
async checkStatus(reference) {
const response = await fetch(`${this.baseUrl}/transaction/${reference}`, {
headers: {
'Authorization': `Bearer ${this.apiKey}`
}
});
return await response.json();
}
async getBalance() {
const response = await fetch(`${this.baseUrl}/balance`, {
headers: {
'Authorization': `Bearer ${this.apiKey}`
}
});
return await response.json();
}
}
// Usage Example
const client = new ZurichPayClient('zp_live_abc123def456', 'production');
// Send STK Push
async function processPayment() {
try {
const result = await client.stkPush(
'255712345678',
5000,
'INV-' + Date.now(),
{
firstname: 'John',
lastname: 'Doe',
email: 'john@example.com'
},
{
order_id: 'ORD-12345'
}
);
if (result.status === 'success') {
console.log('Payment initiated:', result.data.reference);
// Check status after a few seconds
setTimeout(async () => {
const status = await client.checkStatus(result.data.reference);
console.log('Payment status:', status.data.status);
}, 5000);
}
} catch (error) {
console.error('Error:', error);
}
}
processPayment();
Python Integration Example
Using the requests library.
import requests
import json
import time
class ZurichPayClient:
def __init__(self, api_key, environment='production'):
self.api_key = api_key
self.base_url = 'https://api.zurichpay.co.tz/v1' if environment == 'production' \
else 'https://sandbox.api.zurichpay.co.tz/v1'
self.headers = {
'Authorization': f'Bearer {api_key}',
'Content-Type': 'application/json'
}
def stk_push(self, phone, amount, reference, customer=None, metadata=None):
"""Send STK Push to customer"""
url = f'{self.base_url}/stk-push'
data = {
'phone': phone,
'amount': amount,
'reference': reference,
'customer': customer or {},
'metadata': metadata or {}
}
response = requests.post(url, headers=self.headers, json=data)
return response.json()
def check_status(self, reference):
"""Check transaction status"""
url = f'{self.base_url}/transaction/{reference}'
response = requests.get(url, headers=self.headers)
return response.json()
def get_balance(self):
"""Get wallet balance"""
url = f'{self.base_url}/balance'
response = requests.get(url, headers=self.headers)
return response.json()
# Usage Example
if __name__ == '__main__':
client = ZurichPayClient('zp_live_abc123def456', 'production')
# Send STK Push
result = client.stk_push(
phone='255712345678',
amount=5000,
reference=f'INV-{int(time.time())}',
customer={
'firstname': 'John',
'lastname': 'Doe',
'email': 'john@example.com'
},
metadata={
'order_id': 'ORD-12345'
}
)
if result.get('status') == 'success':
print(f"Payment initiated: {result['data']['reference']}")
# Check status after a few seconds
time.sleep(5)
status = client.check_status(result['data']['reference'])
print(f"Payment status: {status['data']['status']}")
cURL Examples
Command-line examples for testing and integration.
STK Push
curl -X POST https://api.zurichpay.co.tz/v1/stk-push \
-H "Authorization: Bearer zp_live_abc123def456" \
-H "Content-Type: application/json" \
-d '{
"phone": "255712345678",
"amount": 5000,
"reference": "INV-001",
"customer": {
"firstname": "John",
"lastname": "Doe",
"email": "john@example.com"
},
"metadata": {
"order_id": "ORD-12345"
}
}'
Check Status
curl -X GET https://api.zurichpay.co.tz/v1/transaction/TXN20240319123456 \
-H "Authorization: Bearer zp_live_abc123def456"
Get Balance
curl -X GET https://api.zurichpay.co.tz/v1/balance \
-H "Authorization: Bearer zp_live_abc123def456"
React Component Example
A complete React component for payment processing.
import React, { useState } from 'react';
import axios from 'axios';
const ZurichPayPayment = ({ apiKey, amount, onSuccess, onError }) => {
const [phone, setPhone] = useState('');
const [loading, setLoading] = useState(false);
const [status, setStatus] = useState(null);
const handlePayment = async (e) => {
e.preventDefault();
setLoading(true);
setStatus(null);
try {
const response = await axios.post(
'https://api.zurichpay.co.tz/v1/stk-push',
{
phone,
amount,
reference: `INV-${Date.now()}`,
customer: {
firstname: 'Customer',
lastname: 'Name',
email: 'customer@example.com'
}
},
{
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
}
}
);
if (response.data.status === 'success') {
setStatus('success');
onSuccess?.(response.data);
// Poll for status
setTimeout(async () => {
const statusRes = await axios.get(
`https://api.zurichpay.co.tz/v1/transaction/${response.data.data.reference}`,
{ headers: { 'Authorization': `Bearer ${apiKey}` } }
);
console.log('Final status:', statusRes.data);
}, 5000);
}
} catch (error) {
setStatus('error');
onError?.(error.response?.data || error.message);
} finally {
setLoading(false);
}
};
return (
Zurich Pay Payment
);
};
export default ZurichPayPayment;
// Usage:
// <ZurichPayPayment
// apiKey="zp_live_abc123def456"
// amount={5000}
// onSuccess={(data) => console.log('Success:', data)}
// onError={(error) => console.error('Error:', error)}
// />
Java Integration Example
Using OkHttp for HTTP requests.
import okhttp3.*;
import com.google.gson.Gson;
import java.io.IOException;
public class ZurichPayClient {
private static final String PROD_URL = "https://api.zurichpay.co.tz/v1";
private static final String SANDBOX_URL = "https://sandbox.api.zurichpay.co.tz/v1";
private final OkHttpClient client;
private final String apiKey;
private final String baseUrl;
private final Gson gson;
public ZurichPayClient(String apiKey, boolean production) {
this.client = new OkHttpClient();
this.apiKey = apiKey;
this.baseUrl = production ? PROD_URL : SANDBOX_URL;
this.gson = new Gson();
}
// Request classes
public static class StkPushRequest {
public String phone;
public int amount;
public String reference;
public Customer customer;
public Object metadata;
public static class Customer {
public String firstname;
public String lastname;
public String email;
}
}
public static class StkPushResponse {
public String status;
public int code;
public Data data;
public static class Data {
public String reference;
public String transaction_uuid;
public String provider_reference;
public String status;
public Amount amount;
public static class Amount {
public int value;
public String currency;
}
}
}
public StkPushResponse stkPush(String phone, int amount, String reference,
StkPushRequest.Customer customer) throws IOException {
StkPushRequest request = new StkPushRequest();
request.phone = phone;
request.amount = amount;
request.reference = reference;
request.customer = customer;
String json = gson.toJson(request);
RequestBody body = RequestBody.create(
json, MediaType.parse("application/json")
);
Request httpRequest = new Request.Builder()
.url(baseUrl + "/stk-push")
.post(body)
.addHeader("Authorization", "Bearer " + apiKey)
.build();
try (Response response = client.newCall(httpRequest).execute()) {
String responseBody = response.body().string();
return gson.fromJson(responseBody, StkPushResponse.class);
}
}
// Usage Example
public static void main(String[] args) {
ZurichPayClient client = new ZurichPayClient("zp_live_abc123def456", true);
StkPushRequest.Customer customer = new StkPushRequest.Customer();
customer.firstname = "John";
customer.lastname = "Doe";
customer.email = "john@example.com";
try {
StkPushResponse response = client.stkPush(
"255712345678",
5000,
"INV-" + System.currentTimeMillis(),
customer
);
if ("success".equals(response.status)) {
System.out.println("Payment initiated: " + response.data.reference);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
SDKs & Libraries
Official client libraries for popular programming languages.
PHP SDK
Install via Composer
composer require zurichpay/php-sdk
JavaScript SDK
Install via NPM
npm install zurichpay-js
Python SDK
Install via pip
pip install zurichpay-python
Java SDK
Add to Maven/Gradle
implementation 'co.tz.zurichpay:client:1.0.0'
Postman Collection
Test our API easily with our Postman collection. Import it into your Postman app.
📬 Postman Collection
Click the button below to get the Postman collection with all endpoints pre-configured.
Download Postman CollectionFrequently Asked Questions
How long does an STK push take?
STK pushes typically arrive within 5-10 seconds. The customer has 2 minutes to enter their PIN before the request expires.
What happens if the customer doesn't have enough balance?
The transaction will fail and you'll receive a webhook with status 'failed'. No money is deducted from your account.
How do I test without real money?
Use sandbox environment with test API keys. Test phone numbers and amounts are provided in the sandbox.
Are there any setup fees?
No! Registration is free. You only pay transaction fees when you process payments.
How do I get support?
Contact us via phone: 0793 473 044 or 0787 108 899. Email: support@zurichpay.co.tz
Support & Contact
Phone Support
Available 24/7
0793 473 044
0787 108 899
Email Support
Response within 1 hour
support@zurichpay.co.tz
tech@zurichpay.co.tz
Live Chat
Available on dashboard
Click the chat icon in your dashboard
Office
Visit us
Dar es Salaam, Tanzania