Ze względu na potrzebę zmaksymalizowania bezpieczeństwa wprowadzanych danych kart, ta metoda płatności wymaga wdrożenia dodatkowych zabezpieczeń na stronie sklepu i jest jedną z najbardziej zaawansowanych integracji.
- Pobierz biblioteki Tpay z serwisu Github.com
- Stwórz plik (np. w folderze examples) rozszerzający klasę PaymentCardForms, w którego konstruktorze podaj swoje dane dostępowe do API kart płatniczych:
<?php
namespace tpayLibs\examples;
use tpayLibs\src\_class_tpay\PaymentForms\PaymentCardForms;
use tpayLibs\src\_class_tpay\Utilities\Util;
use tpayLibs\src\Dictionaries\FieldsConfigDictionary;
include_once 'config.php';
include_once 'loader.php';
class CardGate extends PaymentCardForms
{
public function __construct()
{
$this->cardApiKey = 'bda5eda723bf1ae71a82e90a249803d3f852248d';
$this->cardApiPass = 'IhZVgraNcZoWPLgA';
$this->cardKeyRSA = 'LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0NCk1JR2ZNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0R05BRENCaVFLQmdRQ2NLRTVZNU1Wemd5a1Z5ODNMS1NTTFlEMEVrU2xadTRVZm1STS8NCmM5L0NtMENuVDM2ekU0L2dMRzBSYzQwODRHNmIzU3l5NVpvZ1kwQXFOVU5vUEptUUZGVyswdXJacU8yNFRCQkxCcU10TTVYSllDaVQNCmVpNkx3RUIyNnpPOFZocW9SK0tiRS92K1l1YlFhNGQ0cWtHU0IzeHBhSUJncllrT2o0aFJDOXk0WXdJREFRQUINCi0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQ';
$this->cardVerificationCode = '6680181602d396e640cb091ea5418171';
$this->cardHashAlg = 'sha1';
parent::__construct();
}
}
- Dodaj publiczną funkcję sterującą klasą, w której udostępnimy bramkę płatności na stronie sklepu wywołując metodę getOnSiteCardForm():
public function init()
{
if (empty($_POST)) {
echo $this->getOnSiteCardForm('CardGate.php');
}
}
Parametr wejściowy funkcji getOnSiteCardForm($url) musi zawierać nazwę pliku, który obierze dane z formularza i wykona próbę realizacji zapłaty. W tym przykładzie będzie to ten sam plik, który edytujemy.
- Dodaj funkcję odbierającą dane z formularza i wykonującą zapytanie do serwera Tpay:
private function makeCardPayment()
{
$cardData = Util::post('carddata', FieldsConfigDictionary::STRING);
$clientName = Util::post('client_name', FieldsConfigDictionary::STRING);
$clientEmail = Util::post('client_email', FieldsConfigDictionary::STRING);
$saveCard = Util::post('card_save', FieldsConfigDictionary::STRING);
Util::log('Secure Sale post params', print_r($_POST, true));
if ($saveCard === 'on') {
$this->setOneTimer(false);
}
$this->setAmount(123)->setCurrency(985)->setOrderID('test payment 123');
$this->setLanguage('en')->setReturnUrls('https://tpay.com', 'https://google.pl');
return $this->registerSale($clientName, $clientEmail, 'test sale', $cardData);
}
Niektóre dane należy pobrać z bazy danych (kwotę, walutę itp.), w tym przykładzie wprowadziliśmy dane przykładowe.
- Wykonanie funkcji MakeCardPayment() zwróci wynik, który może zawierać link do bramki 3DS lub dane udanej sprzedaży (w zależności od zabezpieczenia 3DS na karcie klienta).
Przykładowy zestaw parametrów zwracany przez API dla kart bez 3DS:
$response = [
'result' => '1',
'test_mode' => '1',
'sale_auth' => 't59c28295aeb071b0cf6471b24f727f6456998de',
'cli_auth' => 't59c2810d59285e3e0ee9d1f1eda1c2f4c554e24',
'currency' => '985',
'amount' => '10.99',
'date' => '2017-06-28 15:31:46',
'status' => 'correct',
'card' => '1234****',
'sign' => '5b7667a28605179ca60350e7468b123d1a911097',
];
- Jeżeli płatność się powiedzie, można korzystając ze zwróconych danych oznaczyć zamówienie w sklepie jako opłacone. W tym celu należy dopisać funkcję setOrderAsComplete, która w oparciu o tablicę danych w $response, wywoła mechanizm sklepowy zmieniający status zamówienia i zapisujący przydatne dane - np. token klienta do ponownego użytku (cli_auth) oraz tytuł transakcji (sale_auth). W przypadku karty z 3DS należy wykonać przekierowanie do bramki 3DS.
- Na wypadek wprowadzenia niepoprawnych danych karty lub niewystarczających środków na koncie, warto dać klientowi drugą szansę i wygenerować dla niego kolejną transakcję metodą register_sale. Metoda ta różni się od secure sale tylko tym, że dane karty płatniczej są podawane w panelu transakcyjnym, a nie na stronie sklepu.
W tym celu zmodyfikujemy funkcję makeCardPayment() oraz dodamy funkcję tryToSaleAgain():
private function tryToSaleAgain()
{
//Spróbuj utworzyć nową transakcję i przekierować klienta do panelu Tpay
$response = $this->makeCardPayment(true);
if (isset($response['sale_auth'])) {
header("Location: " . 'https://secure.tpay.com/cards/?sale_auth=' . $response['sale_auth']);
} else {
echo $response['err_desc'];
}
}
private function makeCardPayment($failOver = false)
{
$cardData = Util::post('carddata', FieldsConfigDictionary::STRING);
$clientName = Util::post('client_name', FieldsConfigDictionary::STRING);
$clientEmail = Util::post('client_email', FieldsConfigDictionary::STRING);
$saveCard = Util::post('card_save', FieldsConfigDictionary::STRING);
Util::log('Secure Sale post params', print_r($_POST, true));
if ($saveCard === 'on') {
$this->setOneTimer(false);
}
$this->setAmount(123)->setCurrency(985)->setOrderID('test payment 123');
$this->setLanguage('en')->setReturnUrls('https://tpay.com', 'https://google.pl');
return $failOver === false ?
$this->registerSale($clientName, $clientEmail, 'test sale', $cardData) :
$this->setCardData(null)->registerSale($clientName, $clientEmail, 'test sale');
}
- W ostatnim kroku należy zmodyfikować sterowanie funkcjami klasy, układając je w logiczny ciąg:
public function init()
{
if (empty($_POST)) {
//Wyświetl formularz jeśli nie przesłano żadnych danych
$this->showCardDirectHtml();
} else {
//Wykonaj próbę zapłaty
$response = $this->makeCardPayment();
if (isset($response['result']) && (int)$response['result'] === 1) {
//Płatność kartą bez 3DS się powiodła
$this->setOrderAsComplete($response);
} elseif(isset($response['3ds_url'])) {
//Karta ma zabezpieczenie 3DS, wykonaj przekierowanie do strony 3DS
header("Location: " . $response['3ds_url']);
} else {
//Wprowadzono niepoprawne dane karty
$this->tryToSaleAgain();
}
}
}
- Cały plik można wykonać dodając poza klasą linijkę:
(new CardGate())->init();
- Pamiętaj, że w przypadku płatności wymagającej przekierowania do panelu transakcyjnego Tpay lub bramki 3DS, powiadomienie o poprawnym opłaceniu zostanie wysłane do Twojego sklepu, dopiero po finalizacji transakcji.
W celu odebrania takiego powiadomienia, utwórz plik CardNotification.php, rozszerzający klasę CardNotificationHandler. W panelu odbiorcy płatności należy podać adres URL prowadzący do tego pliku zgodnie z dokumentacją.
Dodaj konstruktor nowej klasy:
<?php
namespace tpayLibs\examples;
use tpayLibs\src\_class_tpay\Notifications\CardNotificationHandler;
include_once 'config.php';
include_once 'loader.php';
class CardNotification extends CardNotificationHandler
{
public function __construct()
{
$this->cardApiKey = 'bda5eda723bf1ae71a82e90a249803d3f852248d';
$this->cardApiPass = 'IhZVgraNcZoWPLgA';
$this->cardKeyRSA = 'LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0NCk1JR2ZNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0R05BRENCaVFLQmdRQ2NLRTVZNU1Wemd5a1Z5ODNMS1NTTFlEMEVrU2xadTRVZm1STS8NCmM5L0NtMENuVDM2ekU0L2dMRzBSYzQwODRHNmIzU3l5NVpvZ1kwQXFOVU5vUEptUUZGVyswdXJacU8yNFRCQkxCcU10TTVYSllDaVQNCmVpNkx3RUIyNnpPOFZocW9SK0tiRS92K1l1YlFhNGQ0cWtHU0IzeHBhSUJncllrT2o0aFJDOXk0WXdJREFRQUINCi0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQ';
$this->cardVerificationCode = '6680181602d396e640cb091ea5418171';
$this->cardHashAlg = 'sha1';
parent::__construct();
}
}
- Następnie dodaj wywołanie funkcji sprawdzającej powiadomienie systemowe (wbudowanej w bibliotekę Tpay) oraz funkcję getOrderDetailsFromDatabase, która zwróci kwotę i walutę zamówienia zapisane w sklepie, na potrzeby walidacji powiadomienia:
private function getTpayNotification()
{
//Jeżeli chcesz wyłączyć sprawdzanie adresu IP serwera Tpay, wykonaj tą komendę:
$this->disableValidationServerIP();
//Jeżeli korzystasz z proxy, wykonaj tą komendę aby sprawdzić adres IP w tablicy HTTP_X_FORWARDED_FOR:
$this->enableForwardedIPValidation();
$notification = $this->handleNotification();
//Pobierz szczegóły zamówienia z bazy danych sklepu
$shopOrderData = $this->getOrderDetailsFromDatabase($notification['order_id']);
//Sprawdź poprawność podpisu powiadomienia
$this
->setAmount($shopOrderData['amount'])
->setCurrency($shopOrderData['currency'])
->setOrderID($notification['order_id']);
$this->validateCardSign($notification['sign'], $notification['sale_auth'],
$notification['card'], $notification['date'], $notification['status']);
return $notification;
}
private function getOrderDetailsFromDatabase($orderId)
{
//Zaprogramuj pobranie szczegółów zamówienia z bazy sklepu w oparciu o OrderId
//Przykładowe dane na potrzeby przykładu
return [
'amount' => 123.00,
'currency' => 985,
];
}
- Dodaj funkcję sterującą klasą init() oraz własną funkcję setOrderAsConfirmed(), która w oparciu o zwrócone dane oznaczy zamówienie jako opłacone oraz zapisze przydatne dane - np. token klienta do ponownego użytku (cli_auth) oraz tytuł transakcji (sale_auth).
public function init()
{
$notification = $this->getTpayNotification();
if (isset($notification['status']) && $notification['status'] === 'correct') {
$this->setOrderAsConfirmed($notification);
}
}
Zmienna $notification będzie zawierała tablicę danych, opisanych w dokumentacji.
- Dodaj wywołanie nowo stworzonej klasy na jej końcu:
(new CardNotification())->init();