Tapaya
Online platbySprievodca integráciouEmbedded Checkout

Embedded Checkout

Pridajte do svojej vlastnej pokladne zabezpečené polia pre kartu a Apple Pay alebo Google Pay.

Vložte zabezpečené polia pre kartu priamo do svojej pokladne. Formulár aj odosielacie tlačidlo máte vo svojej réžii; Tapaya zabezpečuje validáciu karty, tokenizáciu a 3-D Secure. Vložené platby sú dostupné v ostrej prevádzke pre schválených obchodníkov a na testovanie je k dispozícii sandbox. Tento sprievodca používa 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.

Platobné metódy

Embedded Checkout podporuje karty, Apple Pay a Google Pay. Pre karty použite vlastné tlačidlo Pay a pre Apple Pay a Google Pay oficiálne tlačidlá peňaženiek. Pozrite si Pridanie Apple Pay a Google Pay.

Skôr ako začnete

Inštalácia SDK

Jeden balík obsahuje serverového klienta, SDK pre prehliadač aj React hook:

npm install @tapayadot/checkout
ImportKde
@tapayadot/checkoutIba na vašom serveri. Import v zostavení pre prehliadač vyhodí chybu, takže sa váš API kľúč nikdy nedostane na stránku.
@tapayadot/checkout/clientVaša stránka pokladne.
@tapayadot/checkout/reactVaša stránka pokladne, ak používate React.

Konfigurácia servera

Vložené platby nepotrebujú návratové URL. Serverové príklady nižšie sú vo verziách HTTP (fetch), Node.js SDK a cURL. Všetky volajú to isté API obchodníka a polia pre kartu v prehliadači nechávajú bez zmeny.

Node.js SDKHTTP (fetch) a cURL
KľúčTAPAYA_SECRET_KEY, ktorý načíta new Tapaya()TAPAYA_CHECKOUT_API_KEY
ProstredieTAPAYA_ENVIRONMENT=sandbox alebo production. Bez tejto premennej kľúč tapaya_live_ zvolí produkčné prostredie a pri ostatných kľúčoch sa predvolene použije sandbox.TAPAYA_CHECKOUT_API_URL: https://api.sandbox.tapaya.com alebo https://api.tapaya.com
Režim platformyOdovzdávajte merchantToken objednávky v každom volaní alebo ho nastavte pomocou new Tapaya(key, { merchantToken })Posielajte X-Tapaya-Merchant-Token v každej požiadavke. Pomocná funkcia pre fetch ho načítava z TAPAYA_MERCHANT_TOKEN.

Checkout kľúč obchodníka nič ďalšie nepotrebuje. V režime platformy použite Secret key organizácie a backend, ktorý obsluhuje viacerých obchodníkov, nechajte vyberať token podľa objednávky z autentifikovanej relácie zákazníka. Pozrite si Autentifikácia Checkoutu. Client secrets sú špecifické pre každú platbu.

Dokončite onboarding s online platbami a overte, že je obchodník v nastaveniach Checkoutu v stave Ready.

Povolenie Tapaya vo vašej CSP

Loader sťahuje Tapaya.js 0.2.0 z https://js.tapaya.com/checkout/0.2.0/checkout.js; verzia skriptu pre prehliadač je nezávislá od verzie Node.js SDK. Pridajte origin assetov Tapaya do svojej existujúcej Content Security Policy:

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

Prehliadač dostane publishable key a client secret špecifický pre danú platbu. Váš tajný API kľúč obchodníka zostáva na vašom serveri a údaje o karte aj skripty poskytovateľa platieb zostávajú vnútri rámca Tapaya. Na lokálne testovanie hostujte Tapaya.js sami a jeho origin odovzdajte loaderu, napríklad loadTapaya(publishableKey, { apiOrigin, origin: 'http://localhost:4191' }).

Ako to funguje

  1. Vaša stránka pripojí polia pre kartu od Tapaya a nasmeruje ich na endpoint na vašom serveri.
  2. Pri načítaní stránky a pri platbe zákazníka volajú polia pre kartu tento endpoint. Ten vráti platbu objednávky ako clientSecret: prihlasovací údaj pre jednu platbu, pomocou ktorého polia pre kartu potvrdia platbu priamo v Tapaya. Kód vašej stránky ho nikdy nečíta.
  3. Vaša stránka sleduje outcome platby. Keď sa platba dokončí, váš server ju overí a až potom objednávku vybavíte.

Prijatie platby

Získanie nastavení pre prehliadač

Načítajte nastavenia pre prehliadač raz na serveri a pridajte ich do konfigurácie svojho frontendu.

Túto pomocnú funkciu majte na serveri. Kontroluje chyby HTTP, na požiadanie považuje chýbajúcu platbu objednávky za null a požiadavky 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 vracia publishableKey a environment. Hodnotu apiOrigin nastavte podľa URL API, ktoré ste volali; nie je to pole odpovede s konfiguráciou.

