Tapaya
Online platbyPrůvodce integracíEmbedded Checkout

Embedded Checkout

Přidejte do své vlastní pokladny zabezpečená pole pro kartu a Apple Pay nebo Google Pay.

Vložte zabezpečená pole pro kartu přímo do své pokladny. Formulář i odesílací tlačítko máte ve své režii; Tapaya zajišťuje validaci karty, tokenizaci a 3-D Secure. Vložené platby jsou dostupné v ostrém provozu pro schválené obchodníky a pro testování je k dispozici sandbox. Tento průvodce používá Tapaya.js 0.2.0 a Node.js SDK 0.3.0.

Your frontend
Your backend
Tapaya
  1. 1. Your frontend to Your backend: Prepare payment.
  2. 2. Your backend to Tapaya: Create or resume payment.
  3. 3. Tapaya to Your backend: Payment and client secret.
  4. 4. Your backend to Your frontend: Client secret and status.
  5. 5. Your frontend to Tapaya: Confirm card and 3-D Secure.
  6. 6. Tapaya to Your frontend: Payment outcome.
  7. 7. Your backend to Tapaya: Read order's payment.
  8. 8. Tapaya to Your backend: Payment status.
  9. 9. Your backend to Your frontend: Show verified result.

Platební metody

Embedded Checkout podporuje karty, Apple Pay a Google Pay. Pro karty použijte vlastní tlačítko Zaplatit a pro Apple Pay a Google Pay oficiální tlačítka peněženek. Viz Přidání Apple Pay a Google Pay.

Než začnete

Instalace SDK

Jeden balíček obsahuje serverového klienta, SDK pro prohlížeč i React hook:

npm install @tapayadot/checkout
ImportKde
@tapayadot/checkoutPouze na vašem serveru. Import v sestavení pro prohlížeč vyhodí chybu, takže se váš API klíč nikdy nedostane na stránku.
@tapayadot/checkout/clientVaše stránka pokladny.
@tapayadot/checkout/reactVaše stránka pokladny, pokud používáte React.

Konfigurace serveru

Vložené platby nepotřebují návratové URL. Serverové příklady níže jsou ve verzích HTTP (fetch), Node.js SDK a cURL. Všechny volají stejné API obchodníka a pole pro kartu v prohlížeči nechávají beze změny.

Node.js SDKHTTP (fetch) a cURL
KlíčTAPAYA_SECRET_KEY, který načítá new Tapaya()TAPAYA_CHECKOUT_API_KEY
ProstředíTAPAYA_ENVIRONMENT=sandbox nebo production. Bez této proměnné klíč tapaya_live_ vybere produkční prostředí a u ostatních klíčů se ve výchozím stavu použije sandbox.TAPAYA_CHECKOUT_API_URL: https://api.sandbox.tapaya.com nebo https://api.tapaya.com
Režim platformyPředávejte merchantToken objednávky v každém volání nebo ho nastavte pomocí new Tapaya(key, { merchantToken })Posílejte X-Tapaya-Merchant-Token v každém požadavku. Pomocná funkce pro fetch ho načítá z TAPAYA_MERCHANT_TOKEN.

Checkout klíč obchodníka nic dalšího nepotřebuje. V režimu platformy použijte Secret key organizace a backend obsluhující více obchodníků nechte vybírat token podle autentizované objednávky. Viz Autentizace Checkoutu. Client secrety jsou specifické pro každou platbu.

Dokončete onboarding s online platbami a ověřte, že je obchodník v nastavení Checkoutu ve stavu Ready.

Povolení Tapaya ve vaší CSP

Loader stahuje Tapaya.js 0.2.0 z https://js.tapaya.com/checkout/0.2.0/checkout.js; verze prohlížečového skriptu je nezávislá na verzi Node.js SDK. Přidejte origin assetů Tapaya do své stávající Content Security Policy:

script-src https://js.tapaya.com
frame-src https://js.tapaya.com

