PHP SDK roadmap
The official Credicorp client for PHP. It authenticates, caches tokens, retries on back-off and verifies webhooks, so you write only the part that is specific to your application. Built for PHP 8.1 and up, framework-agnostic, PSR-compliant.
Roadmap — this SDK does not exist yet. The credicorp/credicorp-php package, sk_test_…/sk_live_… credentials and the applications, payments and webhook_endpoints routes shown throughout this page are part of the planned partner/v1 write plane and are not yet available — those routes return 503 Service Unavailable. The live partner/v1 surface today is the OAuth token plane plus the authenticated read-only MCP tier. Start with the Quickstart; we will announce the SDK and write plane on the changelog.
Requirements
- PHP
- 8.1 or newer, with the
jsonandcurlextensions enabled. - Composer
- 2.x, to install and autoload the package.
- Credentials
- A project secret key —
sk_test_…for sandbox,sk_live_…for live.
Install
Add the package with Composer. It pulls in a PSR-18 HTTP client and PSR-7 messages; no global state, no extensions beyond core.
composer require credicorp/credicorp-phpThen make sure Composer's autoloader is on the include path — in most projects it already is:
require __DIR__ . '/vendor/autoload.php';
Configure from the environment
Never hard-code a secret key. Read it from the environment so the same code runs unchanged in sandbox and production — the client selects the environment from the key prefix, so a sk_test_… key can never reach live money.
# .env — keep this out of version control CC_SECRET_KEY=sk_test_8Kd2c9QmRtPLm0eXa4 CC_WEBHOOK_SECRET=whsec_3fa7c9Qm0eRtPLm
use Credicorp\Credicorp; // Simplest form — just the key. $cc = new Credicorp(getenv('CC_SECRET_KEY')); // Or pass options for full control. $cc = new Credicorp([ 'secret_key' => getenv('CC_SECRET_KEY'), 'timeout' => 30, // seconds per request 'max_retries' => 5, // 429/5xx, full-jitter back-off ]);
The client performs the OAuth client-credentials exchange on first use and caches the access token in memory until shortly before it expires. You do not call /oauth/token yourself — see OAuth 2.0.
Your first create call
Open an application for a UK incorporated company. The client sends an Idempotency-Key automatically, so retrying this call can never create two applications. Read back the handoff_url and redirect the applicant into the hosted journey.
use Credicorp\Credicorp; use Credicorp\Exception\ApiException; $cc = new Credicorp(getenv('CC_SECRET_KEY')); try { $app = $cc->applications->create([ 'business' => ['company_number' => '16093826'], 'amount_pence' => 2500000, // £25,000 'term_months' => 12, 'purpose' => 'working_capital', 'redirect_uri' => 'https://yourapp.com/return', 'reference' => 'INV-3045', // echoed on every webhook ]); // Send the applicant here to complete the journey. header('Location: ' . $app->handoff_url); } catch (ApiException $e) { // Typed: $e->getType(), $e->getRequestId(), $e->getStatusCode() error_log("Credicorp error {$e->getType()} (req {$e->getRequestId()})"); }
Response 201 Created
{
"id": "app_8Kd2c9Qm",
"object": "application",
"status": "created",
"amount_pence": 2500000,
"term_months": 12,
"reference": "INV-3045",
"handoff_url": "https://apply.credicorp.co.uk/s/3fa7…",
"created_at": "2026-06-29T10:00:00Z"
}Amounts are always integers in pence. Pass 2500000 for £25,000 — never a float. The principal must be at least £1,000 and the term between 3 and 60 months. See the Apply API for the full schema.
Retrieve and act on it later
$app = $cc->applications->retrieve('app_8Kd2c9Qm'); if ($app->status === 'funded') { $account = $cc->accounts->retrieve($app->account_id); // …reconcile against your ledger }
Pagination
List endpoints are cursor-paginated. The PHP client wraps them in a lazy iterator that follows the next_cursor for you — loop over it directly and the SDK fetches each page on demand. You never read has_more or thread a cursor by hand.
// autoPagingIterator() transparently walks every page. $funded = $cc->applications->all([ 'status' => 'funded', 'limit' => 50, // page size; 100 is the max ]); foreach ($funded->autoPagingIterator() as $app) { echo $app->id . ' ' . $app->reference . PHP_EOL; }
Need one page at a time — for example to render a table with a "load more" button? Read the page and pass its cursor back on the next call.
$page = $cc->applications->all(['limit' => 20]); foreach ($page->data as $app) { /* … */ } if ($page->has_more) { $next = $cc->applications->all([ 'limit' => 20, 'cursor' => $page->next_cursor, ]); }
See Pagination for the cursor contract and response envelope.
Verifying webhooks
Credicorp signs every webhook with the Credicorp-Signature header. The SDK's Webhook::constructEvent() helper verifies that signature against your endpoint's signing secret, rejects anything outside the timestamp tolerance (guarding against replays), and returns a typed event — all in one call. Always verify before you trust a payload.
use Credicorp\Webhook; use Credicorp\Exception\SignatureVerificationException; $payload = file_get_contents('php://input'); $sig = $_SERVER['HTTP_CREDICORP_SIGNATURE'] ?? ''; try { $event = Webhook::constructEvent( $payload, $sig, getenv('CC_WEBHOOK_SECRET') // whsec_… ); } catch (SignatureVerificationException $e) { http_response_code(400); // forged or stale — reject exit; } switch ($event->type) { case 'application.funded': $accountId = $event->data->object->account_id; // …mark the deal funded in your system break; case 'payment.paid': // …reconcile the repayment break; } http_response_code(200); // ack within 5s or we retry
Verify against the raw request body — do not json_decode and re-encode first, or the signature will not match. Acknowledge with a 2xx within five seconds; deliveries that time out are retried with back-off. See Webhooks roadmap.
Error handling
Failed requests raise a typed ApiException (or a subclass such as RateLimitException or InvalidRequestException). Every exception carries the correlation_id — include it when you contact support so we can trace the call.
| Exception | HTTP | When |
|---|---|---|
InvalidRequestException | 400 | Bad or missing parameters. |
AuthenticationException | 401 | Missing or invalid key. |
RateLimitException | 429 | Quota exceeded; retried automatically. |
ApiException | 5xx | Server-side fault; retried automatically. |
That is the whole loop — install, configure, create, list, and verify. Every method on the client maps 1:1 to an endpoint in the API reference, and the integrate lending guide walks the full apply-to-funded journey end to end.
