Minimal PHP client for the NexGenSpin Coordinator API with Ed25519 request signing via ext-sodium.
This package demonstrates how to:
- Sign Coordinator requests as
timestamp + exact JSON body - Send the required authentication headers
- Query games, currencies, and languages
- Create demo and real-money sessions
The client itself accepts a single private key format:
- PKCS#8 DER encoded private key represented as hex
For convenience, the example script can read a PEM file and convert it into PKCS#8 DER hex before constructing the client.
- PHP 7.4+
ext-sodium
Compatibility has been verified with a live request in Docker using php:7.4-cli.
src/NexGenSpinCoordinatorClient.php— Coordinator clientsrc/KeyLoader.php— PEM to PKCS#8 DER hex helperexamples/smoke_test.php— runnable staging smoke testexamples/compose.yaml— local Docker/OrbStack smoke test
Install the package with Composer:
composer require nexgenspin/coordinatorThen autoload it through Composer:
require __DIR__ . '/vendor/autoload.php';The bundled example uses:
- Staging environment
- A hardcoded operator caller ID in
examples/smoke_test.php - A local PEM file at
./operator.pem
Run it with:
docker compose -f examples/compose.yaml run --rm phpBefore running the example, place your own private key at ./operator.pem or update the path in examples/smoke_test.php.
require __DIR__ . '/vendor/autoload.php';
use NexGenSpin\Coordinator\NexGenSpinCoordinatorClient;
$client = NexGenSpinCoordinatorClient::staging(
'019d4f70-2dfa-781a-9de6-39503e63c0f4',
'302e020100300506032b65700422042033a9b1b71e0deb2e8354098106c9d349bac6f46ae486303b4cf974a94fc7a2fb'
);require __DIR__ . '/vendor/autoload.php';
use NexGenSpin\Coordinator\KeyLoader;
use NexGenSpin\Coordinator\NexGenSpinCoordinatorClient;
$pkcs8DerHex = KeyLoader::pkcs8DerHexFromPemFile(__DIR__ . '/operator.pem');
$client = NexGenSpinCoordinatorClient::staging(
'019d4f70-2dfa-781a-9de6-39503e63c0f4',
$pkcs8DerHex
);require __DIR__ . '/vendor/autoload.php';
use NexGenSpin\Coordinator\KeyLoader;
use NexGenSpin\Coordinator\NexGenSpinCoordinatorClient;
$pemPath = __DIR__ . '/operator.pem';
$pkcs8DerHex = KeyLoader::pkcs8DerHexFromPemFile($pemPath);
$client = NexGenSpinCoordinatorClient::staging(
'019d4f70-2dfa-781a-9de6-39503e63c0f4',
$pkcs8DerHex
);
$games = $client->games();
print_r($games['response']['json']);Fetches the list of games available to the operator.
$games = $client->games();
print_r($games['response']['json']);Fetches the list of supported currencies.
$currencies = $client->currencies();
print_r($currencies['response']['json']);Fetches the list of supported languages.
$languages = $client->languages();
print_r($languages['response']['json']);Creates a game session.
For a demo session, omit the operator session and customer identifiers.
$demoSession = $client->createSession([
// Game identifier from the games query
'game' => ['id' => '019d2822-20ed-7212-95a9-def642d50ddf'],
// Currency for all transactions within this session (immutable)
'currency' => ['code' => 'USD'],
// Preferred language (BCP 47)
'languageTag' => 'en',
// IP address of the customer — supports both IPv4 and IPv6 formats
'ipAddress' => '203.0.113.42',
]);
print_r($demoSession['response']['json']);$session = $client->createSession([
// Session ID on the Operator's platform
'id' => 'SESSION_ID_ON_OPERATOR_SIDE',
// Game identifier from the games query
'game' => ['id' => '019d2822-20ed-7212-95a9-def642d50ddf'],
// Customer on the Operator's platform
'customer' => [
// Customer ID on the Operator's platform
'id' => 'CUSTOMER_ID_ON_OPERATOR_SIDE',
// Name is optional — displayed in the game interface, auto-generated if omitted
'name' => 'John',
// ISO 3166-1 alpha-2 country code
'countryCode' => 'US',
],
// Currency for all transactions within this session (immutable)
'currency' => ['code' => 'USD'],
// Preferred language (BCP 47)
'languageTag' => 'en',
// IP address of the customer — supports both IPv4 and IPv6 formats
'ipAddress' => '203.0.113.42',
]);
print_r($session['response']['json']);Each client method returns an array with:
request.timestamprequest.bodyrequest.signatureresponse.headersresponse.body_rawresponse.json
The response structure is intentionally verbose to support debugging and integration testing.
- Coordinator signatures are computed as
timestamp + exact JSON body - There is no delimiter between the timestamp and the body
- The body you sign must be byte-for-byte identical to the body you send
- GraphQL requests with no variables must send
{}rather than[]