Coupon API — PHP example
This PHP 8.1+ example implements the complete server-side flow: retrieve current outcomes, authenticate, place the selected coupon type, and query its state.
Requirements and configuration
Install PHP 8.1 or newer with the curl extension. Save the source as
coupon-example.php and configure these environment variables:
export COUPON_API_BASE_URL='https://YOUR_COUPON_API_DOMAIN'
export COUPON_LOGIN='YOUR_LOGIN'
export COUPON_PASSWORD='YOUR_PASSWORD'
export SPORT_LINE_BASE_URL='https://YOUR_SPORT_LINE_DOMAIN'
export SPORT_LINE_PACKAGE='YOUR_SPORT_LINE_API_KEY'
Optional variables are the same as in the JavaScript example:
| Variable | Default | Values |
|---|---|---|
COUPON_EXAMPLE_MODE | single | single, express, multi |
COUPON_AMOUNT | 10 | A positive number |
COUPON_CURRENCY | USD | Currency code or internal currency name |
SPORT_LINE_TYPE | live | live, line |
DOCUMENT_LANGUAGE | en | Two-letter language code |
Complete runnable example
The same verified source is rendered in both documentation languages.
<?php
declare(strict_types=1);
function requiredEnv(string $name): string
{
$value = trim((string) getenv($name));
if ($value === '') {
throw new RuntimeException("{$name} is not configured");
}
return $value;
}
$couponBaseUrl = rtrim(requiredEnv('COUPON_API_BASE_URL'), '/');
$couponLogin = requiredEnv('COUPON_LOGIN');
$couponPassword = requiredEnv('COUPON_PASSWORD');
$sportLineBaseUrl = rtrim(requiredEnv('SPORT_LINE_BASE_URL'), '/');
$sportLinePackage = requiredEnv('SPORT_LINE_PACKAGE');
$exampleMode = getenv('COUPON_EXAMPLE_MODE') ?: 'single';
$amount = (float) (getenv('COUPON_AMOUNT') ?: '10');
$currency = getenv('COUPON_CURRENCY') ?: 'USD';
$lineType = getenv('SPORT_LINE_TYPE') ?: 'live';
$language = getenv('DOCUMENT_LANGUAGE') ?: 'en';
if (!in_array($exampleMode, ['single', 'express', 'multi'], true)) {
throw new RuntimeException('COUPON_EXAMPLE_MODE must be single, express, or multi');
}
if (!in_array($lineType, ['live', 'line'], true)) {
throw new RuntimeException('SPORT_LINE_TYPE must be live or line');
}
if (!is_finite($amount) || $amount <= 0) {
throw new RuntimeException('COUPON_AMOUNT must be a positive number');
}
function requestJson(
string $url,
string $method = 'GET',
array $headers = [],
?array $body = null,
): array {
$curl = curl_init($url);
if ($curl === false) {
throw new RuntimeException('Unable to initialize cURL');
}
$httpHeaders = array_merge(['Accept: application/json'], $headers);
curl_setopt_array($curl, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => $httpHeaders,
CURLOPT_CONNECTTIMEOUT => 5,
CURLOPT_TIMEOUT => 15,
]);
if ($body !== null) {
curl_setopt(
$curl,
CURLOPT_POSTFIELDS,
json_encode($body, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE),
);
}
$rawResponse = curl_exec($curl);
if ($rawResponse === false) {
$message = curl_error($curl);
curl_close($curl);
throw new RuntimeException("Network error: {$message}");
}
$httpStatus = (int) curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
curl_close($curl);
$payload = json_decode($rawResponse, true, 512, JSON_THROW_ON_ERROR);
if ($httpStatus < 200 || $httpStatus >= 300) {
$message = $payload['error_message'] ?? 'request failed';
throw new RuntimeException("HTTP {$httpStatus}: {$message}");
}
return $payload;
}
function requireCouponSuccess(array $payload, string $operation): array
{
if (($payload['code'] ?? null) !== 1) {
$code = $payload['error_code'] ?? 'unknown';
$message = $payload['error_message'] ?? 'error';
throw new RuntimeException("{$operation}: {$code} {$message}");
}
return $payload['body'];
}
function login(string $baseUrl, string $username, string $password): string
{
$payload = requestJson(
"{$baseUrl}/api/partner/login",
'POST',
['Content-Type: application/json'],
['username' => $username, 'password' => $password],
);
return requireCouponSuccess($payload, 'Login')['token'];
}
function sportLineGet(string $baseUrl, string $package, string $path): array
{
return requestJson("{$baseUrl}{$path}", 'GET', ["Package: {$package}"]);
}
function findGames(
string $baseUrl,
string $package,
string $lineType,
string $language,
int $limit,
): array {
$menu = sportLineGet($baseUrl, $package, "/v1/menu/{$lineType}/{$language}");
$games = [];
foreach ($menu['body'] ?? [] as $sport) {
foreach ($sport['sub'] ?? [] as $country) {
foreach ($country['sub'] ?? [] as $tournament) {
$path = "/v1/events/{$sport['id']}/{$tournament['id']}"
. "/sub/50/{$lineType}/{$language}";
$result = sportLineGet($baseUrl, $package, $path);
foreach ($result['body'] ?? [] as $group) {
foreach ($group['events_list'] ?? [] as $game) {
$games[(string) $game['game_id']] = $game;
if (count($games) >= $limit) {
return array_values($games);
}
}
}
}
}
}
throw new RuntimeException(
sprintf('Only %d suitable events found; %d required', count($games), $limit),
);
}
function findOutcome(mixed $value): ?array
{
if (!is_array($value)) {
return null;
}
$rate = $value['oc_rate'] ?? null;
$block = $value['oc_block'] ?? false;
if (
isset($value['oc_pointer']) &&
is_numeric($rate) &&
(float) $rate > 1 &&
($block === false || $block === 0) &&
!isset($value['op_id'])
) {
return $value;
}
foreach ($value as $child) {
$outcome = findOutcome($child);
if ($outcome !== null) {
return $outcome;
}
}
return null;
}
function getSelection(
string $baseUrl,
string $package,
string $lineType,
string $language,
array $game,
): array {
$path = "/v1/event/{$game['game_id']}/group/{$lineType}/{$language}";
$event = sportLineGet($baseUrl, $package, $path);
$outcome = findOutcome($event['body'] ?? []);
if ($outcome === null) {
throw new RuntimeException("No available outcome for game {$game['game_id']}");
}
$technicalPointer = str_replace('|', '#', $outcome['oc_pointer']);
$playerPart = isset($outcome['op_id']) ? "#{$outcome['op_id']}" : '';
return [
'pointer' => "{$lineType}#{$technicalPointer}#{$outcome['oc_rate']}{$playerPart}",
'gameId' => $game['game_id'],
'event' => "{$game['opp_1_name']} — {$game['opp_2_name']}",
'market' => $outcome['oc_group_name'] ?? null,
'outcome' => $outcome['oc_name'] ?? null,
'coefficient' => $outcome['oc_rate'],
];
}
function couponRequest(
string $baseUrl,
string $token,
string $path,
string $method = 'GET',
?array $body = null,
): array {
$headers = ["Authorization: Bearer {$token}"];
if ($body !== null) {
$headers[] = 'Content-Type: application/json';
}
$payload = requestJson("{$baseUrl}{$path}", $method, $headers, $body);
return requireCouponSuccess($payload, $path);
}
function summarizeCoupon(array $coupon): array
{
$couponStatuses = [0 => 'NEW', 2 => 'WIN', 4 => 'LOSE', 8 => 'RETURN', 15 => 'UPDATE'];
$betStatuses = [
0 => 'NET', 1 => 'WIN', 2 => 'LOSE', 3 => 'RETURN', 4 => 'RECALCULATE',
21 => 'HALF_WIN', 22 => 'HALF_LOSE', 23 => 'PUSH',
];
return [
'couponCode' => $coupon['coupon_code'],
'status' => $coupon['status'],
'statusName' => $couponStatuses[$coupon['status']] ?? 'UNKNOWN',
'amount' => $coupon['amount'],
'coefficient' => $coupon['coef'],
'realWin' => $coupon['real_win'] ?? null,
'events' => array_map(
static fn (array $event): array => [
'gameId' => $event['game_id'],
'bet' => $event['bet_name'],
'status' => $event['status'],
'statusName' => $betStatuses[$event['status']] ?? 'UNKNOWN',
],
$coupon['events_data'] ?? [],
),
];
}
function printJson(string $label, mixed $value): void
{
echo $label, PHP_EOL;
echo json_encode(
$value,
JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR,
), PHP_EOL;
}
$requiredGames = $exampleMode === 'single' ? 1 : 2;
$games = findGames(
$sportLineBaseUrl,
$sportLinePackage,
$lineType,
$language,
$requiredGames,
);
$selections = array_map(
fn (array $game): array => getSelection(
$sportLineBaseUrl,
$sportLinePackage,
$lineType,
$language,
$game,
),
$games,
);
printJson('Selected outcomes:', array_map(
static function (array $selection): array {
unset($selection['pointer']);
return $selection;
},
$selections,
));
$token = login($couponBaseUrl, $couponLogin, $couponPassword);
$placement = couponRequest(
$couponBaseUrl,
$token,
'/api/partner/coupons/place',
'POST',
[
'list_bets' => array_column($selections, 'pointer'),
'amount' => $amount,
'currency' => $currency,
'callback_url' => null,
'lang' => $language,
'mode' => 'reject',
'mode_type' => null,
'multi' => $exampleMode === 'multi',
],
);
$created = $placement['coupons'];
printJson('Created coupons:', array_map('summarizeCoupon', $created));
$couponCodes = array_column($created, 'coupon_code');
$current = [];
foreach ($couponCodes as $couponCode) {
$current[] = couponRequest(
$couponBaseUrl,
$token,
'/api/partner/coupons/get?coupon_code=' . rawurlencode($couponCode),
);
}
printJson('Current state:', array_map('summarizeCoupon', $current));
$active = couponRequest($couponBaseUrl, $token, '/api/partner/coupons/active');
printJson('Active coupons:', array_map('summarizeCoupon', $active));
$calculated = couponRequest(
$couponBaseUrl,
$token,
'/api/partner/coupons/calculated?time=120',
);
printJson('Recently settled:', array_map('summarizeCoupon', $calculated));
$batch = couponRequest(
$couponBaseUrl,
$token,
'/api/partner/coupons/results',
'POST',
['coupon_ids' => $couponCodes],
);
printJson('Batch result:', array_map('summarizeCoupon', $batch['coupons']));
Run
COUPON_EXAMPLE_MODE=single php coupon-example.php
COUPON_EXAMPLE_MODE=express php coupon-example.php
COUPON_EXAMPLE_MODE=multi php coupon-example.php
Every run creates real coupons in the configured system. With multi, the amount
applies to every single. Do not log the JWT or password, and do not blindly retry
placement after a timeout.
See the JavaScript example, cURL example, and Singles, accumulators, and multi.