Credit card payments are becoming more and more popular in the e-commerce market

This tutorial help you understand the mechanism of this type of payment and help you implement it correctly on your own website. This method can also be used to save a customer card by generating a token that will later bind the credit card to the merchant account and allow payment without the need to re-enter the data.

The mechanism of card payment through the integrated gateway

Due to the need to maximize the security of the entered card data, this payment method requires the implementation of additional security on the store's website and is one of the most advanced integrations.

  1. First of all, you should add the option of choosing a card payment method for your clients by, for example, displaying an additional "Quick credit card payment" button, e.g.
  2. After selecting this payment method, you should display the form to the customer in which he will be able to enter card details and read the Tpay.com regulations.
  3. After entering the data, the action of checking the correctness of the card number can be performed through a syntax checking script. Each card number is generated based on a specific scheme and these schemes are publicly available. At this point, you should also recognize whether the data entered are supported by our system. We currently accept payments by VISA and MasterCard.
  4. After entering the supported data and clicking "Place an order", you must perform data encryption before further sending.
  5. Tpay.com provides ready to use programming libraries in PHP language, which support all these actions automatically and do not require a lot of developers' work. All you need to do is adjust the configuration code to your needs.
  6. The further process depends on 3D Secure protection on the customer card:
    1. Card with 3DS
      1. Payment with a secured card will require the client to be redirected to the so-called 3DS gates, where he will have to confirm the payment in a manner dependent on his bank (eg by entering a one-time SMS code).
      2. After successful payment, the customer will be redirected to the store's success page.
      3. The notification of payment will be sent to the store's address, configured in the seller's account settings.
    2. Card without 3DS
      1. The attempt to collect funds will be made immediately after placing the order, and information about the success or failure will be returned immediately.
      2. In this situation, there is no need to redirect to external websites, and the returned data clearly indicate the attribution of funds to the seller's account.
      3. If the payment fails, allow the customer to try again with another card.

Technical explanation of card payment on the store's website

  1. The card transaction is carried out by sending the API request. Access data such as a key, password, verification code and RSA public key are required to start the integration and should be generated in accordance with this instruction.
  2. To display the scripted form, we recommend using a ready to use PHP library as in this example.
  3. When placing the order, the merchant system should already have the customer's details, such as email address, name and surname, which will be required to prepare the transaction.
  4. Before submitting the form, the following information must be encrypted:
    • card number,
    • CVV code,
    • expiration date,
    with the RSA public key in accordance with the guidelines described in the securesale method description.
  5. Then clear all inputs and leave only one with encrypted string. This operation should take place on the client's side, in order to avoid any forwarding of the customer's card data in an open form. An example of a JS script that supports such encryption can be found in Tpay library at github.com.
    The Tpay library contains a form, that performs these activities automatically!
  6. The second step is to call the securesale method, which is used to send the necessary data to the Tpay server.
    An example of this method can be found on the github site in PHP tpay-com library, and you can also do a test call by clicking "Try" in the documentation.
    The minimum set of parameters to be sent in this method is:
    {
      "name": "john doe",
      "email": "[email protected]",
      "desc": "payment for order xyz",
      "amount": 10.99,
      "api_password": "XtCns9OAue8zSFJ",
      "sign": "a0be2d6884ce71265e38faa71a84228676784d81",
      "currency": 985,
      "card": "SrKhV6VEPiaw4cQEM2Mr0Nj7fuVhRdAdQ9Bk+sv7iM/St12s/W4zRNyqVptN3KIVMqr6coRXNfSPF/o/N9pLy94koY255N5U8wc/ApsBVyo7tlV7xQDxM+I5ksZ3f11qhxzIsk8inmzQGJywrg9CGvZAoiHazgDSVRuy7Tj4vbk="
    }​

    The additional "onetimer" parameter decides if to save the payment card. If this method is not to be used to memorize the payment card, you should always send the "onetimer" parameter within request body. 
    The card can be used to implement recursive payments. The saved token will be returned in the "cli_auth" parameter, in the system notification or in the response to the API call (depending on having 3D Secure protection).

  7. The further process depends on 3D Secure on customer card:
    1. A card with 3DS:
      1. The returned parameter "3ds_url" will contain a link to the 3DS gateway, to which the client should be redirected in order to complete the 3DS validation in his bank.
      2. After successful payment confirmation by the client, an asynchronous payment notification will be sent to the server result address (defined in the account settings), just like in the case of regular bank transfers.
        Read the documentationdescribing card transaction notifications. On github.com, we've provided a ready example that automatically verifies the notification and returns its validated content.
      3. The URL to which the payer will be redirected after payment confirmation can be defined statically in the seller's account settings or sent dynamically within the request body parameters.
    2. A card without 3DS:
      1. The result of the charge will be returned immediately and if successful, should be considered binding.
      2. In the case of failure, the reason for the transaction rejection that can be referred to this table will be returned in the "reason" parameter.

