cashfin/cashfin-php

Official PHP SDK for the Cashfin Business API

Maintainers

Package info

gitlab.com/cashfin_africa1/cashfin_sdks

Homepage

Issues

pkg:composer/cashfin/cashfin-php

Transparency log

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

dev-develop 2026-08-14 14:38 UTC

This package is auto-updated.

Last update: 2026-08-14 11:51:58 UTC


README

Packagist Version PHP Version License

Official PHP SDK for the Cashfin Business API. Works with plain PHP, Laravel, Symfony, and any other PHP framework.

Requirements

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

Installation

composer require cashfin/cashfin-php

Quick Start

<?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

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
$cashfin = new CashfinClient([
    'api_key'     => getenv('CASHFIN_API_KEY'),
    'timeout'     => 60,
    'max_retries' => 5,
    'debug'       => true,
]);

Framework Integration

Laravel

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

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

Add your key to .env:

CASHFIN_API_KEY=cs_your_client_secret

Dependency injection:

use Cashfin\CashfinClient;

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

    public function pay(Request $request)
    {
        $payment = $this->cashfin->payments->mpesa([
            'amount' => $request->amount,
            'phone'  => $request->phone,
        ]);
        return response()->json($payment);
    }
}

Facade:

use Cashfin\Laravel\Facades\Cashfin;

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

Symfony

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

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

Inject it into your controller or service normally via constructor injection.

Plain PHP

require 'vendor/autoload.php';

$cashfin = new \Cashfin\CashfinClient(['api_key' => 'cs_xxxx']);

API Reference

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

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

Products — $cashfin->products

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

// Get a product
$cashfin->products->retrieve('507f1f77bcf86cd799439011');

// Create a product
$cashfin->products->create([
    'title'       => 'Premium Widget',
    'price'       => 1999.99,
    'description' => 'A high-quality widget',
    'stock'       => 100,
    'sku'         => 'WDGT-001',
    'type'        => 'product',
    'status'      => 'published',
    'variants'    => [
        ['attributetitle' => 'Color', 'valuetitle' => 'Red', 'valueprice' => 2099.99],
    ],
]);

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

Categories — $cashfin->categories

$cashfin->categories->all(['status' => 'active']);
$cashfin->categories->retrieve($id);
$cashfin->categories->create(['title' => 'Electronics', 'status' => 'active']);
$cashfin->categories->update($id, ['title' => 'Updated Name']);

Orders — $cashfin->orders

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

// Create / checkout
$cashfin->orders->checkout([
    'customeremail' => 'john@example.com',
    'items' => [
        ['itemid' => 'prod_abc', 'quantity' => 2, 'rate' => 1999.99],
    ],
    'shippingaddress' => [
        'name'    => 'John Doe',
        'address' => '123 Main St',
        'city'    => 'Nairobi',
        'country' => 'KE',
        'phone'   => '254712345678',
    ],
]);

Payments — $cashfin->payments

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

// $payment['data']['checkoutrequestid'] — track via webhook

Customers — $cashfin->customers

$cashfin->customers->all(['type' => 'individual']);
$cashfin->customers->retrieve($id);
$cashfin->customers->create([
    'name'     => 'Jane Doe',
    'email'    => 'jane@example.com',
    'phone'    => '254700000000',
    'country'  => 'KE',
    'currency' => 'KES',
    'type'     => 'individual',
]);
$cashfin->customers->update($id, ['phone' => '254711111111']);

Invoices — $cashfin->invoices

$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 — $cashfin->subscriptions

$cashfin->subscriptions->all(['status' => 'active']);
$cashfin->subscriptions->retrieve($id);
$cashfin->subscriptions->create([
    'customerid'   => $customerId,
    'items'        => [['name' => 'Pro Plan', 'rate' => 2999]],
    'billingcycle' => 'monthly',   // daily | weekly | monthly | quarterly | yearly
    'autorenew'    => true,
]);

Payment Links — $cashfin->paymentLinks

$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 — $cashfin->transactions

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

Receipts — $cashfin->receipts

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

Vendors — $cashfin->vendors

$cashfin->vendors->all();
$cashfin->vendors->retrieve($id);
$cashfin->vendors->create(['name' => 'Acme Supplies', 'email' => 'acme@example.com']);
$cashfin->vendors->update($id, ['phone' => '254700000001']);

Expenses — $cashfin->expenses

$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 — $cashfin->bills

$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 — $cashfin->purchaseOrders

$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 — $cashfin->leads

$cashfin->leads->all(['status' => 'new']);
$cashfin->leads->retrieve($id);
$cashfin->leads->create([
    'name'    => 'Prospect Ltd',
    'email'   => 'contact@prospect.co.ke',
    'phone'   => '254700000002',
    'source'  => 'website',
    'budget'  => 100000,
]);
$cashfin->leads->update($id, ['status' => 'qualified']);

Quotes — $cashfin->quotes

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

Contacts — $cashfin->contacts

$cashfin->contacts->all();
$cashfin->contacts->retrieve($id);
$cashfin->contacts->create(['name' => 'Alice Smith', 'email' => 'alice@example.com']);
$cashfin->contacts->update($id, ['position' => 'CEO']);

Contracts — $cashfin->contracts

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

Bookings — $cashfin->bookings

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

Appointments — $cashfin->appointments

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

Campaigns — $cashfin->campaigns

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

Marketing Lists — $cashfin->lists

$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'     => 'subscriber@example.com',
    '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.

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' => 'New Product',
        'price' => 999,
    ]);
} 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 doesn't exist
    echo 'Not found: ' . $e->getMessage();
} catch (RateLimitException $e) {
    // 429 — retry after $e->getRetryAfter() seconds
    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

Cashfin sends webhook events to your endpoint when payment, order, or subscription events occur.

<?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'];
        // fulfil the order using $data['transactionid'], $data['amount'], etc.
        break;

    case 'order.created':
        // process the new order
        break;

    case 'invoice.paid':
        // mark invoice as settled in your system
        break;

    case 'subscription.renewed':
        // extend the customer's access
        break;
}

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

Laravel webhook route example:

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

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

        // Handle $event['type'] …

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

Pagination

All list methods accept page and limit parameters. The response includes a meta key:

$page = 1;
do {
    $result = $cashfin->products->all(['page' => $page, 'limit' => 50]);
    foreach ($result['data'] as $product) {
        // process $product
    }
    $page++;
} while ($result['meta']['hasNext']);

Debugging

Set debug => true in the config (or CASHFIN_DEBUG=true for Laravel) to log all requests and raw responses to PHP's error log:

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

Versioning

This SDK follows Semantic Versioning:

  • PATCH (1.0.x) — bug fixes, no breaking changes
  • MINOR (1.x.0) — new features, backwards-compatible
  • MAJOR (x.0.0) — breaking changes

Check CHANGELOG.md for the full history.

Contributing

Pull requests are welcome. Please run the test suite before submitting:

composer install
composer test

Support

  • Documentation: docs.cashfin.africa
  • Email: support@cashfin.africa
  • Issues: open a ticket in your Cashfin dashboard

License

MIT — see LICENSE.