SportAPI Documentation
EN
C Product documentationCoupon API
v1
Service & pricing ↗ Get access ↗
Coupon API / PHP

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:

VariableDefaultValues
COUPON_EXAMPLE_MODEsinglesingle, express, multi
COUPON_AMOUNT10A positive number
COUPON_CURRENCYUSDCurrency code or internal currency name
SPORT_LINE_TYPElivelive, line
DOCUMENT_LANGUAGEenTwo-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.