SportApi
Documentación de API · versión 1.2.0

Cuotas, apuestas y cupones — documentación técnica de API

Este archivo contiene un script mínimo e independiente para conectarse al sistema de cálculo de cupones SportAPI. Los detalles y los casos raros se encuentran en documentación completa.

Flujo principal http
POST /api/partner/login
POST /api/partner/coupons/place
GET  /api/partner/coupons/calculated?time=10

1. Lo que necesitas obtener del gerente.

  • API de URL base;
  • inicio de sesión y contraseña de la cuenta del cliente;
  • cuando utilice la devolución de llamada, habilite la función y la frase secreta callback_secret.

Los ejemplos utilizan una dirección condicional:

BASE_URL="https://coupon-api.example.com"

En producción, utilice HTTPS. Todas las fechas se transmiten como marca de tiempo Unix en milisegundos y los valores monetarios se transmiten como números decimales sin precisión fija.

2. Formato de respuesta API

Éxito:

{
  "code": 1,
  "body": {},
  "error_code": null,
  "error_message": null,
  "date": 1784970000000,
  "time_ms": 5,
  "path": "/api/partner/example"
}

El error comercial generalmente también viene con HTTP 200:

{
  "code": 0,
  "body": null,
  "error_code": 1002,
  "error_message": "Not all params",
  "date": 1784970000000,
  "time_ms": 5,
  "path": "/api/partner/example"
}

Verifique siempre el código HTTP, luego code, luego error_code. No utilice el texto error_message como clave de software.

3. Autorización

POST /api/partner/login
Content-Type: application/json
{
  "username": "partner-demo",
  "password": "strong-password"
}

El campo login se admite como alias compatible username.

Respuesta exitosa:

{
  "code": 1,
  "body": {
    "token": "<jwt-token>",
    "user_id": 17,
    "username": "partner-demo"
  },
  "error_code": null,
  "error_message": null
}

En todas las solicitudes protegidas, pase:

Authorization: Bearer <jwt-token>

Errores básicos de inicio de sesión:

CódigoRazón
1002Nombre de usuario o contraseña no enviados.
1003Inicio de sesión desconocido o contraseña incorrecta.
1004La cuenta del cliente ha sido deshabilitada.
1006La fecha de acceso ha caducado.
1007El saldo del cliente es cero o negativo.

Estos errores se devuelven desde HTTP 200, code = 0. HTTP 401 de un método protegido significa que el JWT falta, no es válido, ha caducado o está revocado; Inicia sesión nuevamente. HTTP 403 significa rol inadecuado o acceso denegado.

4. Indicador de tarifa

Cada resultado seleccionado se transmite como una línea ya preparada de la línea deportiva:

line_type#game_id#group_id#type_id#rate#coefficient[#player_id]

Ejemplos:

line#737779544#1#1#0#1.85
live#738917381|119|5869|0.5#3.6#149439538
  • line_type: line o live;
  • rate: parámetro total/handicap o 0;
  • player_id: ID de jugador opcional;
  • Se admiten los delimitadores # y |.

No recopile ni corrija el puntero manualmente; pase el valor obtenido de la línea sin cambios.

5. Crear un cupón

POST /api/partner/coupons/place
Authorization: Bearer <jwt-token>
Content-Type: application/json
{
  "list_bets": [
    "line#737779544#1#1#0#1.85"
  ],
  "amount": 10,
  "currency": "USD",
  "callback_url": "https://partner.example.com/api/coupon-result",
  "lang": "en",
  "mode": "reject",
  "mode_type": null,
  "multi": false
}
campoObligatorioPropósito
list_betssiUno o más punteros.
amountsiSe crea el importe positivo de un cupón.
currencyNoCualquier designación de cadena, incluida la moneda virtual.
callback_urlNodevolución de llamada de URL; sin una devolución de llamada no puede enviarlo, puede pasar null, una cadena vacía o un dominio de sitio.
langNoUn código de dos letras para uno de los aproximadamente 50 idiomas admitidos. El lenguaje de los nombres se fija en el momento de la creación.
modeNoreject o accept; predeterminado reject.
mode_typePara acceptDirección permitida del cambio de coeficiente.
multiNoUn cupón general o individual individual; predeterminado false.

probabilidades

