TAŇBA-OOH Әзірлеушілерге

Ойнатқыштардан және CMS жүйесінен көрсетілімдерді жіберу

Ешкім тексере алмайтын көрсетілім жай мәлімдеме ғана болып қалады. Бұл нұсқаулықта ойнатқыштың немесе оларды басқаратын CMS жүйесінің аяқталған көрсетілімдерді tanba.pop/1 пішімінде қалай хабарлайтыны көрсетілген: платформа оларды санай алады, ал кез келген адам сандарды сақталған пакеттермен салыстыра алады.

Анықтамалық беттер: Көрсетілімдерді есепке алу API және Құрылғы протоколы.

Арнаны таңдаңыз

Пакет келген арна платформаның оған қаншалықты сенетінін анықтайды. Пакет денесі деңгейді мәлімдемейді.

АрнаЖол және хостАутентификацияДәлел деңгейі
Сертификатhttps://devices.tanba-ooh.kz хостындағы POST /d/v1/telemetryКлиенттік сертификат және денеге құрылғы кілтімен жасалған қолтаңбаmtls
Қолтаңбаhttps://agents.tanba-ooh.kz хостындағы POST /s/v1/telemetryТіркеу кезінде жазылған кілтпен жасалған дене қолтаңбасы; денеде device_id және ts барsigned
CMS (оператор растаған)https://app.tanba-ooh.kz хостындағы POST /api/v1/pop/batcheswrite:plays рұқсаты бар API кілті және CMS орнатылымы үшін жазылған кілтпен жасалған қолтаңбаattested

Қай деңгейлердің алымға есептелетінін осы интерфейс емес, платформа белгілейді. Оператор растаған көрсетілімдер оператордың өз мәлімдемесі болып табылады: олар операторды ешқашан бұзушылықтан арылтпайды және құрылғылар хабарлайтынның жанында айқас тексеру қызметін атқарады.

Пакет

{"schema": "tanba.pop/1", "batch_seq": 1042, "prev_sha256": "9f2b6a41c7d83e05a1b4c6d7e8f90123456789abcdef0123456789abcdef0123",
 "sent_at": "2026-10-14T09:05:00.120Z", "events": [ ]}

Жазбалар

Әр аяқталған көрсетілімге бір play жазбасы:

ӨрісТүріЕреже
kind"play"Басқа түрлері: reject (code өрісімен, мәні NO_SIGNATURE, BAD_SIGNATURE, HASH_MISMATCH, EXPIRED_APPROVAL, REVOKED біреуі), mode_change және tamper; оларда at бар
seqбүтін санplay үшін міндетті. Әр ойнатқыш үшін пакеттер шекарасынан өтіп, әр жазбаға +1, 1-ден бастап
play_idжол, A-Za-z0-9_- таңбаларынан 8-ден 64-ке дейінОйнатқыш үшін бірегей
approval_numberTNB-YYYY-NNNNNNslot берілмесе міндетті
slotgov немесе emergencyУәкілетті орган материалы: келісу нөмірі жоқ, ешқашан есептелмейді, бөлек саналады
media_sha25664 hex таңбаКөрсетілген файлдың SHA-256 мәні
start, endRFC 3339, миллисекундтарstart end мәнінен кеш емес
duration_ms0 немесе одан көп бүтін санЭкранда өлшенген уақыт
structureқұрылым кодыМіндетті емес; ойнатқыштың өз құрылымы болуы тиіс, әйтпесе жазба белгі алады
screen{"power": ...}Міндетті емес; көрсетілім кезіндегі дисплей күйі: on, off немесе unknown

Көрсетілімдер қалай есептеледі

Реттілік, қайталаулар және тізбек

Жауап

{"batch_seq": 1042, "received": 12, "accepted": 10, "duplicates": 0, "refused": 2, "flagged": 1, "duplicate": false,
 "items": [{"index": 1, "status": "flagged", "flags": ["incomplete"]}, {"index": 10, "status": "refused", "reason": "time_in_future"},
           {"index": 11, "status": "refused", "reason": "end_before_start"}]}

items тізімінде тек сөзсіз қабылданбаған жазбалар аталады. index 0-ден санала бастайды. Таза пакетте тізім бос.

CMS жүйесінен жіберу

Ойнатқыштары CMS астында жұмыс істейтін оператор олардың көрсетілімдерін CMS жүйесінен бір қол қойылған пакетпен көптеген ойнатқыш үшін хабарлай алады.

Консольде бір реттік баптау

