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_MODE | single | single, express, multi |
COUPON_AMOUNT | 10 | Положительное число |
COUPON_CURRENCY | USD | Код или внутреннее название валюты |
SPORT_LINE_TYPE | live | live, line |
DOCUMENT_LANGUAGE | en | Двухбуквенный код языка |
Полный исполняемый пример
<?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».