ConfiguraciónComportamiento
mode = rejectRechazar la creación cuando cambie el coeficiente.
mode = accept, mode_type = 1Acepta sólo promociones.
mode = accept, mode_type = 2Acepte sólo la degradación.
mode = accept, mode_type = 3Acepta cualquier cambio.

Ordinario, expreso y multi

SolicitarResultadoCancelación del saldo del cliente
Un resultadouno ordinarioamount
Múltiples resultados, multi = falseun expresoamount
Múltiples resultados, multi = trueSencillo separado para cada resultadoamount × número de cupones creados

Un tren expreso no puede contener más de 15 eventos. No puedes combinar múltiples apuestas en el mismo partido, incluido el partido principal, mitades, períodos, córners, faltas y otros subeventos relacionados. Esta combinación devuelve 506.

6. Confirmación de creación de cupón y oferta.

Respuesta completa exitosa con cupón y datos de apuesta aceptada dentro de events_data:

{
  "code": 1,
  "body": {
    "coupons": [
      {
        "coupon_code": "000000000272",
        "amount": 10,
        "win": 18.5,
        "potential_win": 18.5,
        "real_win": null,
        "coef": 1.85,
        "original_coef": 1.85,
        "calculate_coef": null,
        "has_return": false,
        "date": 1784970000000,
        "status": 0,
        "asian": false,
        "calculate_date": null,
        "coupon_type": 1,
        "events_count": 1,
        "events_data": [
          {
            "id": 912,
            "game_id": 737779544,
            "main_game_id": 737779544,
            "is_sub_game": false,
            "parent_game_id": null,
            "sub_game_key": null,
            "raw_pointer": "line#737779544#1#1#0#1.85",
            "line_type": "line",
            "is_live": false,
            "bet_group_id": 1,
            "bet_group_name": "Match result",
            "bet_id": 1,
            "bet_name": "First team to win",
            "sport_id": 1,
            "sport_name": "Football",
            "tournament_id": 10001,
            "tournament": "National League",
            "event_date": 1784971800000,
            "status": 0,
            "opp1": "Team A",
            "opp2": "Team B",
            "coef": 1.85,
            "calc_coef": null,
            "bet_score": "line#737779544#1#1#0#1.85",
            "calculate_date": null,
            "calculate_score": null,
            "settlement_reason_code": null,
            "settlement_reason": null,
            "placement_score_full": null,
            "placement_score_periods": null,
            "calculation_score_full": null,
            "calculation_score_periods": null,
            "timer": null,
            "dop_name": null,
            "rate": "0",
            "sgame_id": null,
            "game_num": null,
            "stat_id": null,
            "team1_id": 101,
            "team2_id": 102,
            "opp_icon1": 101,
            "opp_icon2": 102
          }
        ]
      }
    ]
  },
  "error_code": null,
  "error_message": null,
  "date": 1784970000015,
  "time_ms": 45,
  "path": "/api/partner/coupons/place"
}

Campos de cupón

Cada artículo body.coupons[] es un cupón independiente creado.

campoTipoSignificado
coupon_codestringCódigo de cupón público de 12 dígitos. Guárdelo como una cadena para evitar perder los ceros a la izquierda.
amountnumberEl monto del cupón transferido al momento de la creación.
winnumberMonto ganador mostrado actualmente.
potential_winnumberPosibles ganancias antes de la liquidación final.
real_winnumber/nullMonto de pago real después del cálculo; antes del cálculo - null.
coefnumberRelación de cupón actual o final.
original_coefnumberEl coeficiente total del cupón en el momento de su creación.
calculate_coefnumber/nullCoeficiente calculado final; antes del cálculo - null.
has_returnbooleantrue, si el cupón contiene una apuesta con estado de reembolso.
dateintegerFecha de creación del cupón, marca de tiempo Unix en milisegundos.
statusintegerEstado actual del cupón. Los valores se describen en la sección “Estado del cupón y de la oferta”.
asianbooleanSigno de la presencia de un acuerdo asiático, incluida la mitad de ganancias o pérdidas.
calculate_dateinteger/nullFecha de cálculo del cupón en milisegundos; antes del cálculo - null.
coupon_typeintegerTipo de cupón: 1 - ordinario, 2 - exprés.
events_countintegerNúmero de apuestas dentro del cupón.
events_dataarrayToda la gama de apuestas incluidas en el cupón.

Campos de oferta

Cada elemento de events_data[] describe una apuesta aceptada específica dentro del cupón.

