SportAPI Документация
RU
C Документация продуктаCoupon API
v1
Услуга и цены ↗ Получить доступ ↗
Coupon API / PHP

Coupon API — пример на PHP

Пример для PHP 8.1+ выполняет полный серверный сценарий: получает актуальные исходы, авторизуется, создаёт выбранный тип купона и запрашивает его состояние.

Требования и настройки

Понадобятся PHP 8.1 или новее и расширение curl. Сохраните код в coupon-example.php и задайте переменные окружения:

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'

Дополнительные переменные совпадают с JavaScript-примером:

ПеременнаяПо умолчаниюВозможные значения
COUPON_EXAMPLE_MODEsinglesingle, express, multi
COUPON_AMOUNT10Положительное число
COUPON_CURRENCYUSDКод или внутреннее название валюты
SPORT_LINE_TYPElivelive, line
DOCUMENT_LANGUAGEenДвухбуквенный код языка

Полный исполняемый пример

<?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']));

Запуск

COUPON_EXAMPLE_MODE=single php coupon-example.php
COUPON_EXAMPLE_MODE=express php coupon-example.php
COUPON_EXAMPLE_MODE=multi php coupon-example.php

Каждый запуск создаёт реальные купоны в указанной системе. При multi сумма применяется к каждому ординару. Не выводите JWT и пароль в журналы и не повторяйте создание вслепую после timeout.

См. также JavaScript-пример, cURL-пример и «Ординары, экспрессы и multi».