PHP SDK
The official Cashfin SDK for PHP applications.
Installation
composer require cashfin/cashfin-phpRequirements
- PHP 8.0 or later
- Composer
ext-jsonandext-curlextensions (standard in most environments)
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 Options
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,
]);Response Format
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
List Products
$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
$product = $cashfin->products->retrieve('507f1f77bcf86cd799439011');
echo $product['data']['title'];
echo $product['data']['price'];Create Product
$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
$product = $cashfin->products->update('507f1f77bcf86cd799439011', [
'price' => 2499.99,
'stock' => 75,
]);Categories
List Categories
$categories = $cashfin->categories->all(['status' => 'active']);Get Category
$category = $cashfin->categories->retrieve('507f191e810c19729de860ea');Create Category
$category = $cashfin->categories->create([
'title' => 'Electronics',
'description' => 'Electronic devices and accessories',
'status' => 'active',
]);Update Category
$category = $cashfin->categories->update('507f191e810c19729de860ea', [
'description' => 'Updated description',
]);Orders
List Orders
$orders = $cashfin->orders->all(['status' => 'pending']);Get Order
$order = $cashfin->orders->retrieve('507f1f77bcf86cd799439011');Create Checkout
$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
$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 webhookCustomers
List Customers
$customers = $cashfin->customers->all(['type' => 'individual']);Get Customer
$customer = $cashfin->customers->retrieve('507f1f77bcf86cd799439011');Create Customer
$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
$cashfin->customers->update('507f1f77bcf86cd799439011', [
'phone' => '254711111111',
]);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
Create Subscription
$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
$subscription = $cashfin->subscriptions->retrieve('507f1f77bcf86cd799439020');
echo 'Status: ' . $subscription['data']['status'];
echo 'Next Billing: ' . $subscription['data']['nextbillingdate'];List Subscriptions
$cashfin->subscriptions->all(['status' => 'active']);Payment Links
$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->all(['status' => 'completed', 'method' => 'mpesa']);
$cashfin->transactions->retrieve($id);Receipts
$cashfin->receipts->all(['customerid' => $customerId]);
$cashfin->receipts->retrieve($id);Vendors
$cashfin->vendors->all();
$cashfin->vendors->retrieve($id);
$cashfin->vendors->create(['name' => 'Acme Supplies', 'email' => '[email protected]']);
$cashfin->vendors->update($id, ['phone' => '254700000001']);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->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->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->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
$cashfin->quotes->all(['status' => 'sent', 'customerid' => $customerId]);
$cashfin->quotes->retrieve($id);Contacts
$cashfin->contacts->all();
$cashfin->contacts->retrieve($id);
$cashfin->contacts->create(['name' => 'Alice Smith', 'email' => '[email protected]']);
$cashfin->contacts->update($id, ['position' => 'CEO']);Contracts
$cashfin->contracts->all(['status' => 'active']);
$cashfin->contracts->retrieve($id);Bookings
$cashfin->bookings->all(['status' => 'scheduled']);
$cashfin->bookings->retrieve($id);Appointments
$cashfin->appointments->all(['status' => 'active']);
$cashfin->appointments->retrieve($id);Campaigns
$cashfin->campaigns->all(['status' => 'sent', 'type' => 'email']);
$cashfin->campaigns->retrieve($id);Marketing 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' => '[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
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 messagegetStatusCode()— HTTP status codegetRequestId()— server-side request ID for support tracinggetErrorData()— raw response body as array
Webhooks
Verify Signature
<?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:
$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:
$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:
php artisan vendor:publish --tag=cashfin-configAdd your credentials to .env:
CASHFIN_API_KEY=cs_your_client_secret
CASHFIN_WEBHOOK_SECRET=your_webhook_secretConfig File
// config/services.php
return [
// ...
'cashfin' => [
'key' => env('CASHFIN_API_KEY'),
'secret' => env('CASHFIN_WEBHOOK_SECRET'),
],
];Dependency Injection
<?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
use Cashfin\Laravel\Facades\Cashfin;
$products = Cashfin::products()->all();Service Class
<?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
// 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:
// 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:
services:
Cashfin\CashfinClient:
arguments:
- api_key: '%env(CASHFIN_API_KEY)%'
timeout: 30
max_retries: 3Inject it into your controller or service via constructor injection:
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
// 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']);
}