Skip to content

PHP SDK

The official Cashfin SDK for PHP applications.

Installation

bash
composer require cashfin/cashfin-php

Requirements

  • PHP 8.0 or later
  • Composer
  • ext-json and ext-curl extensions (standard in most environments)

Quick Start

php
<?php

require 'vendor/autoload.php';

use Cashfin\CashfinClient;

$cashfin = new CashfinClient([
    'api_key' => 'cs_your_client_secret',
]);

// List products
$result = $cashfin->products->all(['limit' => 10]);
foreach ($result['data'] as $product) {
    echo $product['title'] . ' — KES ' . $product['price'] . PHP_EOL;
}

// Initiate M-Pesa payment
$payment = $cashfin->payments->mpesa([
    'amount'      => 1500,
    'phone'       => '254712345678',
    'referenceid' => 'ORDER-001',
]);
echo 'Checkout ID: ' . $payment['data']['checkoutrequestid'];

Configuration Options

Pass a configuration array to CashfinClient:

KeyTypeDefaultDescription
api_keystringrequiredYour Cashfin client secret (cs_…)
timeoutint30Request timeout in seconds
max_retriesint3Retry attempts on transient failures
debugboolfalseLog all requests/responses via error_log()
base_urlstringhttps://api.cashfin.africa/businessOverride the base URL
php
$cashfin = new CashfinClient([
    'api_key'     => getenv('CASHFIN_API_KEY'),
    'timeout'     => 60,
    'max_retries' => 5,
    'debug'       => true,
]);

Response Format

All methods return plain PHP arrays matching the API response shape. Paginated endpoints return:

php
[
    'success' => true,
    'data'    => [ /* array of items */ ],
    'meta'    => [
        'page'    => 1,
        'limit'   => 10,
        'total'   => 42,
        'pages'   => 5,
        'hasNext' => true,
        'hasPrev' => false,
    ],
]

Products

List Products

php
$result = $cashfin->products->all([
    'page'       => 1,
    'limit'      => 20,
    'status'     => 'published', // draft | published | archived
    'type'       => 'product',   // product | service | plan
    'categoryid' => 'abc123',
    'featured'   => true,
]);

foreach ($result['data'] as $product) {
    echo $product['title'] . "\n";
}

Get Product

php
$product = $cashfin->products->retrieve('507f1f77bcf86cd799439011');

echo $product['data']['title'];
echo $product['data']['price'];

Create Product

php
$product = $cashfin->products->create([
    'title'       => 'Premium Widget',
    'description' => 'A high-quality widget',
    'price'       => 1999.99,
    'stock'       => 100,
    'sku'         => 'WDGT-001',
    'type'        => 'product',
    'status'      => 'published',
    'featured'    => true,
    'variants'    => [
        [
            'attributetitle' => 'Color',
            'valuetitle'     => 'Blue',
            'valueprice'     => 1999.99,
            'valuestock'     => 50,
        ],
    ],
]);

echo 'Created: ' . $product['data']['id'];

Update Product

php
$product = $cashfin->products->update('507f1f77bcf86cd799439011', [
    'price' => 2499.99,
    'stock' => 75,
]);

Categories

List Categories

php
$categories = $cashfin->categories->all(['status' => 'active']);

Get Category

php
$category = $cashfin->categories->retrieve('507f191e810c19729de860ea');

Create Category

php
$category = $cashfin->categories->create([
    'title'       => 'Electronics',
    'description' => 'Electronic devices and accessories',
    'status'      => 'active',
]);

Update Category

php
$category = $cashfin->categories->update('507f191e810c19729de860ea', [
    'description' => 'Updated description',
]);

Orders

List Orders

php
$orders = $cashfin->orders->all(['status' => 'pending']);

Get Order

php
$order = $cashfin->orders->retrieve('507f1f77bcf86cd799439011');

Create Checkout