Мұны тек оператор ұйымының әкімшісі «Интеграциялар» бөлімінде жасай алады. Әр қадам аудит журналына жазылады.

  1. CMS орнатылымын тіркеңіз: атауы, түрі және ашық кілті, яғни DER SubjectPublicKeyInfo құрылымының base64 түріндегі ECDSA P-256 кілті. Жабық кілт CMS жүйеңізден ешқашан шықпайды. Консоль әр пакетке кіретін көз идентификаторын көрсетеді.
  2. Құрылымдарыңыздың біріне қызмет ететін CMS-тің әр ойнатқышын байланыстырыңыз: CMS оны қалай атайтыны (жазбалардағы player, 64 таңбаға дейін) және құрылым. Сыртқы идентификатор орнатылымға бір рет қолданылады. Байланыспаған ойнатқыштың көрсетілімдері player_unknown деп қабылданбайды.
  3. write:plays рұқсаты бар API кілтін жасаңыз. Аутентификация және API кілттері бөлімін қараңыз.
  4. Кілтті ауыстыру үшін орнатылымға жаңа ашық кілтті тіркеңіз. Жаңа кілт бірден қолданылады; ескісі тағы 24 сағат жарамды болып қалады, содан кейін 401 source_unauthorized жауабымен қабылданбайды.
  5. Кілт ағып кетсе, оны бірден аяқтаңыз: POST /api/v1/pop-sources/{id}/keys/{fingerprint}/revoke (оператор әкімшісі). Жаңа кілт тіркелгенше көз пакеттерді қабылдамайды. Әрбір пакеттің денесінде X-Tanba-Source тақырыбымен бірдей source_id болуы тиіс.

Пакетке қол қою

Қолтаңба JSON денесінің нақты байттары үшін ES256, gzip жіберсеңіз, ашқаннан кейінгі байттар үшін. Ол шикі түрде жазылады: r, содан кейін s, әрқайсысы 32 байт, толтырусыз base64url. Төмендегі көмекшіні pop_client.py деп сақтаңыз. Оған Python 3 және cryptography пакеті қажет.

import base64, gzip, hashlib, json, sys, urllib.request
from datetime import datetime, timezone
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import ec, utils


def new_key(path):
    """Run once. Keep the private key; register the printed public key in the console."""
    key = ec.generate_private_key(ec.SECP256R1())
    with open(path, "wb") as f:
        f.write(key.private_bytes(serialization.Encoding.PEM, serialization.PrivateFormat.PKCS8, serialization.NoEncryption()))
    der = key.public_key().public_bytes(serialization.Encoding.DER, serialization.PublicFormat.SubjectPublicKeyInfo)
    print("public key:", base64.b64encode(der).decode())


def load_key(path):
    with open(path, "rb") as f:
        return serialization.load_pem_private_key(f.read(), password=None)


def sign(key, body: bytes) -> str:
    r, s = utils.decode_dss_signature(key.sign(body, ec.ECDSA(hashes.SHA256())))
    return base64.urlsafe_b64encode(r.to_bytes(32, "big") + s.to_bytes(32, "big")).rstrip(b"=").decode()


def send(base, api_key, source_id, key, batch_seq, prev_sha256, records):
    body = json.dumps({"schema": "tanba.pop/1", "source_id": source_id, "batch_seq": batch_seq, "prev_sha256": prev_sha256,
                       "sent_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.000Z"), "records": records},
                      separators=(",", ":")).encode()
    req = urllib.request.Request(base + "/api/v1/pop/batches", data=gzip.compress(body), method="POST", headers={
        "Authorization": "Bearer " + api_key, "X-Tanba-Source": source_id, "X-Tanba-Signature": sign(key, body),
        "Content-Type": "application/json", "Content-Encoding": "gzip"})
    with urllib.request.urlopen(req, timeout=60) as resp:
        answer = json.load(resp)
    return answer, hashlib.sha256(body).hexdigest()  # the hash is the next batch's prev_sha256


if __name__ == "__main__":
    if sys.argv[1] == "keygen":      # python3 pop_client.py keygen cms-key.pem
        new_key(sys.argv[2])
    elif sys.argv[1] == "sign":      # python3 pop_client.py sign cms-key.pem batch.json
        print(sign(load_key(sys.argv[2]), open(sys.argv[3], "rb").read()))

Кілтті бір рет жасап, басылған ашық кілтті консольде тіркеңіз:

python3 pop_client.py keygen cms-key.pem

curl арқылы жіберу

Пакетті batch.json файлына жазыңыз, файлға қол қойыңыз және сол байттарды жіберіңіз. --data-binary пайдаланыңыз: -d жол үзілімдерін алып тастар еді, қолтаңба сәйкес келмей қалар еді.

export POP_API_KEY='tnbk_EXAMPLE_secret'
export POP_SOURCE='3f6c1d2a-8b47-4e19-a5d0-7c2e9b14f836'
cat > batch.json <<'EOF'
{"schema":"tanba.pop/1","source_id":"3f6c1d2a-8b47-4e19-a5d0-7c2e9b14f836","batch_seq":1042,"prev_sha256":"","sent_at":"2026-10-14T09:05:00.120Z","records":[{"kind":"play","player":"1001:55:2","seq":18,"play_id":"7c1e9a40b2d35f68","approval_number":"TNB-2026-000123","media_sha256":"9f2b6a41c7d83e05a1b4c6d7e8f90123456789abcdef0123456789abcdef0123","start":"2026-10-14T09:04:50.000Z","end":"2026-10-14T09:05:00.000Z","duration_ms":10000}]}
EOF
curl -X POST 'https://app.tanba-ooh.kz/api/v1/pop/batches' \
  -H "Authorization: Bearer $POP_API_KEY" \
  -H "X-Tanba-Source: $POP_SOURCE" \
  -H "X-Tanba-Signature: $(python3 pop_client.py sign cms-key.pem batch.json)" \
  -H 'Content-Type: application/json' \
  --data-binary @batch.json

