SDKs

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 json and curl extensions 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.

bash
composer require credicorp/credicorp-php

Then make sure Composer's autoloader is on the include path — in most projects it already is:

php
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
# .env — keep this out of version control
CC_SECRET_KEY=sk_test_8Kd2c9QmRtPLm0eXa4
CC_WEBHOOK_SECRET=whsec_3fa7c9Qm0eRtPLm
php
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.

POST/partner/v1/applications
php
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

json
{
  "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

php
$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.

GET/partner/v1/applications
php
// 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.

php
$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.

php
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.

ExceptionHTTPWhen
InvalidRequestException400Bad or missing parameters.
AuthenticationException401Missing or invalid key.
RateLimitException429Quota exceeded; retried automatically.
ApiException5xxServer-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.