Integrations

PHP

Server-side steps in PHP.

Prerequisites

  • PHP with the openssl and curl extensions.
  • The secret key of the scenario and a permanent API token, read from environment variables. Never put them in browser code or in a repository.
  • The API address provided with your access.

The call sequence

  1. Your server encrypts the raw client key with the secret key of the scenario.
  2. Your page opens the widget with the scenario identifier and the encrypted client key.
  3. The user completes the steps in the widget.
  4. Your server learns of the result from the SESSION_COMPLETE webhook, or reads session status.
  5. Your server decides on the customer from the server-side status.

Encrypt the client key

The secret key of the scenario never leaves your server. See working with clientKey for the algorithm.

<?php

function encryptClientKey(string $clientKeyRaw, string $secret): string
{
    // First 32 characters of the SHA-256 hash of the secret, written as hex.
    $key = substr(hash('sha256', $secret), 0, 32);
    $iv = random_bytes(16);

    $encrypted = openssl_encrypt($clientKeyRaw, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv);
    if ($encrypted === false) {
        throw new RuntimeException('client key encryption failed');
    }

    return base64_encode($iv . $encrypted);   // the clientKey for the widget
}

header('Content-Type: application/json');
$clientKeyRaw = (string) $currentUserId;       // your own identifier, at most 36 characters
echo json_encode(['clientKey' => encryptClientKey($clientKeyRaw, getenv('SCENARIO_SECRET_KEY'))]);

Store the raw value with your user or order, so that you can match the result later.

Create a session

<?php

function apiPost(string $path, array $body): array
{
    $ch = curl_init(getenv('API_BASE_URL') . $path);
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
        CURLOPT_POSTFIELDS => json_encode($body),
        CURLOPT_RETURNTRANSFER => true,
    ]);
    $reply = curl_exec($ch);
    curl_close($ch);
    return json_decode($reply, true) ?? [];
}

$session = apiPost('/kyc/session/create', [
    'token' => getenv('API_TOKEN'),              // permanent API token
    'schemaId' => getenv('SCENARIO_ID'),         // scenario identifier
]);

if (!isset($session['sessionId'])) {
    // A failure arrives as a body with status "error" or "failed".
    throw new RuntimeException('session was not created: ' . ($session['status'] ?? 'unknown'));
}
// $session['status'] is the session status, "idle"

Read the result with apiPost('/kyc/session/status', ['token' => ..., 'sessionId' => ...]) and branch on status, never on message text. See response format and errors and session statuses.

Troubleshooting

SymptomWhat to check
The widget does not openThe domain is in the allowed domains, the page is on HTTPS, and the widget is not inside a cross-origin iframe. See installation.
The widget reports that decryption failedThe client key was not encrypted, or a different secret key was used, or the string was cut. See working with clientKey.
A call returns success but the body has an errorBranch on the status field of the body. See response format and errors.
The result never reaches your serverCheck the callback URL in the scenario. See webhooks.

Next: receive the result

Set up a webhook endpoint on your backend.

Receiving webhooks