Prohlížeč dostane publishable key a client secret specifický pro danou platbu. Váš tajný API klíč obchodníka zůstává na vašem serveru a údaje o kartě i skripty poskytovatele plateb zůstávají uvnitř rámce Tapaya. Pro lokální testování hostujte Tapaya.js sami a jeho origin předejte loaderu, například loadTapaya(publishableKey, { apiOrigin, origin: 'http://localhost:4191' }).

Jak to funguje

  1. Vaše stránka připojí pole pro kartu od Tapaya a nasměruje je na endpoint na vašem serveru.
  2. Při načtení stránky a při platbě zákazníka volají pole pro kartu tento endpoint. Ten vrátí platbu objednávky jako clientSecret: přihlašovací údaj pro jednu platbu, pomocí kterého pole pro kartu potvrdí platbu přímo u Tapaya. Kód vaší stránky ho nikdy nečte.
  3. Vaše stránka sleduje outcome platby. Když se platba dokončí, váš server ji ověří a teprve potom objednávku vyřídíte.

Přijetí platby

Získání nastavení pro prohlížeč

Načtěte nastavení pro prohlížeč jednou na serveru a přidejte je do konfigurace svého frontendu.

Tuto pomocnou funkci mějte na serveru. Kontroluje chyby HTTP, na požádání považuje chybějící platbu objednávky za null a požadavky automaticky neopakuje.

server/tapaya.js
const apiOrigin = new URL(process.env.TAPAYA_CHECKOUT_API_URL).origin;
const apiKey = process.env.TAPAYA_CHECKOUT_API_KEY;
const merchantToken = process.env.TAPAYA_MERCHANT_TOKEN;

export async function paymentRequest(path, { method = 'GET', body, key, allowMissing = false } = {}) {
	const response = await fetch(new URL(path, apiOrigin), {
		method,
		headers: {
			Authorization: `Bearer ${apiKey}`,
			...(merchantToken ? { 'X-Tapaya-Merchant-Token': merchantToken } : {}),
			...(body === undefined ? {} : { 'Content-Type': 'application/json' }),
			...(key ? { 'Idempotency-Key': key } : {}),
		},
		...(body === undefined ? {} : { body: JSON.stringify(body) }),
		redirect: 'error',
		cache: 'no-store',
		signal: AbortSignal.timeout(35000),
	});
	if (allowMissing && response.status === 404) return null;
	if (!response.ok) throw new Error(`Payment request failed (${response.status})`);
	return response.json();
}

const { publishableKey } = await paymentRequest('/merchant/payments/configuration');
export const config = { publishableKey, apiOrigin };

API vrací publishableKey a environment. Hodnotu apiOrigin nastavte podle URL API, které jste volali; není to pole odpovědi s konfigurací.

Obě hodnoty můžete bezpečně vložit do stránky. Publishable key má předponu pk_test_, pokud používáte testovací klíče brány, a pk_live_ u ostrých klíčů brány. Loaderu vždy předávejte apiOrigin vrácený vaším serverem, aby prohlížeč i server zůstaly ve stejném prostředí.

Přidání polí pro kartu

Přidejte kontejner do svého stávajícího formuláře pokladny:

checkout.html
<form id="checkout">
	<!-- Your other checkout inputs go here. -->
	<div id="card-fields"></div>
	<button type="submit" disabled>Pay</button>
	<p id="message" role="status"></p>
</form>

Načtěte Tapaya a připojte pole. prepare je endpoint, který přidáte v dalším kroku.

checkout.ts
import { loadTapaya } from '@tapayadot/checkout/client'
import { config } from './config' // { publishableKey, apiOrigin } from your server

const form = document.querySelector<HTMLFormElement>('#checkout')!
const button = form.querySelector<HTMLButtonElement>('button')!
const message = document.querySelector<HTMLElement>('#message')!
const tapaya = await loadTapaya(config.publishableKey, { apiOrigin: config.apiOrigin })
const payment = tapaya.createPayment({
	prepare: '/api/payments',
	onChange: ({ canSubmit, outcome }) => {
		button.disabled = !canSubmit
		if (outcome === 'completed') location.assign('/checkout/confirmation')
		if (outcome === 'ended') message.textContent = 'This payment cannot be completed. Start a new checkout.'
	},
})
await payment.mount('#card-fields')