campoTipoSignificado
idinteger/nullID interno de la apuesta aceptada. Se utiliza junto con coupon_code para buscar una tarifa dentro de un cupón.
game_idintegerID del evento o subevento en el que se realiza la apuesta.
main_game_idinteger/nullID del partido principal al que pertenece el evento o subevento.
is_sub_gamebooleantrue, si la apuesta se relaciona con una mitad, período, set, córners u otro subevento.
parent_game_idinteger/nullEl ID del evento principal inmediato, si existe.
sub_game_keystring/nullClave técnica de un subevento o período.
raw_pointerstringEl indicador de tasa original aceptado por la API.
line_typestringTipo de línea: line - pre-partido, live - evento en tiempo real.
is_livebooleantrue, si la apuesta se creó en una línea viva.
bet_group_idintegerID del grupo de apuesta o mercado.
bet_group_namestring/nullNombre localizado del grupo de apuestas.
bet_idintegerID de la selección seleccionada dentro del grupo de apuestas.
bet_namestring/nullNombre completo localizado de la selección seleccionada.
sport_idinteger/nullIdentificación deportiva.
sport_namestring/nullNombre localizado del deporte.
tournament_idinteger/nullIdentificación del torneo.
tournamentstring/nullNombre localizado del torneo.
event_dateinteger/nullFecha de inicio del evento, marca de tiempo de Unix en milisegundos.
statusintegerEstado actual del cálculo de la tarifa. No confundir con el estado del cupón.
opp1string/nullNombre del primer equipo o participante.
opp2string/nullNombre del segundo equipo o participante.
coefnumberLas probabilidades de apuesta a las que se aceptó la apuesta.
calc_coefnumber/nullMultiplicador de apuesta calculado; antes del cálculo - null.
bet_scorestringCampo compatible con API heredada que contiene el indicador de apuesta en lugar del resultado del partido.
calculate_dateinteger/nullFecha de cálculo de la tarifa en milisegundos; antes del cálculo - null.
calculate_scorestring/nullLa cuenta utilizada para calcular la apuesta.
settlement_reason_codestring/nullCódigo de máquina estable del motivo del pago o devolución; antes del cálculo - null.
settlement_reasonstring/nullTexto explicativo del motivo del pago o devolución; antes del cálculo - null.
placement_score_fullstring/nullPuntuación total del evento en el momento en que se creó la apuesta, si está disponible.
placement_score_periodsstring/nullPuntuación por período en el momento en que se creó la apuesta, si está disponible.
calculation_score_fullstring/nullLa puntuación total del evento en el momento en que se liquida la apuesta.
calculation_score_periodsstring/nullCuenta por período al momento del cálculo de la tarifa.
timerinteger/nullTemporizador de eventos en el momento de guardar los datos, si está disponible.
dop_namestring/nullNombre del subevento: mitad, período, set, entrada, etc.
ratestringParámetro de resultado, por ejemplo, el valor del total o handicap; para un resultado sin parámetro - "0".
sgame_idstring/nullClave externa del subevento.
game_numinteger/nullNúmero de juego, si lo proporciona la fuente.
stat_idstring/nullID de evento estadístico externo.
team1_idinteger/nullDNI del primer equipo o participante.
team2_idinteger/nullDNI del segundo equipo o participante.
opp_icon1integer/nullID compatible del primer icono de comando; coincide con team1_id.
opp_icon2integer/nullID de icono del segundo equipo compatible; coincide con team2_id.

El valor null es normal para datos que aún no se han calculado o no están disponibles. No lo reemplace automáticamente con 0 o la cadena vacía.

Considere el cupón aceptado sólo si code = 1 y la presencia de objetos en body.coupons. Ante esto, la canasta es una elección preliminar: la selección podría desaparecer, ser bloqueada o cambiar las cuotas.

Después del éxito:

  1. guarde todos los objetos body.coupons, no solo el primero;
  2. almacene coupon_code como una cadena con ceros a la izquierda;
  3. guarde los datos requeridos de events_data;
  4. hacer coincidir el cupón con el usuario final;
  5. registrar la transacción financiera del usuario en el sistema del socio.

Identificadores:

campoPropósito
coupon_codeCódigo de cupón público.
events_data[].idID de la apuesta aceptada específica dentro del cupón.
devolución de llamada events_data[].uuidEl mismo ID de oferta, pasado como una cadena.
batchIdID de la versión del paquete de devolución de llamada, no el cupón o la tarifa.

