# Cloud Accept API (/sk/in-store/integration/cloud_accept_api)





<div className="bg-background relative h-auto w-full overflow-hidden rounded-lg border mt-8">
  <FlickeringGrid className="absolute inset-0 z-0 size-full" squareSize="4" gridGap="6" maxOpacity="0.1" flickerChance="0.1" height="800" width="2000" />

  <CloudAcceptDiagram />
</div>

Cloud Accept API umožňuje vašej elektronickej registračnej pokladnici (ECR) alebo backendu POS spustiť platbu na zariadení
Tapaya cez cloud. Váš systém nikdy nekomunikuje so zariadením priamo: odošle požiadavku na platbu do Tapaya, Tapaya
upozorní spárované zariadenie a zariadenie zobrazí zákazníkovi platobnú obrazovku. Informácie o priebehu k vám prichádzajú
ako webhooky, prípadne si ich zisťujete dopytovaním (pollingom).

## Výber spôsobu párovania zariadení [#choose-how-to-pair-devices]

Každé zariadenie, na ktoré posielate platby, potrebuje `externalDeviceId`: názov, pod ktorým ho pozná váš systém, napríklad
`till-01`. Priradiť ho môžete jedným z dvoch spôsobov:

<Cards>
  <VisualCard title="Z vašej aplikácie" visual="<Layers />" href="#assign-from-your-app" description="Vaša aplikácia priradí externalDeviceId prostredníctvom Accept SDK na zariadení." />

  <VisualCard title="QR kód z platformy" visual="<QrCode />" href="#scan-a-qr-code" description="Obchodník zobrazí QR kód v Tapaya Platform a naskenuje ho aplikáciou pre obchodníkov." />
</Cards>

## Skôr než začnete [#before-you-start]