php
$order = $cashfin->orders->checkout([
    'customeremail' => '[email protected]',
    'items'         => [
        [
            'itemid'   => '507f1f77bcf86cd799439011',
            'quantity' => 2,
            'rate'     => 1999.99,
        ],
    ],
    'shippingaddress' => [
        'name'    => 'John Doe',
        'address' => '123 Main St',
        'city'    => 'Nairobi',
        'country' => 'KE',
        'phone'   => '254712345678',
    ],
]);

echo 'Order: '   . $order['data']['orderno'];
echo 'Payment: ' . $order['data']['paymentlink']['shorturl'];

Payments

M-Pesa STK Push

php
$payment = $cashfin->payments->mpesa([
    'amount'      => 1500,            // KES
    'phone'       => '254712345678',  // 254XXXXXXXXX format
    'referenceid' => 'ORDER-001',
    'description' => 'Payment for Order #001',
]);

echo 'Checkout ID: ' . $payment['data']['checkoutrequestid'];
// Track the payment result via webhook

Customers

List Customers

php
$customers = $cashfin->customers->all(['type' => 'individual']);

Get Customer

php
$customer = $cashfin->customers->retrieve('507f1f77bcf86cd799439011');

Create Customer

php
$customer = $cashfin->customers->create([
    'name'     => 'John Doe',
    'email'    => '[email protected]',
    'phone'    => '+254712345678',
    'country'  => 'KE',
    'currency' => 'KES',
    'type'     => 'individual',
]);

echo 'Customer ID: ' . $customer['data']['id'];

Update Customer

php
$cashfin->customers->update('507f1f77bcf86cd799439011', [
    'phone' => '254711111111',
]);

Invoices

php
$cashfin->invoices->all(['status' => 'sent', 'customerid' => $customerId]);
$cashfin->invoices->retrieve($id);
$cashfin->invoices->create([
    'customerid' => $customerId,
    'items'      => [['name' => 'Consulting', 'quantity' => 5, 'rate' => 1000]],
    'duedate'    => '2025-12-31',
]);
$cashfin->invoices->update($id, ['status' => 'paid']);

Subscriptions

Create Subscription

php
$subscription = $cashfin->subscriptions->create([
    'customerid'   => '507f1f77bcf86cd799439011',
    'items'        => [
        [
            'name'     => 'Pro Plan',
            'rate'     => 2999.00,
            'quantity' => 1,
        ],
    ],
    'billingcycle' => 'monthly',  // daily | weekly | monthly | quarterly | yearly
    'autorenew'    => true,
]);

echo 'Subscription: ' . $subscription['data']['subscriptionno'];

Get Subscription

php
$subscription = $cashfin->subscriptions->retrieve('507f1f77bcf86cd799439020');

echo 'Status: '       . $subscription['data']['status'];
echo 'Next Billing: ' . $subscription['data']['nextbillingdate'];

List Subscriptions

php
$cashfin->subscriptions->all(['status' => 'active']);
php
$cashfin->paymentLinks->all(['status' => 'active']);
$cashfin->paymentLinks->retrieve($id);
$cashfin->paymentLinks->create([
    'title'       => 'Pay for Event Ticket',
    'amount'      => 500,
    'description' => 'Annual conference ticket',
    'currency'    => 'KES',
]);

Transactions

php
$cashfin->transactions->all(['status' => 'completed', 'method' => 'mpesa']);
$cashfin->transactions->retrieve($id);

Receipts

php
$cashfin->receipts->all(['customerid' => $customerId]);
$cashfin->receipts->retrieve($id);

Vendors

php
$cashfin->vendors->all();
$cashfin->vendors->retrieve($id);
$cashfin->vendors->create(['name' => 'Acme Supplies', 'email' => '[email protected]']);
$cashfin->vendors->update($id, ['phone' => '254700000001']);

Expenses

