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:

1. Create an Account

Sign up for a free account and get your API credentials

Register Now

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"
}

4. Handle Webhooks

Set up your webhook URL to receive payment notifications

Webhook Guide

Authentication

All API requests require authentication using your API key. Include it in the Authorization header as a Bearer token.

Header Format
Authorization: Bearer YOUR_API_KEY_HERE
Example Request
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.

POST /stk-push
Sends an STK push to the customer's mobile money number

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

JSON 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

201 Created
{
  "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

400 Bad Request
{
  "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.

GET /transaction/{reference}
Example Request
curl -X GET https://api.zurichpay.co.tz/v1/transaction/TXN20240319123456 \
  -H "Authorization: Bearer zp_live_abc123def456"
Response
{
  "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

POST /your-webhook-url
{
  "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)

Signature Verification
$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.

GET /balance
Response
{
  "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.

POST /transfer
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
Example Request
{
  "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 - STK Push
<?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.

JavaScript (Fetch API)
// 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.

Python
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 - 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 - Check Status
curl -X GET https://api.zurichpay.co.tz/v1/transaction/TXN20240319123456 \
  -H "Authorization: Bearer zp_live_abc123def456"

Get Balance

cURL - 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.

React Payment Component
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

setPhone(e.target.value)} placeholder="255712345678" required style={{ width: '100%', padding: '10px', border: '2px solid #e0e0e0', borderRadius: '5px' }} />
{status === 'success' && (
Payment initiated! Check your phone.
)} {status === 'error' && (
Payment failed. Please try again.
)}
); }; 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.

Java
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 Collection

Frequently 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

Zurich Pay AI Assistant

👋 Hello! I'm your Zurich Pay AI assistant. I can help you with:
  • API integration questions
  • Code examples in any language
  • Error troubleshooting
  • Payment flow explanations
  • Webhook setup

What would you like help with today?

Just now

Suggested Questions

PHP STK Push Webhook Verification Error Codes Sandbox Testing Rate Limits Failed Payments