# Hosted Checkout (/sk/online/integration/hosted_checkout)





Pošlite zákazníka na platobnú stránku hostovanú spoločnosťou Tapaya pre karty, Apple Pay a Google Pay a potom overte výsledok na
svojom serveri. Vaša stránka nikdy nepracuje s údajmi o karte a do svojho obchodu nepridávate žiadne polia pre kartu.

<CheckoutFlowDiagram />

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

Zvoľte kľúč podľa toho, kto prevádzkuje váš backend. Nech zvolíte ktorýkoľvek, uchovávajte ho na svojom serveri; pravidlá na strane servera uvádza stránka [Zabezpečenie
Checkoutu](/online/security/checkout_security).

<Cards>
  <VisualCard title="Jeden obchod" visual="<Store />" href="/online/integration/checkout_settings#create-merchant-checkout-keys" description="Použite checkout kľúč obchodníka. Určuje svojho obchodníka a používajú ho aj tunajšie príklady." />

  <VisualCard title="Mnoho obchodníkov" visual="<Building2 />" href="/online/integration/api_integration#authentication" description="Použite Secret key organizácie a s každou požiadavkou posielajte token obchodníka." />
</Cards>

Potom v časti **Checkout** v [Tapaya Platform](https://platform.tapaya.com/checkout-settings):

1. **Pridajte svojho obchodníka** a overte, že sa zobrazuje ako **Ready**. Pozrite si [Nastavenia Checkoutu](/online/integration/checkout_settings).
2. **Zaregistrujte svoj obchod** ako povolený web s predvolenými URL úspechu a zrušenia, a to pre kľúč, ktorý ste zvolili.
   Príklady používajú `https://shop.example`.

<Callout title="Návratové URL sa riadia kľúčom" type="info">
  Relácie vytvorené kľúčom obchodníka používajú návratové URL obchodníka. Relácie vytvorené pomocou Secret key organizácie
  namiesto toho používajú [návratové ciele](/online/integration/checkout_settings#configure-return-destinations) organizácie.
  Tieto dve nastavenia sa nikdy nekombinujú.
</Callout>

## Prijatie platby [#accept-a-payment]

Všetky tri možnosti vytvárajú rovnaké relácie Checkoutu s rovnakými prihlasovacími údajmi. Zvoľte jednu; každý nasledujúci krok
sa riadi vašou voľbou.

<div className="fd-steps [&_h3]:fd-step">
  ### Nastavte svoj server [#set-up-your-server]

  <Tabs defaultValue="fetch" groupId="checkout-server" className="border-0 bg-transparent">
    <TabsList>
      <TabsTrigger value="fetch">
        HTTP (fetch)
      </TabsTrigger>

      <TabsTrigger value="node">
        Node.js SDK
      </TabsTrigger>

      <TabsTrigger value="curl">
        cURL
      </TabsTrigger>
    </TabsList>

    <TabsContent value="fetch">
      Nastavte `TAPAYA_CHECKOUT_API_URL` na `https://api.sandbox.tapaya.com` a svoj kľúč uložte do `TAPAYA_CHECKOUT_API_KEY`.
      API volajte pomocou `fetch` v serverovom behovom prostredí, nikdy z prehliadača zákazníka. Príklady čítajú tieto hodnoty
      cez `process.env`; ak vaše behové prostredie používa iné rozhranie pre tajné údaje, toto načítanie upravte.

      Vytvorte pomocnú funkciu, ktorá pred vrátením tela odpovede skontroluje chyby HTTP:

      ```js title="server/checkout.js"
      const apiUrl = process.env.TAPAYA_CHECKOUT_API_URL;
      const apiKey = process.env.TAPAYA_CHECKOUT_API_KEY;

      async function checkoutRequest(path, options = {}) {
      	const response = await fetch(new URL(path, apiUrl), {
      		...options,
      		headers: {
      			...options.headers,
      			Authorization: `Bearer ${apiKey}`,
      		},
      		redirect: 'error',
      		cache: 'no-store',
      		signal: AbortSignal.timeout(8000),
      	});

      	if (!response.ok) {
      		throw new Error(`Checkout request failed (${response.status})`);
      	}

      	return response.json();
      }
      ```
    </TabsContent>

    <TabsContent value="node">
      Použite Node.js 22.13.0 alebo novší a nainštalujte na svoj server balík ESM:

      <CodeBlockTabs defaultValue="npm" groupId="package-manager">
        <CodeBlockTabsList>
          <CodeBlockTabsTrigger value="npm">
            npm
          </CodeBlockTabsTrigger>

          <CodeBlockTabsTrigger value="pnpm">
            pnpm
          </CodeBlockTabsTrigger>

          <CodeBlockTabsTrigger value="bun">
            Bun
          </CodeBlockTabsTrigger>
        </CodeBlockTabsList>

        <CodeBlockTab value="npm">
          ```sh
          npm install @tapayadot/checkout
          ```
        </CodeBlockTab>

        <CodeBlockTab value="pnpm">
          ```sh
          pnpm add @tapayadot/checkout
          ```
        </CodeBlockTab>

        <CodeBlockTab value="bun">
          ```sh
          bun add @tapayadot/checkout
          ```
        </CodeBlockTab>
      </CodeBlockTabs>

      Svoj kľúč uložte do `TAPAYA_SECRET_KEY` a zvoľte prostredie sandbox. SDK samo súbory `.env` nenačítava,
      preto tieto hodnoty načítajte do prostredia svojho servera ešte pred vytvorením klienta.

      ```sh title=".env"
      TAPAYA_SECRET_KEY=tapaya_test_...
      TAPAYA_ENVIRONMENT=sandbox
      ```

      ```ts title="server/checkout.ts"
      import { Tapaya } from '@tapayadot/checkout';

      const tapaya = new Tapaya(); // reads TAPAYA_SECRET_KEY
      ```

      Bez `TAPAYA_ENVIRONMENT` kľúč `tapaya_live_` zvolí produkčné prostredie a ostatné kľúče predvolene použijú sandbox. V
      režime platformy odovzdajte token obchodníka: `new Tapaya(key, { merchantToken })`, alebo `merchantToken` pri každom volaní. Ak
      je váš kľúč uložený pod iným názvom, odovzdajte ho ako prvý argument. [Referenčná príručka Node.js
      SDK](/online/integration/node_sdk) opisuje vlastné URL API, časové limity, opakované pokusy a chyby.
    </TabsContent>

    <TabsContent value="curl">
      Tieto príkazy spúšťajte zo svojho servera alebo z dôveryhodného terminálu. Načítajte svoj kľúč do `TAPAYA_CHECKOUT_API_KEY`, nedávajte ho
      do kódu v prehliadači ani do systému na správu verzií a nasmerujte URL API na sandbox:

      ```bash
      export TAPAYA_CHECKOUT_API_URL='https://api.sandbox.tapaya.com'
      ```
    </TabsContent>
  </Tabs>

  ### Vytvorte reláciu [#create-a-session]

  Vypočítajte celkovú sumu objednávky na svojom serveri vrátane dane a dopravy. Objednávku a jedinečný
  `checkoutAttemptKey` uložte **pred** odoslaním požiadavky a `order` načítajte z tejto uloženej snímky. Pole `merchantOrderId`
  je povinné.

  <Tabs defaultValue="fetch" groupId="checkout-server" className="border-0 bg-transparent">
    <TabsList>
      <TabsTrigger value="fetch">
        HTTP (fetch)
      </TabsTrigger>

      <TabsTrigger value="node">
        Node.js SDK
      </TabsTrigger>

      <TabsTrigger value="curl">
        cURL
      </TabsTrigger>
    </TabsList>

    <TabsContent value="fetch">
      ```js
      const session = await checkoutRequest('/merchant/checkout-sessions', {
      	method: 'POST',
      	headers: {
      		'Content-Type': 'application/json',
      		'Idempotency-Key': order.checkoutAttemptKey,
      	},
      	body: JSON.stringify({
      		merchantOrderId: order.id,
      		amount: order.totalInMinorUnits,
      		currency: order.currency,
      		successUrl: 'https://shop.example/checkout/return',
      		cancelUrl: 'https://shop.example/checkout/cancel',
      	}),
      });
      ```
    </TabsContent>

    <TabsContent value="node">
      ```ts
      const session = await tapaya.checkout.sessions.create(
      	{
      		merchantOrderId: order.id,
      		amount: order.totalInMinorUnits,
      		currency: order.currency,
      		successUrl: 'https://shop.example/checkout/return',
      		cancelUrl: 'https://shop.example/checkout/cancel',
      	},
      	{ idempotencyKey: order.checkoutAttemptKey },
      );
      ```
    </TabsContent>

    <TabsContent value="curl">
      Príklad účtuje 121,00 EUR za `order-1001` vrátane 21,00 EUR DPH a 6,05 EUR za dopravu, s
      uloženým idempotency key `order-1001-checkout-1`. Nahraďte tieto hodnoty údajmi svojej uloženej objednávky.

      ```bash
      curl --request POST \
      	"$TAPAYA_CHECKOUT_API_URL/merchant/checkout-sessions" \
      	--fail-with-body --silent --show-error --max-time 8 \
      	--header "Authorization: Bearer $TAPAYA_CHECKOUT_API_KEY" \
      	--header 'Content-Type: application/json' \
      	--header 'Idempotency-Key: order-1001-checkout-1' \
      	--data '{
      		"merchantOrderId": "order-1001",
      		"amount": 12100,
      		"currency": "EUR",
      		"tax": 2100,
      		"shipping": 605,
      		"taxRate": 21,
      		"successUrl": "https://shop.example/checkout/return",
      		"cancelUrl": "https://shop.example/checkout/cancel"
      	}'
      ```
    </TabsContent>
  </Tabs>

  Odpoveď obsahuje `id` relácie a hostovanú `url`. Súhrny položiek, rozpisy daní, údaje o zákazníkovi
  a všetky polia odpovede opisuje časť [Vytvorenie relácie Checkoutu](/online/integration/api_integration#create-a-checkout-session).

  **Ak požiadavka zlyhá alebo vyprší jej časový limit,** zopakujte ju s rovnakým kľúčom a nezmeneným telom. Pri neistom výsledku nikdy
  negenerujte nový kľúč: zmenené telo s rovnakým kľúčom vráti HTTP 409.

  | Možnosť            | Automatické opakovanie                                                                                                                                                                                                                                                                                                                                                 |
  | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  | HTTP (fetch), cURL | Nie. Požiadavku zopakujte sami. Pozrite si [Idempotencia a chyby](/online/integration/api_integration#idempotency-and-errors).                                                                                                                                                                                                                                         |
  | Node.js SDK        | Až dvakrát po chybe pripojenia, vypršaní časového limitu alebo HTTP 429, 502, 503 či 504, s rovnakým kľúčom a telom. Pozrite si [Idempotencia a opakovanie požiadaviek](/online/integration/node_sdk#idempotency-and-retries). Medzi samostatnými volaniami alebo po reštartoch odovzdávajte svoj uložený kľúč: ak ho vynecháte, pre každé volanie sa vygeneruje nový. |

  ### Presmerujte zákazníka [#redirect-the-customer]

  Uložte `session.id` k objednávke ako jej `checkoutSessionId` a potom pošlite prehliadač na `session.url` pomocou HTTP
  303\. URL použite bez zmeny, nezostavujte ju sami.

  ```ts
  // First persist session.id as the order's checkoutSessionId.
  return Response.redirect(session.url, 303);
  ```

  Ak váš framework používa iné API pre odpovede, pošlite HTTP 303 so `session.url` v hlavičke `Location`. S
  cURL otvorte vrátenú `url` v prehliadači a platbu otestujte ručne.

  ### Overte platbu [#verify-the-payment]

  V handleri návratovej URL dohľadajte objednávku z autentifikovaného kontextu pokladne vo vašej aplikácii a načítajte jej
  uloženú reláciu zo svojho servera. Urobte to aj na URL zrušenia: návšteva ktorejkoľvek z oboch URL nič nedokazuje.

  <Tabs defaultValue="fetch" groupId="checkout-server" className="border-0 bg-transparent">
    <TabsList>
      <TabsTrigger value="fetch">
        HTTP (fetch)
      </TabsTrigger>

      <TabsTrigger value="node">
        Node.js SDK
      </TabsTrigger>

      <TabsTrigger value="curl">
        cURL
      </TabsTrigger>
    </TabsList>

    <TabsContent value="fetch">
      ```js
      const current = await checkoutRequest(
      	`/merchant/checkout-sessions/${encodeURIComponent(order.checkoutSessionId)}`,
      );

      if (
      	current.id !== order.checkoutSessionId ||
      	current.merchantOrderId !== order.id ||
      	current.amount !== order.totalInMinorUnits ||
      	current.currency !== order.currency
      ) {
      	throw new Error('Checkout session does not match the order');
      }

      const paid = current.paymentStatus === 'successful';
      ```
    </TabsContent>

    <TabsContent value="node">
      ```ts
      const current = await tapaya.checkout.sessions.get(order.checkoutSessionId);

      if (
      	current.id !== order.checkoutSessionId ||
      	current.merchantOrderId !== order.id ||
      	current.amount !== order.totalInMinorUnits ||
      	current.currency !== order.currency
      ) {
      	throw new Error('Checkout session does not match the order');
      }

      const paid = current.paymentStatus === 'successful';
      ```
    </TabsContent>

    <TabsContent value="curl">
      ID relácie uložené s vašou objednávkou načítajte do `CHECKOUT_SESSION_ID`:

      ```bash
      curl "$TAPAYA_CHECKOUT_API_URL/merchant/checkout-sessions/$CHECKOUT_SESSION_ID" \
      	--fail-with-body --silent --show-error --max-time 8 \
      	--header "Authorization: Bearer $TAPAYA_CHECKOUT_API_KEY"
      ```

      Pri ukážkovej objednávke overte, že `id` zodpovedá uloženému ID relácie, `merchantOrderId` je `order-1001`, `amount`
      je `12100`, `currency` je `EUR` a `paymentStatus` je `successful`. Samotná odpoveď HTTP 200 platbu nedokazuje.
    </TabsContent>
  </Tabs>

  <Callout title="Objednávku vybavte iba raz a iba po zaplatení" type="warn">
    Platbu zaznamenajte a objednávku vybavte iba vtedy, keď relácia zodpovedá vašej uloženej objednávke a `paymentStatus` je
    `successful`, a zaznamenajte ju atomicky, aby k tomu došlo iba raz. Ak načítanie zlyhá alebo výsledok platby ešte nie je rozhodnutý, ponechajte si
    ID relácie a stav overte znova. Pozrite si [Stav platby](/online/integration/api_integration#payment-status).
  </Callout>
</div>

## Spracovanie výsledkov platby [#handle-payment-outcomes]

Stránka [Zabezpečenie Checkoutu](/online/security/checkout_security#verify-before-you-fulfill) opisuje, čo overiť a ako
zopakovať požiadavku s neznámym výsledkom bez toho, aby ste vytvorili druhú reláciu.

| Výsledok                                | Čo relácia ukazuje                                  | Čo urobiť                                                                                                                                                                                               |
| --------------------------------------- | --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Zamietnutie**                         | `open` s `paymentStatus: failed`                    | Nič. Zákazník môže na tej istej stránke skúsiť inú kartu alebo peňaženku.                                                                                                                               |
| **Zatiaľ neznámy výsledok**             | `open` a `unpaid`, kým Tapaya potvrdzuje výsledok   | Načítajte reláciu neskôr znova.                                                                                                                                                                         |
| **Nová relácia pre tú istú objednávku** | Predchádzajúca nezaplatená relácia hlási `expired`  | Použite novú reláciu. Zaplatená objednávka alebo objednávka s prebiehajúcou platbou vráti HTTP 409. Pozrite si [Jedna platba na objednávku](/online/integration/api_integration#one-payment-per-order). |
| **Vypršanie platnosti**                 | Prestane prijímať nové platby 30 minút po vytvorení | Ak objednávka stále nie je zaplatená, vytvorte novú reláciu. Platba odoslaná pred vypršaním sa aj tak dokončí.                                                                                          |

<Callout title="Webhooky zatiaľ nie sú k dispozícii" type="info">
  Webhooky pre Checkout zatiaľ neexistujú. Spúšťajte na strane servera úlohu na odsúhlasenie, ktorá načítava relácie objednávok, ktorých
  zákazníci sa nikdy nevrátia.
</Callout>

## Kontrola integrácie [#check-the-integration]

<Cards>
  <Card title="Testovacie karty" href="/online/getting-started/testing" description="Vyskúšajte v sandboxe všetky výsledky platby." />

  <Card title="Kontrolný zoznam QA" href="/online/getting-started/checklist" description="Pred spustením ostrej prevádzky pokryte opakované pokusy, prerušené platby a vypršanie platnosti." />
</Cards>