php
$cashfin->expenses->all(['status' => 'pending', 'vendorid' => $vendorId]);
$cashfin->expenses->retrieve($id);
$cashfin->expenses->create([
    'title'    => 'Office Supplies',
    'amount'   => 3500,
    'category' => 'supplies',
    'date'     => '2025-06-01',
    'vendorid' => $vendorId,
]);

Bills

php
$cashfin->bills->all(['status' => 'pending', 'vendorid' => $vendorId]);
$cashfin->bills->retrieve($id);
$cashfin->bills->create([
    'vendorid' => $vendorId,
    'items'    => [['name' => 'Monthly Rent', 'quantity' => 1, 'rate' => 50000]],
    'duedate'  => '2025-07-01',
]);

Purchase Orders

php
$cashfin->purchaseOrders->all(['status' => 'draft', 'vendorid' => $vendorId]);
$cashfin->purchaseOrders->retrieve($id);
$cashfin->purchaseOrders->create([
    'vendorid' => $vendorId,
    'items'    => [['name' => 'Printer Paper', 'quantity' => 10, 'rate' => 500]],
]);

Leads

php
$cashfin->leads->all(['status' => 'new']);
$cashfin->leads->retrieve($id);
$cashfin->leads->create([
    'name'   => 'Prospect Ltd',
    'email'  => '[email protected]',
    'phone'  => '254700000002',
    'source' => 'website',
    'budget' => 100000,
]);
$cashfin->leads->update($id, ['status' => 'qualified']);

Quotes

php
$cashfin->quotes->all(['status' => 'sent', 'customerid' => $customerId]);
$cashfin->quotes->retrieve($id);

Contacts

php
$cashfin->contacts->all();
$cashfin->contacts->retrieve($id);
$cashfin->contacts->create(['name' => 'Alice Smith', 'email' => '[email protected]']);
$cashfin->contacts->update($id, ['position' => 'CEO']);

Contracts

php
$cashfin->contracts->all(['status' => 'active']);
$cashfin->contracts->retrieve($id);

Bookings

php
$cashfin->bookings->all(['status' => 'scheduled']);
$cashfin->bookings->retrieve($id);

Appointments

php
$cashfin->appointments->all(['status' => 'active']);
$cashfin->appointments->retrieve($id);

Campaigns

php
$cashfin->campaigns->all(['status' => 'sent', 'type' => 'email']);
$cashfin->campaigns->retrieve($id);

Marketing Lists

php
$cashfin->lists->all(['type' => 'email']);
$cashfin->lists->retrieve($id);
$cashfin->lists->create(['name' => 'Newsletter Subscribers', 'type' => 'email']);

// Add a contact to a list
$cashfin->lists->addContact($listId, [
    'email'     => '[email protected]',
    'firstname' => 'Bob',
    'lastname'  => 'Kariuki',
]);

Error Handling

The SDK throws typed exceptions for every API error. Catch the specific exception you expect, or the base CashfinException as a fallback.

php
<?php

use Cashfin\Exceptions\AuthenticationException;
use Cashfin\Exceptions\ValidationException;
use Cashfin\Exceptions\NotFoundException;
use Cashfin\Exceptions\RateLimitException;
use Cashfin\Exceptions\ConflictException;
use Cashfin\Exceptions\ForbiddenException;
use Cashfin\Exceptions\ServerException;
use Cashfin\Exceptions\NetworkException;
use Cashfin\Exceptions\CashfinException;

try {
    $product = $cashfin->products->create([
        'title' => 'Widget',
        // Missing required fields
    ]);
} catch (ValidationException $e) {
    // 422 — field-level errors
    foreach ($e->getErrors() as $field => $message) {
        echo "{$field}: {$message}\n";
    }
} catch (AuthenticationException $e) {
    // 401 — invalid API key
    echo 'Check your CASHFIN_API_KEY.';
} catch (NotFoundException $e) {
    // 404 — resource not found
    echo 'Not found: ' . $e->getMessage();
} catch (RateLimitException $e) {
    // 429 — back off and retry
    sleep($e->getRetryAfter());
} catch (ConflictException $e) {
    // 409 — duplicate resource
} catch (ForbiddenException $e) {
    // 403 — insufficient permissions
} catch (ServerException $e) {
    // 500/502/503/504
} catch (NetworkException $e) {
    // Connection failed
} catch (CashfinException $e) {
    // Catch-all for any other Cashfin error
    echo $e->getMessage() . ' (HTTP ' . $e->getStatusCode() . ')';
}