Obe hodnoty môžete bezpečne vložiť do stránky. Publishable key má predponu pk_test_ pri testovacích kľúčoch brány a pk_live_ pri ostrých kľúčoch brány. Loaderu vždy odovzdávajte apiOrigin vrátený vaším serverom, aby prehliadač aj server zostali v tom istom prostredí.

Pridanie polí pre kartu

Pridajte kontajner do svojho existujúceho formulára pokladne:

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čítajte Tapaya a pripojte polia. prepare je endpoint, ktorý pridáte v ďalšom 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 pripojení polia zavolajú prepare, aby nadviazali na existujúcu platbu objednávky. Opätovné načítanie stránky počas platby nikdy nevedie k ďalšiemu zaťaženiu karty: stránka namiesto toho zobrazí aktuálny stav platby.

Vrátenie platby objednávky

Pripojte tento handler k POST /api/payments. getCheckoutOrder() je funkcia vašej vlastnej aplikácie: autentifikuje zákazníka, skontroluje jeho prístup k objednávke a vráti jej uložené id, amount, currency a status. Na túto routu uplatnite svoju obvyklú ochranu proti CSRF.

Skôr ako vytvoríte pokus, načítajte existujúcu platbu. Nevyriešenú platbu použite znova a ďalší pokus vytvorte iba po zamietnutí alebo 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žívajte kód meny veľkými písmenami a uložené ID objednávky s dĺžkou najviac 200 znakov. Po začatí platby údaje objednávky nemeňte. Pri vypršaní časového limitu znova načítajte platbu objednávky a až potom rozhodnite, či je potrebný nový pokus. Nikdy negenerujte nový kľúč len preto, že sa stratila odpoveď.

REST API vracia stavy platby. Node SDK ich prevádza na status, ktorý polia pre kartu očakávajú; integrácie cez fetch a cURL používajú rovnaký prevod vo svojom vlastnom endpointe prepare:

status v APIstatus endpointu prepare
successfulcompleted
declined alebo cancelled s retryAllowed: falseended
ready, pending, action_required alebo zamietnutie či zrušenie, ktoré možno opakovaťopen

Odoslanie z vášho handlera

Pridajte toto do toho istého skriptu. Váš handler zvaliduje formulár 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á pre platbu prepare, potom zvaliduje kartu, vykoná 3-D Secure a potvrdí platbu v Tapaya.

Zobrazenie výsledku

onChange dostane snapshot platby pri každej jeho zmene, vrátane načítania stránky s platbou, ktorá je už dokončená. Riaďte sa hodnotou snapshot.outcome:

outcomeČo zobraziť
openTlačidlo Pay, aktívne, kým je canSubmit true. Po zamietnutí zostáva open a ďalšie submit() dostane nový pokus.
pendingIndikátor priebehu. Výsledok zatiaľ nie je známy, takže nemožno nič zaplatiť znova. SDK kontroluje stav automaticky, kým je stránka viditeľná.
completedVašu potvrdzovaciu stránku. Tam váš server objednávku overí.
endedNovú pokladňu. Pre túto objednávku už nie je možný žiadny ďalší pokus.

Keď sa zobrazenie vašej pokladne odpája (unmount), zavolajte payment.destroy(). V Reacte použite usePayment() z @tapayadot/checkout/react. Prijíma rovnaké možnosti a navyše publishableKey a apiOrigin a vracia fieldRef pre kontajner, aktuálny snapshot a submit().

Overenie pred vybavením

Výsledok v prehliadači slúži iba na zobrazenie. Na potvrdzovacej stránke načítajte platbu objednávky na svojom serveri a objednávku vybavte iba vtedy, keď 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);

Zabezpečte, aby markOrderPaid() zaznamenala platbu iba raz, aj keď sa volá opakovane, a vybavenie spúšťajte z tohto uloženého stavu.

Odsúhlasenie objednávok, ktoré zákazník opustil

Zákazník môže zatvoriť kartu prehliadača skôr, ako sa otvorí potvrdzovacia stránka. Spustite plánovanú úlohu, ktorá pre nezaplatené objednávky volá verify() alebo číta GET /merchant/payments?merchantOrderId=..., s rovnakými kontrolami. Zamietnuté a zrušené pokusy umožňujú nový pokus, takže nezaplatená objednávka zostáva open: uvoľnite ju podľa vlastného harmonogramu, a to iba vtedy, keď jej paymentStatus nie je pending. Neúspešná požiadavka alebo vypršanie časového limitu nie je zamietnutie, preto objednávku nechajte rezervovanú, kým platba nemá známy výsledok.

Pridanie Apple Pay a Google Pay