Python арқылы жіберу

from pop_client import load_key, send

key = load_key("cms-key.pem")
records = [{"kind": "play", "player": "1001:55:2", "seq": 18, "play_id": "7c1e9a40b2d35f68",
            "approval_number": "TNB-2026-000123",
            "media_sha256": "9f2b6a41c7d83e05a1b4c6d7e8f90123456789abcdef0123456789abcdef0123",
            "start": "2026-10-14T09:04:50.000Z", "end": "2026-10-14T09:05:00.000Z", "duration_ms": 10000}]
answer, body_hash = send("https://app.tanba-ooh.kz", "tnbk_EXAMPLE_secret", "3f6c1d2a-8b47-4e19-a5d0-7c2e9b14f836",
                         key, batch_seq=1042, prev_sha256="", records=records)
print(answer)

body_hash мәнін келесі пакеттің prev_sha256 мәні ретінде сақтаңыз. Ойнатқыштардың seq есептегіштерін, batch_seq мәнін және дене хэшін өз қоймаңызда сақтаңыз, сонда қайта іске қосылғаннан кейін нөмірлеу қайталанбай, жалғаса береді.

Өз жүктемеңізді жазбаларға сәйкестендіру

Шектеулер

Пакеттегі жазбалар10 000
Денежолда 4 МБ; ашқаннан кейін 16 МБ және қысылған көлемнен 20 еседен артық емес
Пакеттерорнатылымға секундына 2 (жылдам жіберу 10)
ШақыруларAPI кілтіне секундына 10 (жылдам жіберу 30)
Жазбаларұйымға сағатына 2 000 000

Шектеуден асқан сұрау Retry-After тақырыбымен 429 rate_limited жауабын алады, ал өлшем шектеулері үшін 413.

Ойнатқыштан жіберу

Ойнатқыш консольдегі токен арқылы бір рет тіркеледі, содан кейін өзі хабарлайды. Сертификат арнасында ол клиенттік сертификатын көрсетіп, денеге құрылғы кілтімен қол қояды; қолтаңбалы арнада денеге тіркеу кезінде жазылған кілтпен қол қояды, ал денеде device_id және ts болады, ts платформа сағатынан 300 секундтан алыс болмауы тиіс (екі арна да құрылғыға 10 секундта бір пакет қабылдайды, үзілістен кейін қатарынан 3-ке дейін). Қолтаңба жоғарыдағыдай жасалады. Шақырулар Құрылғы протоколы бетінде тізілген.

Қателер

Қателер тұрақты code өрісі бар problem құжаттары түрінде келеді.

СтатусcodeҚашан
400invalid_json, schema_unknownДене оқылмайды, schema белгісіз
400 / 401bad_signatureҚолтаңба расталмайды (сертификат арнасында 400, мұнда дене дәлел ретінде бөлек сақталады; қалғандарында 401, ештеңе сақталмайды)
401device_unauthorized, device_cert_required, stale_requestТұлғаны растайтын деректер белгісіз, кері қайтарылған немесе мерзімі өткен; қолтаңбалы арнада ts ауқымнан тыс
401source_unauthorizedCMS арнасы: көз белгісіз немесе өшірілген, X-Tanba-Source тақырыбы жоқ немесе кілт 24 сағаттан бұрын ауыстырылған
403forbidden, key_scope_deniedCMS арнасы: көз басқа ұйымға тиесілі, шақырушы API кілті емес немесе кілтте write:plays жоқ
409batch_conflictbatch_seq басқа мазмұнды пакетпен алынған
409legacy_after_poptanba.pop/1 жіберіп қойған құрылғыдан бұрынғы пішімдегі пакет; оны қайталамаңыз
413body_too_large, batch_too_largeӨлшем шектеулерінен асты
415unsupported_media_typeCMS арнасы: JSON емес немесе gzip-тен басқа кодтау
422validation_failedҚабық өрістері: batch_seq, prev_sha256 пішімі, бос events, жоқ немесе сәйкес келмейтін source_id
429rate_limitedЖиілік шектеулерінен асты

Сақталғанды тексеріңіз

Әрбір қабылданған пакет алынған нақты байттармен, қолтаңба тақырыбымен, дене SHA-256 мәнімен және оған қол қойған тұлғаны растайтын деректермен бірге сақталады. Ашық кілті бар кез келген адам сақталған пакетті алып, хэшін есептеп, қолтаңбасын тексеріп, платформа сандарына сенбей-ақ prev_sha256 бойынша тізбекті және seq бойынша жазба нөмірлерін аралап шыға алады. Платформа қабылданған статистиканы күн сайын қалай мөрлейтіні және мөрді офлайн қалай тексеру керек екені Тізімдемені оқу және оның дәлелдерін тексеру нұсқаулығында. Тексеру құралы Жүктеулер бетінде.