Po připojení pole zavolají prepare, aby navázala na existující platbu objednávky. Opětovné načtení stránky během platby nikdy nevede k další platbě: stránka místo toho zobrazí aktuální stav platby.

Vrácení platby objednávky

Připojte tento handler k POST /api/payments. getCheckoutOrder() je funkce vaší vlastní aplikace: autentizuje zákazníka, zkontroluje jeho přístup k objednávce a vrátí její uložené id, amount, currency a status. Na tuto routu uplatněte svou obvyklou ochranu proti CSRF.

Než vytvoříte pokus, načtěte existující platbu. Nevyřešenou platbu použijte znovu a další pokus vytvořte pouze po zamítnutí nebo zrušení s retryAllowed: true.

server/payments.js
import { paymentRequest } from './tapaya.js';
import { getCheckoutOrder } from './orders.js';

export async function findPayment(order) {
	const query = new URLSearchParams({ merchantOrderId: order.id });
	const payment = await paymentRequest(`/merchant/payments?${query}`, { allowMissing: true });
	if (payment && (payment.merchantOrderId !== order.id ||
		payment.amount !== order.amount || payment.currency !== order.currency)) {
		throw new Error('Payment does not match the order');
	}
	return payment;
}

export function browserStatus(payment) {
	if (payment.status === 'successful') return 'completed';
	if (['declined', 'cancelled'].includes(payment.status) && !payment.retryAllowed) return 'ended';
	return 'open';
}

export async function POST(request) {
	const order = await getCheckoutOrder(request);
	const payable = order.status === 'pending_payment';
	let payment = await findPayment(order);
	if (payable && (!payment ||
		(['declined', 'cancelled'].includes(payment.status) && payment.retryAllowed))) {
		// Derive the same key on retries, including after a lost creation response.
		let key = `${order.id}:${payment ? `after:${payment.id}` : 'initial'}`;
		if (/[^\x21-\x7e]/.test(key)) {
			const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(key));
			key = `prepare:${Array.from(new Uint8Array(digest), byte => byte.toString(16).padStart(2, '0')).join('')}`;
		}
		payment = await paymentRequest('/merchant/payments', {
			method: 'POST', key,
			body: { merchantOrderId: order.id, amount: order.amount, currency: order.currency },
		});
		if (payment.merchantOrderId !== order.id || payment.amount !== order.amount || payment.currency !== order.currency) {
			throw new Error('Payment does not match the order');
		}
	}
	if (!payment) return Response.json({ error: 'Order cannot accept payment' }, { status: 409 });
	return Response.json(
		{ clientSecret: payable ? payment.clientSecret : null, status: browserStatus(payment) },
		{ headers: { 'Cache-Control': 'no-store' } },
	);
}

Používejte kód měny velkými písmeny a uložené ID objednávky o délce nejvýše 200 znaků. Po zahájení platby údaje objednávky neměňte. Při vypršení časového limitu znovu načtěte platbu objednávky a teprve pak rozhodněte, zda je potřeba nový pokus. Nikdy negenerujte nový klíč jen proto, že se ztratila odpověď.

REST API vrací stavy platby. Node SDK je převádí na status, který pole pro kartu očekávají; integrace přes fetch a cURL používají stejný převod ve svém vlastním endpointu prepare:

status v APIstatus endpointu prepare
successfulcompleted
declined nebo cancelled s retryAllowed: falseended
ready, pending, action_required nebo zamítnutí či zrušení, které lze opakovatopen

Odeslání z vašeho handleru

Přidejte toto do stejného skriptu. Váš handler zvaliduje formulář a zavolá submit():

checkout.ts
form.addEventListener('submit', async (event) => {
	event.preventDefault()
	if (!form.reportValidity() || !payment.snapshot.canSubmit) return
	await payment.submit().catch(() => {
		// The card fields show the reason. The shopper can try again when canSubmit allows it.
	})
})

