System płatności Tpay oferuje metody pozwalające na zapisanie karty poprzedzone jej pełną weryfikacją i pobranie z niej środków.

System depozytowy może polegać na pobraniu środków z zapisanej lub nowej karty, a następnie zwróceniu części lub całości środków. W zależności od potrzeb można wdrożyć płatności jednorazowe lub zapisywanie karty do ponownego użytku.

System zapamiętywania kart pozwala na ułatwienie procesu pobierania środków z karty klienta w sposób bezpieczny, gdyż pierwsza transakcja prowadząca do zapisania środków poddawana jest weryfikacji przez system 3D Secure.

Przed implementacją odpowiedniego rozwiązania należy zdecydować, czy płatnik ma wprowadzać dane karty na stronie sklepu czy w panelu transakcyjnym Tpay. W artykułach Wdrożenie bramki płatności kartami na stronie sklepu oraz Wdrożenie przekierowania do płatności kartą opisano jak w łatwy sposób udostępnić taką możliwość swoim klientom.

Po pobraniu środków w dowolnym momencie można zwrócić część lub całość depozytu posługując się prostym systemem zwrotów przez API Tpay. Operacje te mogą być inicjowane ręcznie lub w pełni zautomatyzowane, dzięki zastosowaniu komunikacji międzysystemowej - API.

Dodatkowo jeśli zdecydujemy o zapisaniu karty klienta, możemy ją wykorzystać do inicjowania kolejnych płatności lub automatycznych obciążeń w przyszłości. W artykule Wdrożenie płatności rekurencyjnych (cyklicznych) kartami opisano jak w łatwy sposób udostępnić taką możliwość w swoim systemie sprzedażowym.

Przykład wdrożenia systemu depozytowego w oparciu o biblioteki PHP Tpay

Pobranie złotówki weryfikacyjnej i zapisanie karty

  1. Pobierz biblioteki Tpay z serwisu Github.com
  2. 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\TException;
    
    include_once 'config.php';
    include_once 'loader.php';
    
    class CardBasic extends PaymentCardForms
    {
        public function __construct()
        {
            $this->cardApiKey = 'bda5eda723bf1ae71a82e90a249803d3f852248d';
            $this->cardApiPass = 'IhZVgraNcZoWPLgA';
            $this->cardKeyRSA = 'LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0NCk1JR2ZNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0R05BRENCaVFLQmdRQ2NLRTVZNU1Wemd5a1Z5ODNMS1NTTFlEMEVrU2xadTRVZm1STS8NCmM5L0NtMENuVDM2ekU0L2dMRzBSYzQwODRHNmIzU3l5NVpvZ1kwQXFOVU5vUEptUUZGVyswdXJacU8yNFRCQkxCcU10TTVYSllDaVQNCmVpNkx3RUIyNnpPOFZocW9SK0tiRS92K1l1YlFhNGQ0cWtHU0IzeHBhSUJncllrT2o0aFJDOXk0WXdJREFRQUINCi0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQ';
            $this->cardVerificationCode = '6680181602d396e640cb091ea5418171';
            $this->cardHashAlg = 'sha1';
            parent::__construct();
        }
    }​
  3. Dodaj funkcję wykonującą zapytanie do serwera Tpay i przekierowanie do panelu transakcyjnego:
    public function getRedirectTransaction()
    {
        try {
            $config = [
                'name' => 'John Doe',
                'email' => '[email protected]',
                'desc' => 'Card registration payment',
            ];
            $this
                ->setAmount(1.00)
                ->setCurrency(985)
                ->setOrderID(123)
                ->setReturnUrls('https://shop.com/success', 'https://shop.com/error');
            $transaction =  $this->registerSale($config['name'], $config['email'], $config['desc']);
            if (isset($transaction['sale_auth']) === false) {
                throw new TException('Error generating transaction: ' . $transaction['err_desc']);
            }
            $transactionId = $transaction['sale_auth'];
            header("Location: https://secure.tpay.com/cards/?sale_auth=$transactionId");
        } catch (TException $e) {
            echo 'Unable to generate transaction. Reason: ' . $e->getMessage();
        }
    }
  4. Wykonanie funkcji MakeCardPayment() zwróci wynik, zawierający parametr "sale_auth", na który wykonane zostaje przekierowanie klienta.
    Przykładowy zestaw parametrów zwracany przez API:
    $transaction = [
      'result' => '1',
      'sale_auth' => 't59c28295aeb071b0cf6471b24f727f6456998de',
    ];​
  5. Cały plik można wykonać dodając poza klasą linijkę:
    (new CardBasic())->getRedirectTransaction();
  6. Pamiętaj, że 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();
        }
    
    }​
  7. 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'], isset($notification['test_mode']) ? $notification['test_mode'] : '');
    
        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' => 1.00,
            'currency' => 985,
        ];
    }
  8. 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.

  9. Dodaj wywołanie nowo stworzonej klasy na jej końcu:
    (new CardNotification())->init();​​