All exceptions expose:

  • getMessage() — human-readable error message
  • getStatusCode() — HTTP status code
  • getRequestId() — server-side request ID for support tracing
  • getErrorData() — raw response body as array

Webhooks

Verify Signature

php
<?php

require 'vendor/autoload.php';

use Cashfin\Webhook;
use Cashfin\Exceptions\CashfinException;

$payload   = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_CASHFIN_SIGNATURE'] ?? '';
$secret    = getenv('CASHFIN_WEBHOOK_SECRET');

try {
    $event = Webhook::constructEvent($payload, $signature, $secret);
} catch (CashfinException $e) {
    http_response_code(400);
    exit($e->getMessage());
}

switch ($event['type']) {
    case 'payment.completed':
        $data = $event['data'];
        handlePaymentCompleted($data);
        break;
    case 'order.created':
        handleOrderCreated($event['data']);
        break;
    case 'invoice.paid':
        handleInvoicePaid($event['data']);
        break;
    case 'subscription.renewed':
        handleSubscriptionRenewed($event['data']);
        break;
    default:
        echo 'Unhandled event type: ' . $event['type'];
}

http_response_code(200);
echo json_encode(['received' => true]);

Pagination

All list methods accept page and limit parameters. Iterate pages using the hasNext flag from the response meta:

php
$page = 1;
do {
    $result = $cashfin->products->all(['page' => $page, 'limit' => 50]);

    foreach ($result['data'] as $product) {
        echo $product['title'] . "\n";
    }

    $page++;
} while ($result['meta']['hasNext']);

Debugging

Set debug => true in the config to log all requests and raw responses to PHP's error log:

php
$cashfin = new CashfinClient([
    'api_key' => getenv('CASHFIN_API_KEY'),
    'debug'   => true,
]);

Log output format:

[Cashfin] [POST] /payments/mobile/request
[Cashfin] Response 200 {"success":true,...}

For Laravel, set CASHFIN_DEBUG=true in your .env file.

Laravel Integration

Setup

The SDK auto-discovers the service provider. After installing, publish the config:

bash
php artisan vendor:publish --tag=cashfin-config

Add your credentials to .env:

env
CASHFIN_API_KEY=cs_your_client_secret
CASHFIN_WEBHOOK_SECRET=your_webhook_secret

Config File

php
// config/services.php
return [
    // ...
    'cashfin' => [
        'key'    => env('CASHFIN_API_KEY'),
        'secret' => env('CASHFIN_WEBHOOK_SECRET'),
    ],
];

Dependency Injection

php
<?php
// app/Http/Controllers/PaymentController.php

namespace App\Http\Controllers;

use Cashfin\CashfinClient;
use Illuminate\Http\Request;

class PaymentController extends Controller
{
    public function __construct(private CashfinClient $cashfin) {}

    public function initiatePayment(Request $request)
    {
        $validated = $request->validate([
            'amount'   => 'required|numeric|min:10',
            'phone'    => 'required|string',
            'order_id' => 'required|string',
        ]);

        $payment = $this->cashfin->payments->mpesa([
            'amount'      => $validated['amount'],
            'phone'       => $validated['phone'],
            'referenceid' => $validated['order_id'],
        ]);

        return response()->json([
            'success'     => true,
            'checkout_id' => $payment['data']['checkoutrequestid'],
        ]);
    }
}

Facade