currency no se devuelve en el modelo completo ni en la devolución de llamada. Si se necesita la moneda, guarde el valor de la solicitud de creación.

7. Errores de creación

CódigoRazónacción
10Sin cuerpo de solicitud.Corrija la solicitud.
11Puntero no válido.Obtenga el puntero actual de la línea.
12amount no válido.Pase un número positivo.
501El coeficiente ha cambiado.Mostrar nuevo valor o cambiar mode.
502No hay resultado.Eliminar/actualizar oferta en el carrito.
503El resultado está bloqueado.Informar indisponibilidad temporal.
504Error de verificación de resultados.No dar por aceptado el cupón; repetir más tarde.
506En apuestas express un partido.Deje un resultado o utilice sencillos separados.
507Saldo de cliente insuficiente.Recarga tu saldo o reduce el importe total.
1002Conjunto de parámetros no válido.Parámetros correctos.
10000Error interno.Registre el error y verifique el resultado antes de volver a intentarlo.

Para 501504, la nueva API devuelve las tasas problemáticas en body.changes[]. Campo change_type: 1: el coeficiente ha aumentado, 2: disminuido, null: la dirección no es aplicable.

No vuelva a intentar ciegamente POST /coupons/place después del tiempo de espera: es posible que la primera solicitud haya sido aceptada y volver a intentarlo creará un duplicado.

8. Recibir cupones

OperaciónPunto finalResultado
Un cupónGET /api/partner/coupons/get?coupon_code={code}Cupón en body.
ActivoGET /api/partner/coupons/activeMatriz en body[].
Cálculos recientesGET /api/partner/coupons/calculated?time=10Matriz en body[]; máximo 120 minutos.
Por código/períodoPOST /api/partner/coupons/resultsMatriz en body.coupons.
Saldo del clienteGET /api/partner/balancebody.balance.

Puede transferir hasta 100 valores usando códigos:

{
  "coupon_ids": ["000000000272", "000000000273"]
}

O pasar el período de creación no más de 24 horas:

{
  "start_date": 1784880000000,
  "end_date": 1784966400000
}

No combine coupon_ids y fechas en la misma consulta. results filtra por hora de creación y calculated filtra por hora de liquidación final.

9. Estados de cupones y apuestas

Estados de los cupones:

CódigoSignificadofinales
0Activo o parcialmente liquidado.No
2Ganado.si
4Perdido.si
8Totalmente reembolsado.si
15Devuelto para nuevo cálculo; esperar un nuevo resultado.No

Estados de las ofertas:

CódigoSignificadocalc_coef
0No calculado.null
1Ganando.Coeficiente original
2Perdiendo.0
3Regreso.1
4A la espera de nuevo cálculo.null
21La mitad de las ganancias.(coef + 1) / 2
22Media pérdida.0.5
23Empujar.1

Para la acumulación financiera para el usuario final, utilice el real_win ya preparado. No utilice potential_win y no vuelva a calcular el pago usted mismo. Antes del cálculo, real_win, calculate_coef, calc_coef y calculate_date son iguales a null, no a 0.

En el primer estado del cupón 15, si el resultado anterior ya ha sido procesado financieramente, el socio debita nuevamente amount del usuario final, espera un nuevo estado final y cobra un nuevo real_win. Proteger las operaciones del reprocesamiento.

10. Razón del cálculo

Cada apuesta en el modelo completo y callback tiene:

campoPropósito
settlement_reason_codeCódigo de motivo de pago/reembolso estable.
settlement_reasonTexto fuente explicativo.

Antes del cálculo, ambos campos son iguales a null. Para una notificación localizada, use código como:

  • MATCH_POSTPONED - el partido ha sido pospuesto;
  • MATCH_CANCELLED - partido cancelado;
  • MARKET_PUSH - devolución según las reglas del mercado.

Si desconoce el código, guárdelo y utilice el settlement_reason que no esté vacío como texto alternativo. No calcule el pago por motivo: utilice estados y real_win.

Estos campos explican el cálculo o la devolución que ya se ha realizado y no son una fuente separada en tiempo real del estado del partido.

11. devolución de llamada

La devolución de llamada es opcional. Un socio sólo puede trabajar mediante encuestas. Para la devolución de llamada, el administrador debe habilitar la función y crear callback_secret; La URL se envía en cada cupón creado.