1. **Získajte svoj Server Secret Token** na [stránke API Keys](https://platform.tapaya.com/api-keys/). Nižšie ho
   vymeníte za prístupový token obmedzený na obchodníka (pozrite [autentifikácia v Platform API](/platform-api#authentication)).
2. **Zaregistrujte obchodníka** a uchovajte si jeho `merchantToken`. Pozrite
   [Registrácia nového obchodníka](./backend_integration#register-a-new-merchant).
3. **Pripravte každé zariadenie** s aplikáciou Tapaya prihlásenou za daného obchodníka, aby ho bolo možné spárovať
   a mohlo prijímať požiadavky.

<Callout title="Jeden token, jeden obchodník" type="info">
  Každé volanie Cloud Accept API je obmedzené práve na jedného obchodníka. Ak chcete obsluhovať viacerých obchodníkov, získajte
  výmenou samostatný prístupový token pre každého z nich.
</Callout>

<section id="cloud-accept-steps">
  <div className="fd-steps [&_h3]:fd-step">
    ### Spárovanie zariadenia [#pair-a-device]

    Párovanie priradí zariadeniu Tapaya `externalDeviceId`, takže ho môžete nájsť pri výpise zariadení
    a zacieliť naň pri spustení platby.

    #### Priradenie z vašej aplikácie [#assign-from-your-app]

    Vaša aplikácia zavolá na zariadení funkciu Accept SDK, ktorá mu priradí `externalDeviceId`. Vo všetkých
    volaniach Cloud Accept API pre toto zariadenie používajte rovnaký identifikátor.

    #### Naskenovanie QR kódu [#scan-a-qr-code]

    Obchodník otvorí v Tapaya Platform postup párovania, ktorý zobrazí QR kód pre priraďované
    `externalDeviceId`. Obchodník ho naskenuje aplikáciou Tapaya pre obchodníkov na cieľovom
    zariadení a aplikácia párovanie dokončí.

    ### Výmena za prístupový token [#exchange-an-access-token]

    Vymeňte svoj Server Secret Token a `merchantToken` obchodníka za prístupový token pomocou
    `POST /ecr/auth`. `merchantToken` sa odosiela len tu; všetky ďalšie volania odosielajú iba
    `Authorization: Bearer <accessToken>`.

    <CodeBlockTabs defaultValue="curl" groupId="cloud-accept-client">
      <CodeBlockTabsList>
        <CodeBlockTabsTrigger value="curl">
          cURL
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="typescript">
          TypeScript
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="curl">
        ```bash
        curl -X 'POST' 'https://api.tapaya.com/ecr/auth' \
        -H 'Content-Type: application/json' \
        -H 'Authorization: Bearer REPLACE_ME' \
        -d '{
                "merchantToken": "unique_merchant_id_from_your_db"
            }'
        ```
      </CodeBlockTab>

      <CodeBlockTab value="typescript">
        ```typescript
        const res = await fetch('https://api.tapaya.com/ecr/auth', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                Authorization: `Bearer ${process.env.TAPAYA_SECRET_TOKEN}`,
            },
            body: JSON.stringify({ merchantToken: 'unique_merchant_id_from_your_db' }),
        });
        const { accessToken, expiresAt } = await res.json();
        ```
      </CodeBlockTab>
    </CodeBlockTabs>

    ```json title="200 OK"
    {
        "accessToken": "...",
        "tokenType": "Bearer",
        "expiresAt": "2026-01-13T16:30:59Z"
    }
    ```

    | Kód   | Popis                                                      |
    | ----- | ---------------------------------------------------------- |
    | `401` | Neplatný API token alebo API token s uplynutou platnosťou. |
    | `403` | Prístup k obchodníkovi nie je povolený.                    |

    **Obnovte token pred `expiresAt`.** Zavolajte `POST /ecr/auth/refresh` s aktuálnym, stále platným
    `accessToken` v hlavičke `Authorization`. Endpoint vráti rovnaké telo s novým tokenom; uložte si ho
    a odvtedy používajte už len ten nový.

    <CodeBlockTabs defaultValue="curl" groupId="cloud-accept-client">
      <CodeBlockTabsList>
        <CodeBlockTabsTrigger value="curl">
          cURL
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="typescript">
          TypeScript
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="curl">
        ```bash
        curl -X 'POST' 'https://api.tapaya.com/ecr/auth/refresh' \
        -H 'Authorization: Bearer ACCESS_TOKEN'
        ```
      </CodeBlockTab>

      <CodeBlockTab value="typescript">
        ```typescript
        const res = await fetch('https://api.tapaya.com/ecr/auth/refresh', {
            method: 'POST',
            headers: { Authorization: `Bearer ${accessToken}` },
        });
        const renewed = await res.json(); // { accessToken, tokenType, expiresAt }
        ```
      </CodeBlockTab>
    </CodeBlockTabs>

    | Kód   | Popis                                                                                                    |
    | ----- | -------------------------------------------------------------------------------------------------------- |
    | `401` | Prístupový token je neplatný, vypršal alebo bol zneplatnený. Získajte nový výmenou cez `POST /ecr/auth`. |
    | `403` | K obchodníkovi už nemáte prístup. Nový token sa nevydá.                                                  |

    ### Vyhľadanie pripraveného zariadenia [#find-a-ready-device]

    Zariadenia obchodníka vypíšte pomocou `GET /ecr/devices`. Pridaním `?connected=true` vynecháte
    odpojené zariadenia; jedno konkrétne zariadenie načítate pomocou `GET /ecr/devices/{externalDeviceId}`.

    <CodeBlockTabs defaultValue="curl" groupId="cloud-accept-client">
      <CodeBlockTabsList>
        <CodeBlockTabsTrigger value="curl">
          cURL
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="typescript">
          TypeScript
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="curl">
        ```bash
        curl 'https://api.tapaya.com/ecr/devices/till-01' \
        -H 'Authorization: Bearer ACCESS_TOKEN'
        ```
      </CodeBlockTab>

      <CodeBlockTab value="typescript">
        ```typescript
        const res = await fetch('https://api.tapaya.com/ecr/devices/till-01', {
            headers: { Authorization: `Bearer ${accessToken}` },
        });
        const device = await res.json();
        const canPay = device.availability === 'ready';
        ```
      </CodeBlockTab>
    </CodeBlockTabs>

    ```json title="200 OK"
    {
        "externalDeviceId": "till-01",
        "name": "Front counter",
        "connected": true,
        "availability": "ready",
        "lastSeenAt": "2026-01-13T16:30:59Z",
        "updatedAt": "2026-01-13T16:30:59Z"
    }
    ```

    Platby posielajte len na zariadenie, ktorého `availability` je `ready`. Výpis je len momentálny snímok:
    dostupnosť sa znova overuje pri odoslaní, takže aj zariadenie v stave `ready` môže požiadavku
    odmietnuť. Pozrite [Dostupnosť zariadenia](#device-availability).

    ### Odoslanie požiadavky na platbu [#submit-a-payment-request]

    Sumu odošlite na zariadenie pomocou `POST /ecr/payment-requests`. Tapaya požiadavku uloží a
    upozorní zariadenie, ktoré zobrazí platobnú obrazovku.

    <CodeBlockTabs defaultValue="curl" groupId="cloud-accept-client">
      <CodeBlockTabsList>
        <CodeBlockTabsTrigger value="curl">
          cURL
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="typescript">
          TypeScript
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="curl">
        ```bash
        curl -X 'POST' 'https://api.tapaya.com/ecr/payment-requests' \
        -H 'Content-Type: application/json' \
        -H 'Authorization: Bearer ACCESS_TOKEN' \
        -H 'Idempotency-Key: order-1042-attempt-1' \
        -d '{
                "externalDeviceId": "till-01",
                "amount": 12500,
                "tipAmount": 500,
                "currency": "CZK"
            }'
        ```
      </CodeBlockTab>

      <CodeBlockTab value="typescript">
        ```typescript
        const res = await fetch('https://api.tapaya.com/ecr/payment-requests', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                Authorization: `Bearer ${accessToken}`,
                'Idempotency-Key': 'order-1042-attempt-1',
            },
            body: JSON.stringify({
                externalDeviceId: 'till-01',
                amount: 12500, // 125.00 Kč
                tipAmount: 500, // 5.00 Kč
                currency: 'CZK',
            }),
        });
        const { requestId } = await res.json(); // 202 Accepted
        ```
      </CodeBlockTab>
    </CodeBlockTabs>

    **Parametre:**

    | Pole               | Typ       | Povinné | Popis                                                   |
    | ------------------ | --------- | ------- | ------------------------------------------------------- |
    | `externalDeviceId` | `string`  | ✓       | Spárované zariadenie, ktoré má platbu zobraziť.         |
    | `amount`           | `integer` | ✓       | Suma v najmenšej jednotke meny (`12500` je 125,00 Kč).  |
    | `tipAmount`        | `integer` |         | Prepitné v najmenšej jednotke meny.                     |
    | `currency`         | `string`  | ✓       | Kód meny podľa ISO 4217.                                |
    | `paymentToken`     | `string`  |         | Existujúca platba, ku ktorej sa má požiadavka pripojiť. |

    ```json title="202 Accepted"
    {
        "requestId": "...",
        "status": "pending",
        "paymentToken": null,
        "expiresAt": "2026-01-13T16:30:59Z"
    }
    ```

    <Callout title="202 nie je výsledok platby" type="warn">
      `202 Accepted` znamená, že Tapaya požiadavku uložila. Neznamená to, že ju zariadenie zaznamenalo,
      ani že zákazník zaplatil. Čokoľvek, čo zlyhá neskôr, sa k vám dostane ako zmena stavu.
    </Callout>

    **Zabezpečte bezpečné opakovanie požiadaviek.** Hlavička `Idempotency-Key` je povinná. Opätovné použitie kľúča s rovnakým telom
    vráti pôvodnú požiadavku namiesto spustenia druhej platby; opätovné použitie s iným telom
    vráti `409 idempotency_conflict`. Naviažte kľúč na predaj a pokus, napríklad
    `order-1042-attempt-1`.

    ### Sledovanie výsledku [#track-the-result]

    Sledujte požiadavku cez jej [stavy](#request-statuses) pomocou webhookov, alebo pravidelne volajte
    `GET /ecr/payment-requests/{requestId}`. Keď požiadavka dosiahne stav `payment_created`, odpoveď obsahuje
    `paymentToken` a priebeh samotnej platby hlásia udalosti `payment.updated`.

    Ak chcete prijímať webhooky, zaregistrujte svoj HTTPS endpoint pomocou `POST /ecr/webhook-subscriptions`, alebo
    pre obchodníka nastavte callback URL a typy udalostí v Tapaya Platform.

    <CodeBlockTabs defaultValue="curl" groupId="cloud-accept-client">
      <CodeBlockTabsList>
        <CodeBlockTabsTrigger value="curl">
          cURL
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="typescript">
          TypeScript
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="curl">
        ```bash
        curl -X 'POST' 'https://api.tapaya.com/ecr/webhook-subscriptions' \
        -H 'Content-Type: application/json' \
        -H 'Authorization: Bearer ACCESS_TOKEN' \
        -d '{
                "url": "https://integrator.example.com/webhooks/tapaya",
                "events": ["payment_request.updated", "payment.updated"]
            }'
        ```
      </CodeBlockTab>

      <CodeBlockTab value="typescript">
        ```typescript
        const res = await fetch('https://api.tapaya.com/ecr/webhook-subscriptions', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                Authorization: `Bearer ${accessToken}`,
            },
            body: JSON.stringify({
                url: 'https://integrator.example.com/webhooks/tapaya',
                events: ['payment_request.updated', 'payment.updated'],
            }),
        });
        const { subscriptionId, signingSecret } = await res.json(); // 201 Created
        ```
      </CodeBlockTab>
    </CodeBlockTabs>

    ```json title="201 Created"
    {
        "subscriptionId": "...",
        "url": "https://integrator.example.com/webhooks/tapaya",
        "events": ["payment_request.updated", "payment.updated"],
        "signingSecret": "..."
    }
    ```

    <Callout title="Uložte si signing secret" type="warn">
      `signingSecret` sa vracia len pri vytvorení odberu. Bezpečne si ho uložte; budete
      ho potrebovať na overenie podpisu prichádzajúcich webhookov.
    </Callout>

    Odber patrí integrátorovi a obchodníkovi, nie prístupovému tokenu, takže pri obnovení tokenu
    zostáva zachovaný. Na každú udalosť odpovedzte kódom `2xx`, hneď ako ju prijmete.

    <Callout title="Pred uzavretím predaja overte výsledok" type="error">
      Webhooky sú oznámenia, nie doklad o platbe, a niektorý môže doraziť neskoro alebo nemusí doraziť vôbec. Nikdy
      neuzatvárajte predaj len na základe webhooku:

      * **Keď webhook dorazí,** overte konečný výsledok cez API, napríklad pomocou
        [Načítanie podrobností platby](/platform-api/reporting#retrieve-payment-details).
      * **Keď webhook nedorazí** v časovom limite vhodnom pre váš pokladničný proces, najneskôr však
        do `expiresAt` požiadavky, zavolajte `GET /ecr/payment-requests/{requestId}`, aby ste zistili
        jej aktuálny stav, a potom overte platbu cez API, ako je uvedené vyššie.
    </Callout>
  </div>
</section>

## Referencia [#reference]

### Endpointy [#endpoints]

| Metóda   | Endpoint                                      | Popis                                                                                  |
| -------- | --------------------------------------------- | -------------------------------------------------------------------------------------- |
| `POST`   | `/ecr/auth`                                   | Vymení váš API token a `merchantToken` za prístupový token.                            |
| `POST`   | `/ecr/auth/refresh`                           | Obnoví stále platný prístupový token.                                                  |
| `GET`    | `/ecr/devices`                                | Vypíše zariadenia obchodníka. Voliteľný filter `connected=true`.                       |
| `GET`    | `/ecr/devices/{externalDeviceId}`             | Vráti jedno zariadenie a jeho aktuálnu dostupnosť.                                     |
| `POST`   | `/ecr/payment-requests`                       | Uloží požiadavku na platbu a upozorní zariadenie. Vyžaduje `Idempotency-Key`.          |
| `GET`    | `/ecr/payment-requests/{requestId}`           | Vráti stav požiadavky, prípadný dôvod odmietnutia a `paymentToken`.                    |
| `POST`   | `/ecr/webhook-subscriptions`                  | Zaregistruje HTTPS URL a typy udalostí. Signing secret (podpisový kľúč) vráti len raz. |
| `GET`    | `/ecr/webhook-subscriptions`                  | Vypíše vaše odbery pre obchodníka, bez signing secretov.                               |
| `DELETE` | `/ecr/webhook-subscriptions/{subscriptionId}` | Odstráni odber.                                                                        |

### Dostupnosť zariadenia [#device-availability]

Oba endpointy pre zariadenia vracajú `externalDeviceId`, `name`, `connected`, `availability`, `lastSeenAt` a
`updatedAt`.

| Hodnota       | Popis                                                                      |
| ------------- | -------------------------------------------------------------------------- |
| `ready`       | Pripojené a schopné prijať a zobraziť požiadavku.                          |
| `busy`        | Spracúva platbu alebo má aktívnu požiadavku.                               |
| `unavailable` | Odpojené, bez platnej relácie alebo neschopné zobraziť platobnú obrazovku. |

### Stavy požiadavky [#request-statuses]

| Stav              | Popis                                                                   |
| ----------------- | ----------------------------------------------------------------------- |
| `pending`         | Uložená a čaká, kým ju zariadenie prijme.                               |
| `acknowledged`    | Zariadenie ju dostalo; platba ešte nemusí existovať.                    |
| `payment_created` | Platba a jej `paymentToken` existujú. Ďalší priebeh sledujte na platbe. |
| `rejected`        | Zariadenie alebo Tapaya požiadavku odmietli.                            |

### Chyby [#errors]

| Kód           | Dôvod                  | Popis                                             |
| ------------- | ---------------------- | ------------------------------------------------- |
| `403` / `404` | Objekt nie je dostupný | Nemáte oprávnenie alebo objekt neexistuje.        |
| `409`         | `device_busy`          | Zariadenie už má aktívnu požiadavku alebo platbu. |
| `409`         | `device_unavailable`   | Zariadenie je pripojené, ale nie je pripravené.   |
| `409`         | `idempotency_conflict` | Kľúč už bol použitý s iným telom.                 |
| `503`         | `device_offline`       | Zariadenie nie je pripojené.                      |

Chyby validácie sumy a meny sa riadia rovnakými konvenciami ako zvyšok
[Platform API](/platform-api).

### Udalosti webhookov [#webhook-events]

| Udalosť                       | Odosiela sa, keď                                                                 | Obsah `data`                                                                                                         |
| ----------------------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `device.connected`            | Zariadenie nadviaže nové aktívne spojenie.                                       | `externalDeviceId`, `connected`, `availability`, `lastSeenAt`                                                        |
| `device.disconnected`         | Spojenie sa uzavrie alebo vyprší časový limit kontroly aktivity spojenia.        | `externalDeviceId`, `connected`, `availability`, `lastSeenAt`                                                        |
| `device.availability_changed` | Dostupnosť sa zmení medzi `ready`, `busy` a `unavailable`.                       | `externalDeviceId`, `availability`, voliteľne `reason`                                                               |
| `payment_request.updated`     | Požiadavka je uložená, potvrdená, odmietnutá, vyprší alebo k nej vznikne platba. | `requestId`, `externalDeviceId`, `status`, `paymentToken`, `reference`, `reason`, `expiresAt`                        |
| `payment.updated`             | Zmení sa stav platby.                                                            | `paymentToken`, `requestId`, `externalDeviceId`, `status`, `reference`, `amount`, `currency`, voliteľne `receiptUrl` |

Každá udalosť prichádza ako HTTP `POST` v rovnakej obálke:

```json
{
    "eventId": "...",
    "type": "payment_request.updated",
    "occurredAt": "2026-01-13T16:30:59Z",
    "data": {
        "requestId": "...",
        "externalDeviceId": "till-01",
        "status": "acknowledged",
        "paymentToken": null,
        "reference": "ORDER-1042",
        "reason": null,
        "expiresAt": "2026-01-13T16:30:59Z"
    }
}
```