Peňaženky sa zobrazujú vnútri rámca Tapaya vedľa polí pre kartu. Aby sa zobrazili, overte, že:

  • Peňaženky sú povolené v nastaveniach Checkoutu.
  • Apple Pay: obchod má aktívnu doménu obchodu.
  • Google Pay v produkčnom prostredí: je nakonfigurované Google Pay merchant ID.
  • Zariadenie, prehliadač a peňaženka zákazníka danú metódu podporujú.

Odovzdajte wallets s celkovou sumou pokladne v najmenších jednotkách meny. Tapaya zobrazí dostupné metódy a nepodporované alebo vypnuté peňaženky zostanú skryté. Použite tento kód namiesto príkladu createPayment() iba pre 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á suma zobrazená v prehliadači musí pochádzať z vášho servera, ktorý ju pri príprave platby aj tak sám vypočíta a overí.

Kliknutia na peňaženku

Oficiálne tlačidlo peňaženky otvorí natívne platobné okno peňaženky na základe kliknutia zákazníka. onWalletClick zvaliduje vašu pokladňu a rezervuje jej platbu vo chvíli, keď sa na toto tlačidlo klikne, nikdy pri pripojení ani pri výbere metódy. Bez tohto callbacku používajú peňaženky prepare alebo clientSecret. Ak objednávku rezervujete až pri kliknutí, zabezpečte, aby callback prepare volaný pri pripojení nadviazal na existujúcu objednávku bez toho, aby vytvoril novú.

Vlastné tlačidlo Pay nechajte viditeľné iba vtedy, keď je snapshot.paymentMethod rovné card; peňaženky namiesto payment.submit() používajú svoje značkové tlačidlá. Výsledky platieb peňaženkou prechádzajú rovnakým postupom overenia a obnovy ako karty. Zatvorenie platobného okna peňaženky ponechá pokladňu dostupnú; neistá platba zostane čakajúca a zablokuje ďalšie zaťaženie karty.

Aktualizácia celkovej sumy

Kým nie sú vyplnené povinné údaje pokladne, odovzdávajte wallets: { amount, currency, disabled: true }. Celkovú sumu aktualizujte alebo tlačidlá povoľte bez opätovného pripojenia:

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

Odovzdaním wallets: null peňaženky skryjete, kým nebude celková suma pripravená. Tapaya odmietne potvrdenie, ak sa pripravená platba líši od celkovej sumy peňaženky. React hook usePayment() tieto možnosti prijíma a celkovú sumu aktualizuje, keď sa wallets zmení.

Vlastný výber metódy

Ak výber metódy spravuje vaša stránka, odovzdajte paymentMethod: 'card', 'apple_pay' alebo 'google_pay', aby sa vykreslila iba táto metóda. Nedostupná vybraná peňaženka zobrazí popisok walletUnavailable a nahlási snapshot.error.code: 'wallet_unavailable', ak možno bezpečne začať inú platbu; ponúknite vo svojom výbere inú metódu. Výber zamknite, kým je snapshot.busy true alebo je platba v stave pending či action_required.

Rámce a CSP

SDK pridáva k svojmu rámcu allow="payment". Ak je váš obchod vnútri iného rámca, musí platby povoliť aj tento vonkajší rámec. Apple Pay naprieč originmi vyžaduje Safari 17 alebo novší. Skripty poskytovateľov peňaženiek zostávajú vnútri rámca Tapaya, takže vaša stránka nepotrebuje vo svojej CSP žiadne ďalšie originy poskytovateľov.

Spracovanie vypršaného prístupu prehliadača

Client secret vyprší po 24 hodinách a clientSecret je potom null. Platba, ktorá nebola nikdy odoslaná, sa potom zruší a ďalšie prepare() začne pre tú istú objednávku nový pokus. Čakajúca platba zostáva v prehliadači pending a SDK naďalej volá váš endpoint, kým váš server nenahlási jej výsledok ako status. Čítanie na serveri stále funguje, takže ju vaša plánovaná úloha v každom prípade dokončí.

Diagnostika chýb servera

Node SDK pri odmietnutých požiadavkách na API vyhadzuje PaymentsApiError. Na svojom serveri skontrolujte jeho status, code, message, fieldErrors a requestId. Prihlasovacie údaje a client secrets nezapisujte do logov a zákazníkovi zobrazujte vlastné texty.

Prispôsobenie vašej značke

Polia pre kartu používajú ako farbu zvýraznenia pri fokuse (colorFocus) výslednú farbu pokladne obchodníka z nastavení Checkoutu. Hodnoty, ktoré odovzdáte v appearance.variables, ju prepíšu, a posielajú sa iba premenné, ktoré nastavíte.

Chýba vám niečo na tejto stránke?Navrhnite funkciu. Pripojíme túto stránku, aby tím videl súvislosti.