php
use Cashfin\Laravel\Facades\Cashfin;

$products = Cashfin::products()->all();

Service Class

php
<?php
// app/Services/CashfinService.php

namespace App\Services;

use Cashfin\CashfinClient;

class CashfinService
{
    private CashfinClient $client;

    public function __construct()
    {
        $this->client = new CashfinClient([
            'api_key' => config('services.cashfin.key'),
        ]);
    }

    public function createProduct(array $data): array
    {
        return $this->client->products->create($data);
    }

    public function initiatePayment(float $amount, string $phone, string $reference): array
    {
        return $this->client->payments->mpesa([
            'amount'      => $amount,
            'phone'       => $phone,
            'referenceid' => $reference,
        ]);
    }
}

Webhook Controller

php
<?php
// app/Http/Controllers/WebhookController.php

namespace App\Http\Controllers;

use Cashfin\Webhook;
use Illuminate\Http\Request;

class WebhookController extends Controller
{
    public function handle(Request $request)
    {
        $event = Webhook::constructEvent(
            $request->getContent(),
            $request->header('X-Cashfin-Signature', ''),
            config('services.cashfin.secret')
        );

        match ($event['type']) {
            'payment.completed' => $this->handlePaymentCompleted($event['data']),
            'order.created'     => $this->handleOrderCreated($event['data']),
            'invoice.paid'      => $this->handleInvoicePaid($event['data']),
            default             => null,
        };

        return response()->json(['received' => true]);
    }

    private function handlePaymentCompleted(array $payment): void
    {
        // Update order status, send confirmation email, etc.
    }

    private function handleOrderCreated(array $order): void
    {
        // Process new order
    }

    private function handleInvoicePaid(array $invoice): void
    {
        // Mark invoice as settled in your system
    }
}

Register the webhook route excluding CSRF verification:

php
// routes/api.php
Route::post('/webhooks/cashfin', [WebhookController::class, 'handle'])
    ->withoutMiddleware([\App\Http\Middleware\VerifyCsrfToken::class]);

Symfony Integration

Register the client as a service in config/services.yaml:

yaml
services:
    Cashfin\CashfinClient:
        arguments:
            - api_key: '%env(CASHFIN_API_KEY)%'
              timeout: 30
              max_retries: 3

Inject it into your controller or service via constructor injection:

php
use Cashfin\CashfinClient;

class PaymentService
{
    public function __construct(private CashfinClient $cashfin) {}

    public function pay(float $amount, string $phone): array
    {
        return $this->cashfin->payments->mpesa([
            'amount' => $amount,
            'phone'  => $phone,
        ]);
    }
}

Examples

WooCommerce Integration

php
<?php
// Sync a WooCommerce order to Cashfin on checkout

use Cashfin\CashfinClient;

add_action('woocommerce_thankyou', 'sync_order_to_cashfin');

function sync_order_to_cashfin($order_id)
{
    $order = wc_get_order($order_id);

    $cashfin = new CashfinClient([
        'api_key' => get_option('cashfin_api_key'),
    ]);

    $items = [];
    foreach ($order->get_items() as $item) {
        $items[] = [
            'itemid'   => $item->get_product_id(),
            'quantity' => $item->get_quantity(),
            'rate'     => $item->get_total() / $item->get_quantity(),
        ];
    }

    $cashfinOrder = $cashfin->orders->checkout([
        'customeremail'   => $order->get_billing_email(),
        'items'           => $items,
        'shippingaddress' => [
            'name'    => $order->get_shipping_first_name() . ' ' . $order->get_shipping_last_name(),
            'address' => $order->get_shipping_address_1(),
            'city'    => $order->get_shipping_city(),
            'country' => $order->get_shipping_country(),
        ],
    ]);

    // Store Cashfin order ID as post meta
    update_post_meta($order_id, '_cashfin_order_id', $cashfinOrder['data']['id']);
}

Cashfin Business API Documentation