Example of implementation based on PHP Tpay libraries

  1. Download the Tpay libraries from Github.com
  2. Create a file (eg in the examples folder) extending the PaymentCardForms class. In the constructor enter your API access data:
    <?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();
        }
    }​
  3. Add a public control function in which we make the payment gateway available on the store's website by calling the getOnSiteCardForm() method:
    public function init()
    {
        if (empty($_POST)) {
            echo $this->getOnSiteCardForm('CardGate.php');
        }
    }

    The input parameter of getOnSiteCardForm($url) must contain the name of the file, which will collect the data from the form and make the payment attempt. In this example, it will be the same file that we edit.

  4. Add a function that receives data from the form and executes a request to the Tpay server:
    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);
    }​

    Some data should be downloaded from the database (amount, currency, etc.), in this example, we have provided sample data.

  5. Performing the MakeCardPayment() function will return a result, which may contain a link to the 3DS gateway or successful sale data (depending on the 3DS protection on the customer's card). 
    An example set of parameters returned by API for cards without 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',
    ];​
  6. If the payment is successful, you can use that response data to mark the order as paid. To do this, add the setOrderAsComplete function, which, based on the $response array, will trigger a store mechanism that changes the status of the order and saves useful data - e.g. a reuseable client token (cli_auth) and a transaction title (sale_auth). In a case of a card with 3DS, you should redirect the customer to URL address provided in $response.
  7. In case of entering incorrect card details or insufficient funds on the account, it is worth giving the customer a second chance and generating another transaction for him by the register_sale method. This method differs from secure sale only in that the payment card data is provided in the Tpay transaction panel, not on the store's website. 
    For this purpose, we will modify the function makeCardPayment() and add the function tryToSaleAgain():
    private function tryToSaleAgain()
    {
        //Try to create new transaction and redirect customer to Tpay transaction panel
        $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');
    }
  8. In the last step you should modify the control of the class functions by putting them in a logical sequence:
    public function init()
    {
       if (empty($_POST)) {
           //Show new payment form
           $this->showCardDirectHtml();
       } else {
           //Try to sale with provided card data
           $response = $this->makeCardPayment();
           if (isset($response['result']) && (int)$response['result'] === 1) {
               //Successful payment by card not protected by 3DS
               $this->setOrderAsComplete($response);
           } elseif(isset($response['3ds_url'])) {
               //Successfully generated 3DS link for payment authorization
               header("Location: " . $response['3ds_url']);
           } else {
               //Invalid credit card data
               $this->tryToSaleAgain();
           }
       }
    }​
  9. The entire file can be run by adding a line outside the class:
    (new CardGate())->init();​
  10. Remember that in the case of a payment requiring redirection to the Tpay transaction panel or 3DS gateway, a notification about the correct payment will be sent to your store only after the transaction has been successfully finalized. 
    To receive such a notification, create a CardNotification.php file that extends the CardNotificationHandler class. In the merchant panel, enter the URL address leading to this file in accordance with the documentation.
    Add a new class constructor:
    <?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();
        }
    
    }​
  11. Then add the function call checking the system notification (built into the Tpay library) and the function getOrderDetailsFromDatabase, which will return the amount and currency of the order stored in the DB for validation purpose:
    private function getTpayNotification()
    {
        //If you want to disable server IP validation, run this command (not recommended):
        $this->disableValidationServerIP();
        //If you use proxy communication and want to check for Tpay server IP at HTTP_X_FORWARDED_FOR, fun this command:
        $this->enableForwardedIPValidation();
        //Check Tpay server IP and validate parameters
        $notification =  $this->handleNotification();
        //Get order details from your DB
        $shopOrderData = $this->getOrderDetailsFromDatabase($notification['order_id']);
        //Validate notification sign correctness
        $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)
    {
        //Code getting order amount and currency from your DB
        //This is an example of returned values
        return [
            'amount' => 123.00,
            'currency' => 985,
        ];
    }
  12. Add the init() class control function and your own setOrderAsConfirmed() function, which based on the returned data will mark the order as paid and save useful data - e.g. a reused client token (cli_auth) and a transaction title (sale_auth).
    public function init()
    {
        $notification = $this->getTpayNotification();
        if (isset($notification['status']) && $notification['status'] === 'correct') {
            $this->setOrderAsConfirmed($notification);
        }
    }​

    The $notification variable will contain an array of data described in the documentation.

  13. Add the call to the newly created class at the end of file:
    (new CardNotification())->init();​