submit() zavolá pro platbu prepare, potom zvaliduje kartu, provede 3-D Secure a potvrdí platbu u Tapaya.

Zobrazení výsledku

onChange dostane snapshot platby při každé jeho změně, včetně načtení stránky s platbou, která už je dokončená. Řiďte se hodnotou snapshot.outcome:

outcomeCo zobrazit
openTlačítko Zaplatit, aktivní, dokud je canSubmit true. Po zamítnutí zůstává open a další submit() dostane nový pokus.
pendingIndikátor průběhu. Výsledek zatím není znám, takže nelze nic zaplatit znovu. SDK kontroluje stav automaticky, dokud je stránka viditelná.
completedVaši potvrzovací stránku. Tam váš server objednávku ověří.
endedNovou pokladnu. Pro tuto objednávku už není možný žádný další pokus.

Když se zobrazení vaší pokladny odpojuje (unmount), zavolejte payment.destroy(). V Reactu použijte usePayment() z @tapayadot/checkout/react. Přijímá stejné možnosti a navíc publishableKey a apiOrigin a vrací fieldRef pro kontejner, aktuální snapshot a submit().

Ověření před vyřízením

Výsledek v prohlížeči slouží jen k zobrazení. Na potvrzovací stránce načtěte platbu objednávky na svém serveru a objednávku vyřiďte pouze tehdy, když je platba dokončená:

server/confirmation.js
import { findPayment } from './payments.js';

const order = await getCheckoutOrder(request);
const payment = await findPayment(order); // Checks order ID, amount, and currency.
if (payment?.status === 'successful') await markOrderPaid(order.id, payment.id);

Zajistěte, aby markOrderPaid() zaznamenala platbu jen jednou, i když je volána opakovaně, a vyřízení spouštějte z tohoto uloženého stavu.

Odsouhlasení objednávek, které zákazník opustil

Zákazník může zavřít kartu prohlížeče dříve, než se otevře potvrzovací stránka. Spusťte plánovanou úlohu, která pro nezaplacené objednávky volá verify() nebo čte GET /merchant/payments?merchantOrderId=..., se stejnými kontrolami. Zamítnuté a zrušené pokusy umožňují nový pokus, takže nezaplacená objednávka zůstává open: uvolněte ji podle vlastního harmonogramu, a to jen tehdy, když její paymentStatus není pending. Neúspěšný požadavek nebo vypršení časového limitu není zamítnutí, proto objednávku nechte rezervovanou, dokud platba nemá známý výsledek.

Přidání Apple Pay a Google Pay

Peněženky se zobrazují uvnitř rámce Tapaya vedle polí pro kartu. Aby se zobrazily, ověřte, že:

  • Peněženky jsou povolené v nastavení Checkoutu.
  • Apple Pay: obchod má aktivní doménu obchodu.
  • Google Pay v produkčním prostředí: je nakonfigurované Google Pay merchant ID.
  • Zařízení, prohlížeč a peněženka zákazníka danou metodu podporují.

Předejte wallets s celkovou částkou pokladny v nejmenších jednotkách měny. Tapaya zobrazí dostupné metody a nepodporované nebo vypnuté peněženky zůstanou skryté. Použijte tento kód místo příkladu createPayment() pouze pro karty:

checkout-wallets.ts
const payment = tapaya.createPayment({
	prepare: '/api/payments',
	wallets: { amount: order.totalInMinorUnits, currency: order.currency },
	onWalletClick: async () => {
		if (!form.reportValidity()) throw new Error('Complete the checkout details.')
		// Validate the cart and reserve the order on your server.
		const response = await fetch('/api/payments', { method: 'POST' })
		if (!response.ok) throw new Error('Could not prepare the payment.')
		return response.json() // { clientSecret, status }
	},
	onChange: (snapshot) => {
		button.hidden = snapshot.paymentMethod !== 'card'
		button.disabled = !snapshot.canSubmit
		if (snapshot.outcome === 'completed') location.assign('/checkout/confirmation')
	},
})
await payment.mount('#card-fields')

