cashfin / cashfin-php
Official PHP SDK for the Cashfin Business API
Requires
- php: ^8.0
- ext-curl: *
- ext-json: *
- guzzlehttp/guzzle: ^7.0
Requires (Dev)
- mockery/mockery: ^1.6
- phpunit/phpunit: ^10.0
- squizlabs/php_codesniffer: ^3.7
This package is auto-updated.
Last update: 2026-08-14 11:51:58 UTC
README
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-jsonandext-curlextensions (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:
| Key | Type | Default | Description |
|---|---|---|---|
api_key | string | required | Your Cashfin client secret (cs_…) |
timeout | int | 30 | Request timeout in seconds |
max_retries | int | 3 | Retry attempts on transient failures |
debug | bool | false | Log all requests/responses via error_log() |
base_url | string | https://api.cashfin.africa/business | Override 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 messagegetStatusCode()— HTTP status codegetRequestId()— server-side request ID for support tracinggetErrorData()— 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.