Wykonując powyższe kroki uzsykujemy:

  • Automatyczny formularz kierujący do płatności weryfikacyjnej na kwotę 1 PLN
  • Obsłużenie odebrania informacji o poprawnej płatności
  • Odebranie i zapisanie wygenerowanego tokenu karty płatniczej klienta, który będzie wykorzystywany do dalszych obciążeń

 Automatyczny zwrot pobranej opłaty rejestracyjnej

  1. Rozszerz funkcję aktualizującą status zamówienia tak, aby po prawidłowym wykonaniu, uruchomiła funkcję zwracającą zapłaconą złotówkę rejestracyjną, zachowując przy tym zapisanie karty:
    private function setOrderAsConfirmed($params)
    {
        //update your order status
        //save transaction ID (sale_auth) and if exists, client token (cli_auth) for later use
    
        //if the transaction was processed only to register customer card, you can make automatic refund:
        $this->refundPayment($params['sale_auth'], 'Transaction refund');
    }​
  2. Dopisz funkcję, która wywoła instancję klasy odpowiadającej za zwroty:
    private function refundPayment($transactionId, $refundDescription)
    {
        $RefundClass = new CardRefundExample();
        $RefundClass->refund($transactionId, $refundDescription);
    }​
  3. Oraz stwórz kod samej klasy w osobnym pliku:
    <?php
    namespace tpayLibs\examples;
    
    use tpayLibs\src\_class_tpay\Refunds\CardRefunds;
    
    include_once 'config.php';
    include_once 'loader.php';
    
    class CardRefundExample extends CardRefunds
    {
        public function __construct()
        {
            //This is pre-configured sandbox access. You should use your own data in production mode.
            $this->cardApiKey = 'bda5eda723bf1ae71a82e90a249803d3f852248d';
            $this->cardApiPass = 'IhZVgraNcZoWPLgA';
            $this->cardKeyRSA = 'LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0NCk1JR2ZNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0R05BRENCaVFLQmdRQ2NLRTVZNU1Wemd5a1Z5ODNMS1NTTFlEMEVrU2xadTRVZm1STS8NCmM5L0NtMENuVDM2ekU0L2dMRzBSYzQwODRHNmIzU3l5NVpvZ1kwQXFOVU5vUEptUUZGVyswdXJacU8yNFRCQkxCcU10TTVYSllDaVQNCmVpNkx3RUIyNnpPOFZocW9SK0tiRS92K1l1YlFhNGQ0cWtHU0IzeHBhSUJncllrT2o0aFJDOXk0WXdJREFRQUINCi0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQ';
            $this->cardVerificationCode = '6680181602d396e640cb091ea5418171';
            $this->cardHashAlg = 'sha1';
            parent::__construct();
        }
    
    public function refund($transactionId, $refundDescription, $refundAmount = null)
    {
        //If refund amount is empty, the method call will refund full transaction amount
        if (!is_null($refundAmount)) {
            $this->setAmount($refundAmount);
        }
        $result = parent::refund($transactionId, $refundDescription);
    
        return $result['result'] === 1;
    }
    
    }
    ​
  4. Po wykonaniu powyższego kodu, transakcje rejestracyjne na kwotę 1 PLN będą automatycznie zwracane klientom po poprawnym przeprocesowaniu, a w Twojej bazie danych pozostanie zapisana karta klienta. Wykorzystamy ją do pobrania depozytu w kolejnym rozdziale.

Pobranie depozytu z zapisanej karty klienta