Celková částka zobrazená v prohlížeči musí pocházet z vašeho serveru, který ji při přípravě platby i tak sám vypočítá a ověří.

Kliknutí na peněženku

Oficiální tlačítko peněženky otevře nativní okno peněženky na základě kliknutí zákazníka. onWalletClick zvaliduje vaši pokladnu a rezervuje její platbu ve chvíli, kdy se na toto tlačítko klikne, nikdy při připojení ani při výběru metody. Bez tohoto callbacku používají peněženky prepare nebo clientSecret. Pokud objednávku rezervujete až při kliknutí, zajistěte, aby callback prepare volaný při připojení navázal na existující objednávku, aniž by vytvořil novou.

Vlastní tlačítko Zaplatit nechte viditelné pouze tehdy, když je snapshot.paymentMethod rovno card; peněženky místo payment.submit() používají svá značková tlačítka. Výsledky plateb peněženkou procházejí stejným postupem ověření a obnovy jako karty. Zavření okna peněženky ponechá pokladnu dostupnou; nejistá platba zůstane ve stavu pending a zablokuje další platbu.

Aktualizace celkové částky

Dokud nejsou vyplněné povinné údaje pokladny, předávejte wallets: { amount, currency, disabled: true }. Celkovou částku aktualizujte nebo tlačítka povolte bez opětovného připojení:

payment.update({ wallets: { amount: 2450, currency: 'EUR', disabled: false } })

Předáním wallets: null peněženky skryjete, dokud nebude celková částka připravena. Tapaya odmítne potvrzení, pokud se připravená platba liší od celkové částky peněženky. React hook usePayment() tyto možnosti přijímá a celkovou částku aktualizuje, když se wallets změní.

Vlastní výběr metody

Pokud výběr metody spravuje vaše stránka, předejte paymentMethod: 'card', 'apple_pay' nebo 'google_pay', aby se vykreslila pouze tato metoda. Nedostupná vybraná peněženka zobrazí popisek walletUnavailable a nahlásí snapshot.error.code: 'wallet_unavailable', pokud lze bezpečně zahájit jinou platbu; nabídněte ve svém výběru jinou metodu. Výběr zamkněte, dokud je snapshot.busy true nebo je platba ve stavu pending či action_required.

Rámce a CSP

SDK přidává ke svému rámci allow="payment". Pokud je váš obchod uvnitř jiného rámce, musí platby povolit i tento vnější rámec. Apple Pay napříč originy vyžaduje Safari 17 nebo novější. Skripty poskytovatelů peněženek zůstávají uvnitř rámce Tapaya, takže vaše stránka nepotřebuje ve své CSP žádné další originy poskytovatelů.

Řešení vypršeného přístupu prohlížeče

Client secret vyprší po 24 hodinách a clientSecret je pak null. Platba, která nebyla nikdy odeslána, je pak zrušena a další prepare() zahájí pro stejnou objednávku nový pokus. Čekající platba zůstává v prohlížeči pending a SDK nadále volá váš endpoint, dokud váš server nenahlásí její výsledek jako status. Čtení na serveru stále funguje, takže ji vaše plánovaná úloha v každém případě dokončí.

Diagnostika chyb serveru

Node SDK vyhazuje u odmítnutých požadavků na API PaymentsApiError. Na svém serveru zkontrolujte jeho status, code, message, fieldErrors a requestId. Přihlašovací údaje a client secrety nezapisujte do logů a zákazníkovi zobrazujte vlastní texty.

Přizpůsobení vaší značce

Pole pro kartu používají jako barvu zvýraznění při fokusu (colorFocus) výslednou barvu pokladny obchodníka z nastavení Checkoutu. Hodnoty, které předáte v appearance.variables, ji přepíšou, a odesílají se pouze proměnné, které nastavíte.

Chybí vám něco na této stránce?Navrhněte funkci. Připojíme tuto stránku, aby tým viděl souvislosti.