Jak těžké je integrovat platební bránu GoPay bez předchozích zkušeností s bránami? Na jeden z mých e-shopů jsem se rozhodl nasadit platební bránu. Jde o e-shop s personalizovanými produkty, takže neexistuje žádné hotové řešení. Musím to naprogramovat od nuly.
S GoPay se komunikuje přes REST API. Dokumentace je přehledná, ale nikde jsem nenašel žádnou ukázku implementace, takže se zde pokusím sám nějakou ukázku připravit, protože nemá smysl zkoušet integrovat bránu přímo do e-shopu, dokud nemáte vyzkoušenou komunikaci, tzn. např. zíkání tokenu, založení platby, odesílání či rušení platby, zjištění stavu atd.
Pokud jsem to pochopil správně, s bránou se pracuje následujícím způsobem:
- Získání tokenu pomocí ClientID a ClientSecret
— payment-create (pouze pro založení platby)
nebo
— payment-all (pro všechny další operace) - Založení platby
Předání parametrů z e-shopu do brány, tzn. token v hlavičce, cena, měna platby, povolené platební metody, předvybraná platební metoda, položky objednávky, jazyk platební brány, adresy pro notifikace a adresy pro návrat na prodejní místo (více viz standardní platby https://doc.gopay.cz/#zalozeni-platby )
Výsledkem volání je JSON objekt, kde nejdůležitější je parametrid
, který se musí uložit do objednávky pro návrat z brány a identifikaci s notifikacemi z brány.
Druhým důležitým parametrem jegw_url
, které se použije v dalším kroku (pro vyvolání brány). - Vyvolání platební brány
inline nebo přesměrováním - Návrat z brány
přesměrování nareturn_url
, které bylo jedním z param. v kroku 2 (při založení platby)
Zareturn_url
je platební bránou připojen GET parametr?id=<id_platby>
. - Ověření platby
provést dotaz na stav platby a zákazníkovi zobrazit informaci o aktuálním stavu - Refundace platby
platbu lze za určitý poplatek nechat poslat zákazníkovi nazpět
V API dokumentaci jsou v pravém sloupci ukázky kódu. Hned na začátku je návod na instalaci PHP SDK. Pomocí Compos(t)eru jsem naházel potřebné soubory do složky vendor
2. ZALOŽENÍ PLATBY
Krok číslo 1. jsem přeskočil, protože se autorizace (oauth2) řeší interně s využitím PHP SDK. Znamená to, že se o token nemusíme vůbec starat. Můžeme se soustředit rovnou na krok spočívající v založení platby, odeslání informací o platbě na bránu a získání id
a gw_url
, které použijeme pro vyvolání brány ve třetím kroku.
Z dokumentace jsem zkopíroval následující kód, změnil přihlašovací údaje na ty, které mi byly zaslány z GoPay a začal jsem testovat. Zkusím nejprve získat gw_url …
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
<?php require_once("vendor/autoload.php"); use GoPay\Definition\Language; use GoPay\Definition\TokenScope; use GoPay\Definition\Payment\Currency; use GoPay\Definition\Payment\PaymentInstrument; use GoPay\Definition\Payment\BankSwiftCode; use GoPay\Definition\Payment\VatRate; use GoPay\Definition\Payment\PaymentItemType; $gopay = GoPay\payments([ 'goid' => '', //id a další údaje 'clientId' => '', 'clientSecret' => '', 'isProductionMode' => false, 'scope' => GoPay\Definition\TokenScope::ALL, 'language' => GoPay\Definition\Language::CZECH, 'timeout' => 30 ]); $response = $gopay->createPayment([ 'payer' => [ 'default_payment_instrument' => PaymentInstrument::BANK_ACCOUNT, 'allowed_payment_instruments' => [PaymentInstrument::BANK_ACCOUNT], 'default_swift' => BankSwiftCode::FIO_BANKA, 'allowed_swifts' => [BankSwiftCode::FIO_BANKA, BankSwiftCode::MBANK], 'contact' => ['first_name' => 'Zbynek', 'last_name' => 'Zak', 'email' => 'test@test.cz', 'phone_number' => '+420777456123', 'city' => 'C.Budejovice', 'street' => 'Plana 67', 'postal_code' => '373 01', 'country_code' => 'CZE' ] ], 'amount' => 150, 'currency' => Currency::CZECH_CROWNS, 'order_number' => '7501', 'order_description' => 'tiskovina', 'items' => [ ['name' => 'item01', 'amount' => 50], ['name' => 'item02', 'amount' => 100], ], 'additional_params' => [ array('name' => 'invoicenumber', 'value' => '21785') ], 'callback' => [ 'return_url' => 'https://www.eshop.cz/return', 'notification_url' => 'https://www.eshop.cz/notify' ], 'lang' => Language::CZECH ]); if ($response->hasSucceed()) { echo "hooray, API returned {$response}"; echo $response->json['gw_url']; // url for initiation of gateway } else { // errors format: https://doc.gopay.com/en/?shell\#http-result-codes echo "oops, API returned {$response->statusCode}: {$response}"; } // token získá SDK interně, není třeba volat žádnou metodu pro získání tokenu |
Brána nejprve hlásila chybné přihlašovací údaje. Popletl jsem ClientSecret za SecureKey. Po opravě sice brána začala komunikovat, ale gw_url jsem nezískal. Místo toho přišla chybová hláška, se kterou si zatím nevím rady:
oops, API returned 400: Unrecognized field "0" (class cz.gopay.web.api.model.payment.RCreateBasePayment), not marked as ignorable
Brána se snaží nejspíš sdělit, že předávané pole z příkazu GoPay\payments() má hned první položku nerozpoznatelnou. Pole posílané na bránu má při výpisu pomocí print_r tento tvar:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
[0] => Array ( [payer] => Array ( [default_payment_instrument] => BANK_ACCOUNT [allowed_payment_instruments] => Array ( [0] => BANK_ACCOUNT ) [default_swift] => FIOBCZPP [allowed_swifts] => Array ( [0] => FIOBCZPP [1] => BREXCZPP ) [contact] => Array ( [first_name] => Zbynek [last_name] => Zak [email] => test@test.cz [phone_number] => +420777456123 [city] => C.Budejovice [street] => Plana 67 [postal_code] => 373 01 [country_code] => CZE ) ) [amount] => 150 [currency] => CZK [order_number] => 7501 [order_description] => tiskovina [items] => Array ( [0] => Array ( [name] => item01 [amount] => 50 ) [1] => Array ( [name] => item02 [amount] => 100 ) ) [additional_params] => Array ( [0] => Array ( [name] => invoicenumber [value] => 21785 ) ) [callback] => Array ( [return_url] => https://www.eshop.cz/return [notification_url] => https://www.eshop.cz/notify ) [lang] => CS ) |
Nakonec jsem se rozhodl zkontaktovat technickou podporu. Reakce byla v postatě okamžitá. Pan Šimánek ihned objevil, že jsem tam dal jedny hranaté závorky navíc. Vzniklo to zřejmě divokým kopírováním. (V ukázce výše již nejsou, aby si nikdo stejnou chybu nezopakoval.)
Po zrušení duplicitních hranatých závorek přišla první odezva z brány:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
{ "id": 3151179967, "order_number": "7501", "state": "CREATED", "amount": 150, "currency": "CZK", "payer": { "allowed_payment_instruments": [ "BANK_ACCOUNT" ], "default_payment_instrument": "BANK_ACCOUNT", "default_swift": "FIOBCZPP", "contact": { "first_name": "Zbynek", "last_name": "Zak", "email": "test@test.cz", "phone_number": "+420777456123", "city": "C.Budejovice", "street": "Plana 67", "postal_code": "37301", "country_code": "CZE" } }, "target": { "type": "ACCOUNT", "goid": 8808449177 }, "additional_params": [ { "name": "invoicenumber", "value": "21785" } ], "lang": "cs", "gw_url": "https://gw.sandbox.gopay.com/gw/v3/c0adff2998cf168182f00504f61d1949" } |
default_payment_instrument
… toto bude zřejmě způsob platby zvolený zákazníkem, např. BANK_ACCOUNT
allowed_payment_instruments
… typicky:
["PAYMENT_CARD","BANK_ACCOUNT"]
default_swift
… hádám, že jde o swift kód banky, kterou si uživatel vybral pro platbu
allowed_swifts
… seznam bank, ze kterých je možné dodatečně převybrat přímo na bráně?
Seznam swift kódů dole na stránce https://doc.gopay.cz/#payment-instrument .
3. VYVOLÁNÍ PLATEBNÍ BRÁNY
Jestliže již máte id
a gw_url
, můžete vyvolat bránu a vyzkoušet platbu.
Oba identifikátory získáte jednoduše z JSON response příkazem
1 2 |
$response->json['id']; $response->json['gw_url']; |
Po úspěšné odezvě zobrazíme formulář, kde do parametru action vložíme gw_url
1 2 3 4 5 6 7 8 9 10 11 12 |
if ($response->hasSucceed()) { echo "hooray, API returned {$response}"; //echo $response->json['gw_url']; // url for initiation of gateway echo '<form action="'.$response->json['gw_url'].'" method="post" id="gopay-payment-button"> <button name="pay" type="submit">Zaplatit</button> <script type="text/javascript" src="https://gw.sandbox.gopay.com/gp-gw/js/embed.js"></script> </form>'; } else { // errors format: https://doc.gopay.com/en/?shell\#http-result-codes echo "oops, API returned {$response->statusCode}: {$response}"; } |
Formulář pro odeslání objednávky, resp. vyvolání platební brány má ještě skryté pole s názvem signature, které předává bráně šifrovaný podpis platby:
1 2 3 4 |
<form action="https://gate.gopay.cz/gw/v3/3100000099" method="post" id="gopay-payment-button"> <del><input type="hidden" name="signature" value="25ee53a1eccc253a8310f5267d2de6b483f58af9676d883e26600ce3316ai"/></del> <button name="pay" type="submit">Zaplatit</button> </form> |
Podpis platby lze sestavit následovně:
targetGoId+|+paymentSessionId+|+secureKey
V popisu integrace je trochu zavádějící informace, která se týká předchozí verze brány, což je v dokumentaci jenom naznačeno – v názvu stránky: „Implementace nového vzhledu platební brány pro stávající zákazníky“. Nikde není explicitně uvedeno, že se signature týká výhradně SOAP verzí, které už nejsou podporovány (nyní komunikace probíhá pouze přes REST API). Podpis signature
tedy neprogramujeme!
4. NÁVRAT Z BRÁNY
Po úspěšném provedení platby dojde bránou k přesměrování na odkaz return_url
uvedený při založení platby. Důležité je zpracovat k odkazu přidaný parametr ?id=3151179967, tzn. spárovat s objednávkou.
5. OVĚŘENÍ PLATBY
Data týkající se uskutečněné platby získáme například takto:
1 |
$response = $gopay->getStatus($_GET['id']); |
V příkazu výše předávám id platby rovnou po přesměrování. $response zobrazím například příkazem echo $response;
Správně se však musí vytáhnout id z databáze ze spárované objednávky v bodě 4., protože dotaz na stav platby se provádí také při přesměrování na notifikační stránku notification_url
, což může nastat nezávisle na pozadí, bez účasti uživatele a děje se tak po každé transakci, byť se zpožděním. Notifikační dotaz vrací rovněž ?id=<id platby>. Podle id platby je nutné dohledat objednávku, ke které platba náleží, provést dotaz na stav platby, porovnat ho se stavem v databázi a případně provést update stavu v databázi.
V případě plateb kartou se notifikace provádí z toho důvodu, že uživatel může nechtěně zavřít okno těsně po úspěšném provedení transakce, takže nedojde kvůli tomu k přesměrování do e-shopu. Obchod by se nedozvěděl o platbě. Proto musí dojít ještě k dodatečnému zaslání zprávy (k notifikaci) o proběhlé platbě. GoPay zkouší posílat notifikaci celkem 20x, než to vzdá. Uspokojivou odpovědí je pouze status 200.
Jediný rozdíl oproti zpracování výsledku platby těsně po dokončení interakce s bránou spočívá v tom, že v prvním případě zobrazujeme uživateli nějaké informace, v případě druhém (když jde jen o notifikaci) nic nezobrazujeme, protože není komu. Nestačí tedy pouze zapsat stav do objednávky, ale je potřeba také poslat standardní potvrzovací e-mail zákazníkovi, který se posílá když dojde ke standardnímu přesměrování na eshop.
Po zpracování notifikačního dotazu jsem do skriptu pro jistotu dal ještě příkazy:
1 2 |
http_response_code(200); exit; |
Stav platby je v parametru state
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
{ "id": 3151849454, "order_number": "7501", "state": "PAID", "payment_instrument": "BANK_ACCOUNT", "amount": 15000, "currency": "CZK", "payer": { "allowed_payment_instruments": [ "BANK_ACCOUNT" ], "default_payment_instrument": "BANK_ACCOUNT", "default_swift": "FIOBCZPP", "contact": { "first_name": "Zbynek", "last_name": "Zak", "email": "test@test.cz", "phone_number": "+420777456123", "city": "C.Budejovice", "street": "Plana 67", "postal_code": "37301", "country_code": "CZE" } }, "target": { "type": "ACCOUNT", "goid": 8808449177 }, "additional_params": [ { "name": "invoicenumber", "value": "21785" } ], "lang": "cs", "gw_url": "https://gw.sandbox.gopay.com/gw/v3/c0adff2998cf168182f00504f61d1949", "eet_code": { "fik": "4c02401d-5d00-4d7b-870e-46bcf8ef8a4d-fa", "bkp": "2016B312-FE7077D0-53434C4F-3F3791DB-A8C59969", "pkp": "Ft7xCBJG18plPGAx138OsevQWzOlxaxIwoIUaNA9B2+GApWQksYcgpYFvw1oRAFpGnc6RBt0WZ4Aeg" } } |
Stav platby tedy získáme v dalším kroku takto:
1 |
$response->json['state']; |
Přehled jednotlivých stavů:
CREATED
|
Platba vytvořena |
PAID | Platba uhrazena |
CANCELED | Platba zamítnuta |
PAYMENT_METHOD_CHOSEN | Platební metoda potvrzena |
TIMEOUTED | Platbě vypršela životnost |
AUTHORIZED | Platba předautorizována |
REFUNDED | Platba vrácena |
PARTIALLY_REFUNDED | Platba částečně vrácena |
Dále existují ještě podstavy Substate plateb s číselnými kódy stavů (viz API manuál).
Jednotlivé stavy je dobré ošetřit.
Ještě pro zajímavost uvedu, jak lze získávat data z JSON (bez SDK):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
$data = '{ "id": 3151179967, "order_number": "7501", "state": "CREATED", "amount": 150, "currency": "CZK", "payer": { "allowed_payment_instruments": [ "BANK_ACCOUNT" ], "default_payment_instrument": "BANK_ACCOUNT", "default_swift": "FIOBCZPP", "contact": { "first_name": "Zbynek", "last_name": "Zak", "email": "test@test.cz", "phone_number": "+420777456123", "city": "C.Budejovice", "street": "Plana 67", "postal_code": "37301", "country_code": "CZE" } }, "target": { "type": "ACCOUNT", "goid": 8808449177 }, "additional_params": [ { "name": "invoicenumber", "value": "21785" } ], "lang": "cs" }'; $a = json_decode($data, true); echo $a['target']['goid']; |
6. REFUNDACE PLATBY
Je-li potřeba provést storno platby, lze tak učinit příkazem
1 |
$response = $gopay->refundPayment(3000006620, 50000); |
kde první parametr je id a druhý parametr částka k vrácení. Druhý parametr není povinný, takže při jeho vynechání by mělo dojít k refundaci celé částky.
7. TESTOVÁNÍ PLATEB
Pro testování jsou k dispozici fiktivní čísla karet na stránce Provádění plateb v testovacím prostředí.
8. PŘECHOD NA OSTRÝ PROVOZ
Jakmile dojde k podepsání smlouvy, identifikaci a technické oddělení ověří funkčnost integrace (podívají se do hlášek v sandboxu), zašlou ostré provozní údaje (část na mail, zbytek na mobil). Následuje zkušební platba v ostrém režimu, kterou ohlásíte na technické oddělení. Prostě něco objednáte (platbu můžete po vyvolání brány zrušit). Oni to zkontrolují, potvrdí a vy zapnete zobrazování možnosti platby přes bránu i uživatelům. Dokud není potvrzena první platba, nezobrazujte uživatelům možnost platby přes bránu! Řeším to pomocí cookie.
A to je vše.
Hlavně nezapomeňte prohodit i url skript ostré brány, tzn. namísto https://gw.sandbox.gopay.com/gp-gw/js/embed.js tam nakopírujte https://gate.gopay.cz/gp-gw/js/embed.js .