W poprzednich rozdziałach zapisaliśmy kartę klienta oraz zwróciliśmy zapłaconą przez niego złotówkę weryfikacyjną. Dysponując teraz tokenem, reprezentującym jego kartę w systemie Tpay, pobierzemy teraz 25 PLN jako depozyt.

  1. Stwórz plik RecurrentPayment.php (np. w folderze examples) rozszerzający klasę PaymentCard, w którego konstruktorze podaj swoje dane dostępowe do API kart płatniczych:
    <?php
    
    namespace tpayLibs\examples;
    
    use tpayLibs\src\_class_tpay\PaymentCard;
    
    include_once 'config.php';
    include_once 'loader.php';
    
    class RecurrentPayment extends PaymentCard
    {
        private $transactionId = null;
    
        public function __construct()
        {
            $this->cardApiKey = 'bda5eda723bf1ae71a82e90a249803d3f852248d';
            $this->cardApiPass = 'IhZVgraNcZoWPLgA';
            $this->cardKeyRSA = 'LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0NCk1JR2ZNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0R05BRENCaVFLQmdRQ2NLRTVZNU1Wemd5a1Z5ODNMS1NTTFlEMEVrU2xadTRVZm1STS8NCmM5L0NtMENuVDM2ekU0L2dMRzBSYzQwODRHNmIzU3l5NVpvZ1kwQXFOVU5vUEptUUZGVyswdXJacU8yNFRCQkxCcU10TTVYSllDaVQNCmVpNkx3RUIyNnpPOFZocW9SK0tiRS92K1l1YlFhNGQ0cWtHU0IzeHBhSUJncllrT2o0aFJDOXk0WXdJREFRQUINCi0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQ';
            $this->cardVerificationCode = '6680181602d396e640cb091ea5418171';
            $this->cardHashAlg = 'sha1';
            parent::__construct();
        }
    
    }​
  2. Utwórz funkcję sterującą, która wprowadzi podstawowe dane transakcji oraz przygotuje nieopłaconą transakcję metodą presale. W tym miejscu zakładamy, że przekazywany będzie Token klienta pobrany z bazy danych. Nasz testowy token ma wartość 't5a96d292cd0a5c63a14c30adeae55cb200df087': 
    public function init(
            $saleDescription,
            $clientToken,
            $amount,
            $orderId = null,
            $currency = 985,
            $language = 'pl'
        ) {
            $this
                ->setAmount($amount)
                ->setCurrency($currency)
                ->setOrderID($orderId)
                ->setLanguage($language)
                ->setClientToken($clientToken);
            //Przygotuj nieopłaconą transakcję
            $transaction = $this->presaleMethod($saleDescription);
            $this->transactionId = $transaction['sale_auth'];
    
            return $this;
    }
  3. Utwórz funkcję, która będzie próbowała wyegzekwować obciążenie oraz własną funkcję setOrderAsConfirmed, która zaktualizuje status transakcji w Twojej bazie danych:
    public function payBySavedCreditCard()
    {
        //Spróbuj wykonać obciążenie
        //W trybie testowym ta metoda ma 50% szans powodzenia
        $result = $this->saleMethod($this->transactionId);
        if (isset($result['status']) && $result['status'] === 'correct') {
            //Obciążenie się udało, oznacz zamówienie jako opłacone
            return $this->setOrderAsConfirmed();
        } else {
            //Płatność tą kartą została odrzucona, warto zalogować przyczynę odrzucenia
            return $result['reason'];
        }
    }​
  4. Na końcu pliku, poza klasą wystarczy dodać wywołanie z parametrami wejściowymi, np. żeby pobrać 25,00 PLN z zapamiętanej karty klienta wystarczy wywołać:
    (new RecurrentPayment())
        ->init('Deposit founds', 't5a96d292cd0a5c63a14c30adeae55cb200df087', 25.00, 'order_123456', 985, 'pl')
        ->payBySavedCreditCard();​

Po poprawnym wykonaniu powyższego kodu, z karty klienta zostanie pobrana kwota 25 PLN.

Do zwrócenia częsci depozytu można posłużyć się utworzoną wcześniej klasą CardRefundExample, deklarując dodatkowo kwotę zwrotu:

$RefundClass = new CardRefundExample();
$RefundClass->refund($transactionId, $refundDescription, 10.00);