Přeskočit na hlavní obsah

Příklady implementace

Checkout SDK lze implementovat více způsoby v závislosti na prostředí a použitých technologiích e-shopu.

Následující příklady ilustrují základní scénáře implementace, které mohou sloužit jako výchozí bod pro integraci.

1. Vložením scriptu z Comgate CDN

Checkout SDK je možné načíst přímo z Comgate CDN pomocí vloženého <script> tagu.

Stáhnout ukázkový projekt

Příklad je rozsáhlý, proto je zobrazen ve sbalitelném bloku.
<!DOCTYPE html>
<html lang="cs">
<head>
<meta charset="UTF-8" />
<title>Comgate Checkout – CDN example integration</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body {
background: #FFFFFF;
}
.container {
width: 240px;
background: #DDDDDD;
border: solid 1px #d0d0d0;
padding: 5px;
}

.container .button {
background: #808080;
width: 100%;
height: 50px;
}
.container .button:first-child {
margin-bottom: 5px;
}
</style>
</head>
<body>
<div class="container">
<div class="button" id="apple-pay-button-box"></div>
<div class="button" id="google-pay-button-box"></div>
</div>

<!-- Vlož script @comgate/checkout-js (Loaderu) z Comgate CDN (nikdy nepoužívejte jsDelivr, unpkg a další) -->
<!-- Pokud lze, doporučujeme použít NPM balíček @comgate/checkout-js (https://www.npmjs.com/package/@comgate/checkout-js) -->
<script src="https://checkout.comgate.cz/sdk/@2/checkout-js.min.js"></script>
<script>
// kde získat checkoutId: https://apidoc.comgate.cz/checkout/konfigurace#aktivace-checkout-sdk
const checkoutId = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'; // PŘED
const transactionId = 'XXXX-XXXX-XXXX';
const checkoutJsRef = window.comgateCheckoutJs;

const APPLE_PAY_BUTTON_TYPE = 'plain';
const APPLE_PAY_BUTTON_COLOR = 'white';

const GOOGLE_PAY_BUTTON_TYPE = 'short';
const GOOGLE_PAY_BUTTON_COLOR = 'white';

const APPLE_PAY_BUTTON_BOX_SELECTOR = '#apple-pay-button-box';
const GOOGLE_PAY_BUTTON_BOX_SELECTOR = '#google-pay-button-box';

let isCheckoutReadyTmp = false;

/**
* Spustí tok: kontrola verze → přednačtení → vlastní kód → načtení Checkoutu.
*/
function run() {
if (!isCheckoutReady()) {
console.warn('Comgate Checkout: Loader není dostupný.');
return;
}
checkUpdate(); // zkontroluj major update
preloadCheckout(); // proveď přednačtení modulů

// ===================
// === DIY: vlastní kód ===
// ===================

loadCheckout(); // načti Checkout
}
run();

/**
* Ověří, že je Loader pro Checkout SDK připraven k použití.
* @returns {boolean} Vrací true, pokud je Loader připraven, jinak false.
*/
function isCheckoutReady() {
return isCheckoutReadyTmp ||= (
!!checkoutJsRef &&
typeof checkoutJsRef === 'object' &&
typeof checkoutJsRef.loadComgateCheckout === 'function'
);
}

/**
* Zkontroluje implementovanou major verzi vůči aktuálně preferované verzi Checkout SDK.
* Pokud není použita preferovaná verze, zaloguje upozornění.
*/
function checkUpdate() {
if (!isCheckoutReady()) return;
if (checkoutJsRef.VERSION_2 !== checkoutJsRef.VERSION_PREFERRED) {
// DIY: napojte na vlastní logger, případně odstraňte
console.warn(
'Comgate Checkout: Dostupná nová major verze Checkout SDK. ' +
'Aktualizujte kód, abyste získali nejnovější funkce.'
);
}
}

/**
* Provádí přednačtení potřebných modulů do cache prohlížeče,
* aby plátce nečekal ve chvíli, kdy se Checkout začne používat.
*/
function preloadCheckout() {
if (!isCheckoutReady()) {
console.error('Comgate Checkout: Přednačtení modulů se nezdařilo – Loader není k dispozici.');
return;
}

// Pozn.: „fire-and-forget“ – výsledek se dále nezpracovává, stačí ošetřit chybu.
checkoutJsRef.preloadComgateCheckout({
checkoutId,
version: checkoutJsRef.VERSION_2,
modules: [checkoutJsRef.MODULE_GOOGLEPAY, checkoutJsRef.MODULE_APPLEPAY],
})
.catch((error) => {
console.error('Comgate Checkout: Chyba při přednačtení modulů.', error);
});
}

/**
* Spustí (případně i stáhne) potřebné moduly, aby byly připraveny k použití.
*/
function loadCheckout() {
if (!isCheckoutReady()) {
console.error('Comgate Checkout: Načtení modulů se nezdařilo – Loader není k dispozici.');
return;
}

checkoutJsRef.loadComgateCheckout({
checkoutId,
version: checkoutJsRef.VERSION_2,
modules: [checkoutJsRef.MODULE_GOOGLEPAY, checkoutJsRef.MODULE_APPLEPAY],
// debug: true,
})
.then(async (result) => {
let coreInstance = null;
let applePayInstance = null;
let googlePayInstance = null;

if(result.error) {
console.error('Comgate Checkout: Detaily chyb při načítání modulů:', result.error);
return;
}

// Core
coreInstance = await prepareCore(result.core);
if(!coreInstance) {
console.error("Comgate Checkout: Instanci Core se nepodařilo vytvořit");
return;
}

applePayInstance = await prepareApplePay(coreInstance, result.applepay);
if(applePayInstance) {
const apMountResult = await mountApplePay(applePayInstance, APPLE_PAY_BUTTON_BOX_SELECTOR);
if(apMountResult) {
console.log(`Comgate Checkout: Tlačítko Apple pay je připraveno pro plátce.`);
}
}

googlePayInstance = await prepareGooglePay(coreInstance, result.googlepay);
if(googlePayInstance) {
const gpMountResult = await mountGooglePay(googlePayInstance, GOOGLE_PAY_BUTTON_BOX_SELECTOR);
if(gpMountResult) {
console.log(`Comgate Checkout: Tlačítko Google pay je připraveno pro plátce.`);
}
}
})
.catch((error) => {
// Nečekaná chyba (Promise byla rejectnuta)
console.error('Comgate Checkout: Neočekávaná chyba při načítání modulů.', error);
});
}

/**
* Připraví instanci modulu Core
* @param loaderCore
* @return {Promise<CheckoutCore>}
*/
async function prepareCore(loaderCore) {
if(!loaderCore || typeof loaderCore !== 'object' || typeof loaderCore.create !== 'function') {
console.error('Comgate Checkout: Chyba načítání modulu Core. Očekávaná funkce core.create() neexistuje.');
return null;
}

console.log(`Comgate Checkout: modul Core v${loaderCore.version()} je připraven.`);

const coreConfig = {
checkoutId,
transactionId,
locale: "cs",
onPaid: onPaidHandler, // mapování handleru
onCancelled: onCancelledHandler, // mapování handleru
onPending: onPendingHandler, // mapování handleru
onError: onErrorHandler, // mapování handleru
onPaymentStarted: onPaymentStartedHandler, // mapování handleru
onPaymentStopped: onPaymentStoppedHandler, // mapování handleru
debug: true, // v produkci vypnout
//env: 'prod'
}

return loaderCore.create(coreConfig)
// then není potřeba stejně by jen udělal `return instance`
// .then((instance) => { return instance; })
.catch((error) => {
console.error("Comgate Checkout: chyba při vytváření instance CORE", error);
return null;
});

// alternativní zápis
// try {
// return await loaderCore.create(coreConfig);
// } catch (error) {
// console.error("Comgate Checkout: chyba při vytváření instance CORE", error);
// return null;
// }
}

/**
* Připraví instanci modulu Apple Pay
* @param coreInstance
* @param loaderApplePay
* @return {Promise<ModuleApplePay|null>}
*/
async function prepareApplePay(coreInstance, loaderApplePay) {
if(!loaderApplePay || typeof loaderApplePay !== 'object' || typeof loaderApplePay.create !== 'function') {
console.error('Comgate Checkout: Chyba načítání modulu Apple Pay. Očekávaná funkce applepay.create() neexistuje.');
return null;
}

console.log(`Comgate Checkout: modul Apple Pay v${loaderApplePay.version()} je připraven.`);

const apConfig = {
ui: {
type: APPLE_PAY_BUTTON_TYPE,
color: APPLE_PAY_BUTTON_COLOR
},
actions: {
onButtonClick: () => onButtonClickHandler('applepay')
}
};

return loaderApplePay.create(coreInstance, apConfig)
// then není potřeba stejně by jen udělal `return instance`
// .then((instance) => { return instance; })
.catch((error) => {
console.error("Comgate Checkout: chyba při vytváření instance APPLEPAY", error);
return null;
})

// alternativní zápis
// try {
// return await loaderApplePay.create(coreInstance, {ui: {type: APPLE_PAY_BUTTON_TYPE, color: APPLE_PAY_BUTTON_COLOR}});
// } catch (error) {
// console.error("Comgate Checkout: chyba při vytváření instance APPLEPAY", error);
// return null;
// }
}

/**
* Mount tlačítka Apple Pay
* @param apInstance
* @param selector string
* @return {Promise<boolean>}
*/
async function mountApplePay(apInstance, selector) {
const can = await apInstance.canMakePayments()
.catch(() => {
console.warn('Comgate Checkout: ApplePay canMakePayments said no.');
return false;
});

if(!can) {
return false;
}

const apMountPoint = document.querySelector(selector);
if(!apMountPoint) {
console.warn(`Comgate Checkout: ApplePay element pro mountování s id="${selector}" nebyl nalezen.`)
return false;
}

return await apInstance.mount([apMountPoint])
// then není potřeba stejně by jen udělal `return true`
// .then((result) => { return true; }) // result je na then vždy true
.catch((error) => {
console.error("Comgate Checkout: chyba při mount tlačítka APPLEPAY", error);
return false;
});
}

/**
* Připraví instanci modulu Google Pay
* @param coreInstance
* @param loaderGooglePay
* @return {Promise<ModuleGooglePay|null>}
*/
async function prepareGooglePay(coreInstance, loaderGooglePay) {
if(!loaderGooglePay || typeof loaderGooglePay !== 'object' || typeof loaderGooglePay.create !== 'function') {
console.error('Comgate Checkout: Chyba načítání modulu Google Pay. Očekávaná funkce googlepay.create() neexistuje.');
return null;
}

console.log(`Comgate Checkout: modul Google Pay v${loaderGooglePay.version()} je připraven.`);

const gpConfig = {
ui: {
type: GOOGLE_PAY_BUTTON_TYPE,
color: GOOGLE_PAY_BUTTON_COLOR
},
actions: {
onButtonClick: () => onButtonClickHandler('googlepay')
}
};

return loaderGooglePay.create(coreInstance, gpConfig)
// then není potřeba stejně by jen udělal `return instance`
// .then((instance) => { return instance; })
.catch((error) => {
console.error("Comgate Checkout: chyba při vytváření instance GOOGLEPAY", error);
return null;
})

// alternativní zápis
// try {
// return await loaderGooglePay.create(coreInstance, {ui: {type: GOOGLE_PAY_BUTTON_TYPE, color: GOOGLE_PAY_BUTTON_COLOR}});
// } catch (error) {
// console.error("Comgate Checkout: chyba při vytváření instance GOOGLEPAY", error);
// return null;
// }
}

/**
* Mount tlačítka Google Pay
* @param gpInstance
* @param selector string
* @return {Promise<boolean>}
*/
async function mountGooglePay(gpInstance, selector) {
const can = await gpInstance.canMakePayments()
.catch(() => {
console.warn('Comgate Checkout: GooglePay canMakePayments said no.');
return false;
});

if(!can) {
return false;
}

const gpMountPoint = document.querySelector(selector);
if(!gpMountPoint) {
console.warn(`Comgate Checkout: GooglePay element pro mountování s id="${selector}" nebyl nalezen.`)
return false;
}

return await gpInstance.mount([gpMountPoint])
// then není potřeba stejně by jen udělal `return true`
// .then((result) => { return true; }) // result je na then vždy true
.catch((error) => {
console.error("Comgate Checkout: chyba při mountování tlačítka GOOGLEPAY", error);
return false;
});
}

// =================================================================================
// Sekce handlerů určených pro instance Core, ApplePay a GooglePay
// =================================================================================

function onPaidHandler(payload) {
console.log("onPaid", payload);
// DIY: vistory screen - platba provedena
}

function onCancelledHandler(payload) {
console.log("onCancelled", payload);
// DIY: defeat screen - platba byla zrušena
}

function onPendingHandler(payload) {
console.log("onPending", payload);
// DIY: retry payment screen - pokus se nezdařil, zkusit znovu
}

function onErrorHandler(payload) {
console.log("onError", payload);
// DIY: failure screen - vznikla chyba, opakuje později
// refresh stránky?
}

function onPaymentStartedHandler() {
console.log("onPaymentStarted");
// DIY: zobrazit animaci zpracování platby
}

function onPaymentStoppedHandler() {
console.log("onPaymentStopped");
// DIY: skrýt animaci zpracování platby
}

function onButtonClickHandler(service) {
console.log("onButtonClick", service);
//=============================================================================================//
// Pokud je tento handler v configu modulu definován, čeká Checkout na dokončení této funkce. //
// Jako návratový typ očekává void nebo promise! //
//=============================================================================================//

// DIY: libovolná logika, která by neměla být příliš dlouhá
// nedoporučuje se zde dělat requesty a trvající akce
// Google Pay i Apple Pay ověřují fyzické kliknutí a když uplyně příliš času, platbu odmítnou realizovat

// A) standardním koncem je nedefinovaný return

// B) Také je možné vrátit libovolnou hodnotu na kterou se však nebere ohled a považuje se za potvrzení pokračování
// return false;

// C) přímý resolve dovolí taktéž pokračování
// return Promise.resolve();

// D) zatímco, pokud dojde k vrácení reject, tak dojde k zastavení procesu a je nutné znovu kliknout na tlačítko
// (nedochází také k notifikaci plátce, protože tuto akci má eshop pod kontrolou sám)
// return Promise.reject();

// E) pokud dojde k vrácení nevyřešeného promise, čeká na resolve / reject a chová se jako C nebo D
// return new Promise((resolve, reject) => {
// setTimeout(() => {
// resolve();
// // reject();
// }, 2000);
// });
}
</script>
</body>
</html>

2. Integrace pomocí NPM v Reactu

Checkout SDK lze nainstalovat jako závislost v projektu a následně jej využít přímo v komponentách Reactu.

Z důvodu rozsahu nebylo možné zpřístupnit ukázku jako jeden soubor. Kompletní projekt postavený na Vite.js je k dispozici ke stažení (viz odkaz níže).

Upozornění

V současné době není plně funkční automatické našeptávání typů poskytovaných knihovnou @comgate/checkout-js instalovanou z NPM.

Stáhnout ukázkový projekt

Jak projekt spustit lokálně?
  1. Stáhněte archiv a rozbalte ho do pracovní složky.
  2. Spusťte npm i pro instalaci všech závislostí v rootu projektu.
  3. V souboru src/App.tsx aktualizujte hodnotu paymentId.
  4. V souboru src/shared/comgate/checkout/configuration.ts aktualizujte hodnotu CHECKOUT_ID podle tohoto návodu: Comgate Checkout Konfigurace.
  5. Spusťte vývojový server příkazem npm run dev.
  6. Otevřete aplikaci ve svém prohlížeči pomocí poskytnuté localhost adresy.
Tip

V tomto ukázkovém příkladu je ve třídě EventBus použit návrhový vzor Observer, kdy je jedna událost propagována do více cílových komponent.

Hlavním důvodem použití tohoto vzoru je potřeba zachovat jedinou instanci CORE, která spravuje registrované handlery událostí (např. onPaid). Tyto události musí být distribuovány do všech komponent, které jsou v průběhu času dynamicky vytvářeny a rušeny, například komponent zajišťujících obsluhu tlačítek Apple Pay a Google Pay.