El socio determina la URL de devolución de llamada de forma independiente. En producción se deberá utilizar https://; en un entorno de prueba, se permite http://.

El sistema envía:

POST {callback_url}
Content-Type: application/json
X-Coupon-Signature: sha256=<hex_hmac_sha256>

Carga útil corta:

{
  "event": "coupons.settled",
  "batchId": "d407e986f3a64d9d36a77bf532322ef8",
  "couponCount": 1,
  "coupons": [
    {
      "coupon_code": "000000000272",
      "realWin": 18.5,
      "calculate_coefficient": 1.85,
      "status": 2,
      "calculate_date": 1784973600000,
      "events_data": [
        {
          "uuid": "912",
          "status": 1,
          "calculate_coefficient": 1.85,
          "calculate_date": 1784973600000,
          "calculate_score": "2:1",
          "settlement_reason_code": "REMOTE_WIN",
          "settlement_reason": "Win",
          "timer": 0
        }
      ]
    }
  ]
}

Reglas obligatorias:

  1. calcule sha256=<hex(HMAC-SHA256(raw_body, callback_secret))> a partir de los bytes de origen exactos del cuerpo antes de analizar JSON y comparar firmas de forma segura;
  2. procese todos los artículos coupons, el paquete puede contener hasta 100 cupones;
  3. almacene batchId con un índice único;
  4. repetido batchId no debe repetir la cancelación o acumulación;
  5. un coupon_code puede venir con diferentes batchId durante el cálculo progresivo y el recálculo;
  6. devuelva HTTP 200 solo después de que todo el paquete se haya almacenado de forma segura.

La respuesta mínima exitosa es un HTTP 200 vacío. Avanzado:

{
  "success": true,
  "processed": 1
}

processed debe ser igual a couponCount. Las respuestas 201, 202 y 204 no se consideran exitosas.

Las repeticiones se realizan solo en caso de tiempo de espera, error de transporte o HTTP 500, 502, 503, 504: inmediatamente, luego después de 1, 5, 15 y 60 minutos, no más de cinco envíos. No hay reintento automático en HTTP 200 con success: false o processed parcial.

12. Encuesta de reserva

Incluso con devolución de llamada, verifique periódicamente los resultados:

GET /api/partner/coupons/calculated?time=10

Después de una pausa de más de 120 minutos, use POST /api/partner/coupons/results sobre coupon_ids guardado o períodos de creación de hasta 24 horas. Recibir nuevamente la misma fortuna no debería repetir transacciones financieras.

13. Seguridad y almacenamiento

  • almacene el inicio de sesión, la contraseña, JWT y callback_secret solo en el servidor;
  • no pase JWT en la URL y no escriba el token completo en los registros;
  • use tipo decimal para dinero;
  • almacene coupon_code como una cadena;
  • guarde amount, los datos de oferta requeridos y currency, si se utilizan;
  • distinguir entre estados de cupón y oferta;
  • aceptar campos desconocidos y códigos de motivo sin errores;
  • Haga que todas las transacciones financieras del usuario final sean idempotentes.

14. API antigua y retiro

Se siguen admitiendo rutas antiguas, pero las nuevas integraciones deben utilizar /api/partner/**. Las respuestas antiguas y los errores tienen formatos diferentes. Para actualizar un cliente existente, utilice un único archivo “Migración desde la API anterior”.

El retiro está en desarrollo, no está completamente probado y no se recomienda su producción.

15. Lista de verificación final

  • Recibido BASE_URL, nombre de usuario y contraseña.
  • JWT se transmite como un token al portador.
  • Los punteros se toman de la línea sin modificaciones.
  • El carrito se guarda como cupón aceptado solo después de code = 1.
  • Procesado multi, restricciones expresas y errores 501507.
  • Se ha tenido en cuenta la cancelación automática del saldo del cliente.
  • Se guardan todos los coupon_code y los ID de oferta.
  • El pago final se obtiene de real_win.
  • La devolución de llamada se compara con los bytes de origen y se deduplica mediante batchId.
  • Se ha configurado el sondeo de respaldo.
  • El saldo del usuario es independiente del saldo del cliente SportAPI.

La documentación detallada comienza con descripción general de API. El mapa completo de terminales se encuentra en manual.

¿Necesita más documentación o ayuda con la integración?