Seamless Checkout


The Connect2Pay Seamless Checkout is a JavaScript library to be included in the merchants checkout page’s HTML. It renders a responsive payment form for seamlessly passing customer’s payment information into Connect2Pay server and process the payment. The objective of this library is to allow merchants to capture user credit card information in the client-side and pass it directly to Connect2Pay API without breaking PCI-DSS standards.




1 - Create token

To be able to use the Seamless Checkout you need to make a Prepare API call and retrieve the customerToken from the API response:

We strongly advise the use of parameter ctrlCallbackURL.


    use PayXpert\Connect2Pay\Connect2PayClient;
    use PayXpert\Connect2Pay\containers\request\PaymentPrepareRequest;
    use PayXpert\Connect2Pay\containers\Order;
    use PayXpert\Connect2Pay\containers\Shipping;
    use PayXpert\Connect2Pay\containers\Shopper;
    use PayXpert\Connect2Pay\containers\Account;
    use PayXpert\Connect2Pay\containers\constant\OrderShippingType;
    use PayXpert\Connect2Pay\containers\constant\OrderType;
    use PayXpert\Connect2Pay\containers\constant\PaymentMethod;
    use PayXpert\Connect2Pay\containers\constant\PaymentMode;
    use PayXpert\Connect2Pay\containers\constant\AccountAge;
    use PayXpert\Connect2Pay\containers\constant\AccountLastChange;
    use PayXpert\Connect2Pay\containers\constant\AccountPaymentMeanAge;

    $connect2pay = "https://connect2.payxpert.com";
    // This will be provided once your account is approved
    $originator  = "000000";
    $password    = "Gr3atPassw0rd!";

    $c2pClient = new Connect2PayClient($connect2pay, $originator, $password);

    $prepareRequest = new PaymentPrepareRequest();
    $shopper = new Shopper();
    $account = new Account();
    $order = new Order();
    $shipping = new Shipping();

    // Set all information for the payment
    $prepareRequest->setPaymentMethod(PaymentMethod::CREDIT_CARD);
    $prepareRequest->setPaymentMode(PaymentMode::SINGLE);
    // To charge €25.99
    $prepareRequest->setCurrency("EUR");
    $prepareRequest->setAmount(2599);
    // Extra custom data that are returned with the payment status
    $prepareRequest->setCtrlCustomData("Give that back to me please!!");
    // Where the customer will be redirected after the payment
    $prepareRequest->setCtrlRedirectURL("https://merchant.example.com/payment/redirect");
    // URL on the merchant site that will receive the callback notification
    $prepareRequest->setCtrlCallbackURL("https://merchant.example.com/payment/callback");

    $order->setId("ABC-123456");
    $order->setShippingType(OrderShippingType::DIGITAL_GOODS);
    $order->setType(OrderType::GOODS_SERVICE);
    $order->setDescription("Payment of €25.99");

    $shopper->setId("1234567WX");
    $shopper->setFirstName("John")->setLastName("Doe");
    $shopper->setAddress1("Debit Street, 45");
    $shopper->setZipcode("3456TRG")->setCity("New York")->setState("NY")->setCountryCode("US");
    $shopper->setHomePhonePrefix("212")->setHomePhone("12345678");
    $shopper->setEmail("shopper@example.com");

    $account->setAge(AccountAge::LESS_30_DAYS);
    $account->setDate("20210106");
    $account->setLastChange(AccountLastChange::LESS_30_DAYS);
    $account->setLastChangeDate("20210106");
    $account->setPaymentMeanAge(AccountPaymentMeanAge::LESS_30_DAYS);
    $account->setPaymentMeanDate("20210106");
    // Set 'true' to trigger SCA challenge
    $account->setSuspicious(false);

    $shipping->setName("Lady Gogo");
    $shipping->setAddress1("125 Main Street");
    $shipping->setZipcode("ABC-5678")->setState("NY")->setCity("New York")->setCountryCode("US");
    $shipping->setPhone("+47123456789");

    $shopper->setAccount($account);
    $prepareRequest->setShopper($shopper);
    $prepareRequest->setOrder($order);
    $prepareRequest->setShipping($shipping);

    $result = $c2pClient->preparePayment($prepareRequest);
    if ($result !== false) {
        // The customer token info returned by the payment page could be saved in session (may
        // be used later when the customer will be redirected from the payment page)
        $_SESSION['merchantToken'] = $result->getMerchantToken();
        
        // The merchantToken must also be used later to validate the callback to avoid that anyone
        // could call it and abusively validate the payment. It may be stored in local database for this.
        
        // Now redirect the customer to the payment page
        header('Location: ' . $c2pClient->getCustomerRedirectURL($result));
    } else {
        echo "Payment preparation error occurred: " . $c2pClient->getClientErrorMessage() . "\n";
    }


    use PayXpert\Connect2Pay\Connect2PayClient;

    $url = "https://connect2.payxpert.com";
    // This will be provided once your account is approved
    $originator  = "000000";
    $password    = "Gr3atPassw0rd!";

    $c2pClient = new Connect2PayClient($url, $originator, $password);

    // Set all information for the payment
    $c2pClient->setOrderID("ABC-123456");
    $c2pClient->setPaymentMethod(Connect2PayClient::PAYMENT_METHOD_CREDITCARD); // or PAYMENT_METHOD_DIRECTDEBIT for SEPA, for example
    $c2pClient->setPaymentMode(Connect2PayClient::PAYMENT_MODE_SINGLE);
    $c2pClient->setShopperID("1234567WX");
    $c2pClient->setShippingType(Connect2PayClient::SHIPPING_TYPE_VIRTUAL);
    // To charge €25.99
    $c2pClient->setCurrency("EUR");
    $c2pClient->setAmount(2599);
    $c2pClient->setOrderDescription("Payment of €25.99");
    $c2pClient->setShopperFirstName("John");
    $c2pClient->setShopperLastName("Doe");
    $c2pClient->setShopperAddress("NA");
    $c2pClient->setShopperZipcode("NA");
    $c2pClient->setShopperCity("NA");
    $c2pClient->setShopperCountryCode("GB");
    $c2pClient->setShopperPhone("+4712345678");
    $c2pClient->setShopperEmail("shopper@example.com");
    // Extra custom data that are returned with the payment status
    $c2pClient->setCtrlCustomData("Give that back to me please !!");
    // Where the customer will be redirected after the payment
    $c2pClient->setCtrlRedirectURL("https://merchant.example.com/payment/redirect");
    // URL on the merchant site that will receive the callback notification
    $c2pClient->setCtrlCallbackURL("https://merchant.example.com/payment/callback");

    if ($c2pClient->validate()) {
      if ($c2pClient->preparePayment()) {
        // The customer token info returned by the payment page could be saved in session (may
        // be used later when the customer will be redirected from the payment page)
        $_SESSION['merchantToken'] = $c2pClient->getMerchantToken();

        // The merchantToken must also be used later to validate the callback to avoid that anyone
        // could call it and abusively validate the payment. It may be stored in local database for this.

        // Now redirect the customer to the payment page
        header('Location: ' . $c2pClient->getCustomerRedirectURL());
      } else {
        echo "error prepareTransaction: ";
        echo $c2pClient->getClientErrorMessage() . "\n";
      }
    } else {
      echo "Validation error occurred: " . $c2pClient->getClientErrorMessage() . "\n";
    }
import com.payxpert.connect2pay.client.containers.Account;
import com.payxpert.connect2pay.client.containers.Order;
import com.payxpert.connect2pay.client.containers.Shipping;
import com.payxpert.connect2pay.client.containers.Shopper;
import com.payxpert.connect2pay.client.requests.PaymentRequest;
import com.payxpert.connect2pay.constants.PaymentMode;
import com.payxpert.connect2pay.constants.sca.ShippingType;
import com.payxpert.connect2pay.constants.sca.ShopperAccountAge;
import com.payxpert.connect2pay.constants.sca.ShopperAccountLastChange;

class TestPayment {
  public void processPayment() {
    // Process a payment of €39.99
    PaymentRequest request = new PaymentRequest();
    request.setCurrency("EUR").setAmount(3999);
    request.setPaymentMode(PaymentMode.SINGLE);
    // User will be redirected here when clicking on "Go back to merchant" button
    request.setCtrlRedirectURL("http://my.website/somewhere");
    // The payment page will do a callback on this URL after the payment is processed
    request.setCtrlCallbackURL("http://my.website/payment-callback");

    Order order = new Order();
    order.setId("1234ABCD").setShippingType(ShippingType.DIGITAL_GOODS);
    Shopper shopper = new Shopper();
    shopper.setEmail("test@example.com").setFirstName("John").setLastName("Doe")
        .setHomePhonePrefix("47").setHomePhone("123456789").setAddress1("Main Street 41")
        .setZipcode("123456").setCity("London").setCountryCode("GB");
    Account account = new Account();
    account.setAge(ShopperAccountAge.BETWEEN_30_60_DAYS).setSuspicious(false)
        .setLastChange(ShopperAccountLastChange.BETWEEN_30_60_DAYS);
    shopper.setAccount(account);
    Shipping shipping = new Shipping();
    shipping.setName("Jane Doe").setAddress1("Other Street, 54").setCity("London")
        .setZipcode("654321").setCountryCode("GB");

    request.setOrder(order);
    request.setShopper(shopper);
    request.setShipping(shipping);

    // Validate the request
    try {
      request.validate();
    } catch (BadRequestException e) {
      logger.error("Ooops, an error occurred validating the payment request: " + e.getMessage());
      // Handle the error...
    }

    // Instantiate the client and send the prepare request
    // Second argument is the originator ID, third one is the associated API key
    Connect2payClient c2p = new Connect2payClient("https://provided.url", "123456", "GreatP4ssw0rd");
    PaymentResponse response = null;
    try {
      response = c2p.preparePayment(request);
    } catch (Exception e) {
      logger.error("Ooops, an error occurred preparing the payment: " + e.getMessage());
      // Handle the error...
    }

    if (response != null && ResultCode.SUCCESS.equals(response.getCode())) {
      // Get the URL to redirect the user to
      String redirectURL = response.getCustomerRedirectURL();
      if (redirectURL != null) {
        // Redirect the user towards this URL, this will display the payment page
      }
    } else {
      // Handle the failure
    }
  }
}

  // Process a payment of €39.99
  PaymentRequest request = new PaymentRequest();
  request.setOrderId("1234ABCD").setCurrency("EUR").setAmount(3999);
  request.setShippingType(ShippingType.VIRTUAL);
  request.setPaymentMode(PaymentMode.SINGLE);
  // User will be redirected here when clicking on "Go back to merchant" button
  request.setCtrlRedirectURL("http://my.website/somewhere");
  // The payment page will do a callback on this URL after the payment is processed
  request.setCtrlCallbackURL("http://my.website/payment-callback");

  // Validate the request
  try {
    request.validate();
  } catch (BadRequestException e) {
    logger.error("Ooops, an error occurred validating the payment request: " + e.getMessage());
    // Handle the error...
  }

    // Instantiate the client and send the prepare request
    // Second argument is the originator ID, third one is the associated API key
    Connect2payClient c2p = new Connect2payClient("https://connect2.payxpert.com", "123456", "GreatP4ssw0rd");
    PaymentResponse response = null;
    try {
      response = c2p.preparePayment(request);
    } catch (Exception e) {
      logger.error("Ooops, an error occurred preparing the payment: " + e.getMessage());
      // Handle the error...
    }

    if (response != null && ResultCode.SUCCESS.equals(response.getCode()) {
      // Get the URL to redirect the user to
      String redirectURL = response.getCustomerRedirectURL();
      if (redirectURL != null) {
        // Redirect the user towards this URL, this will display the payment page
      }
    } else {
      // Handle the failure
    }

const paymentPage = require("payxpert")("123456", "GreatP4ssw0rd").connect2pay;

const body = {
    "shippingType": "virtual",
    "paymentMode": "single",
    "amount":500,
    "currency":"EUR",
    "orderID":"NODEJS TEST",
    "ctrlRedirectURL":"http://my.website/somewhere",
    "ctrlCallbackURL":"http://my.website/payment-callback"
};

const responseCreatePayment = await paymentPage.createPayment(body);

if (responseCreatePayment.code == "200") {
    // Success
}


var client = new Connect2PayClient(OriginatorConfig.ORIGINATOR_ID, OriginatorConfig.ORIGINATOR_PASSWORD);

var request = client.NewRequestCreatePayment();

request.Data.orderID = "ABC-123456";
request.Data.paymentMethod = PaymentMethod.CREDIT_CARD;
request.Data.paymentMode = PaymentMode.SINGLE;
request.Data.shopperID = "RICH_SHOPPER";
request.Data.shippingType = ShippingType.VIRTUAL;
request.Data.operation = Operation.SALE;

request.Data.amount = 1500;  // 15 EUR
request.Data.currency = "EUR";

request.Data.orderDescription = "Payment of €15.00";

request.Data.shopperFirstName = "RICH";
request.Data.shopperLastName = "SHOPPER";
request.Data.shopperAddress = "NA";
request.Data.shopperZipcode = "999111";
request.Data.shopperCity = "NA";
request.Data.shopperCountryCode = "GB";
request.Data.shopperPhone = "123-456";
request.Data.shopperEmail = "test@test.test";

request.Data.ctrlCustomData = "Give that back to me please !!";
request.Data.ctrlRedirectURL = "https://merchant.example.com/payment/redirect";
request.Data.ctrlCallbackURL = "https://merchant.example.com/payment/callback";

if (request.Data.Validate())
{
    var response = await request.Send();

    if (response.IsSuccessfull())
    {
        Console.WriteLine("Sale operation executed successfully");
        Console.WriteLine("Merchant token: " + response.merchantToken);
        Console.WriteLine("Customer redirect URL: " + request.getCustomerRedirectURL());
    }
}

  • Response:
{
    "code": "200",
    "message": "Request processed successfully",
    "customerToken": "1234",
    "merchantToken": "4321"
}

2 - Prepare your HTML

In your checkout page you should include a div element with a specified attribute id.

<div id="payment-container">
</div>


Outside the div container, include a script tag with source to our Seamless Checkout library, using the customerToken you have received from step 1. You should add the attributes crossorigin=”anonymous”, “integrity=sha384…”, async=true and data-mount-in={DIV CONTAINER ID}

<div id="payment-container">
</div>

<script async="true" src="https://connect2.payxpert.com/payment/{customerToken}/connect2pay-seamless-v1.5.0.js" data-mount-in="#payment-container" 
integrity="sha384-0IS2bunsGTTsco/UMCa56QRukMlq2pEcjUPMejy6WspCmLpGmsD3z0CmF5LQHF5X" crossorigin="anonymous"></script>


Voilà! The credit card form is instantly loaded and rendered into your checkout page.

3 - Process callback

Once payment is successful, your server will receive a server-side notification with a JSON object and all the information regarding the payment. This is why it is very important to use the ctrlCallbackURL parameter in the token creation step!


Client-side callback

Seamless Checkout can trigger a specified callback function and inject the paymentStatus object as parameter.

NEVER use this method for payment confirmation of the order, since the object can be hijacked and values can be modified on the client-side.

ALWAYS rely on server-side callback for this purpose.

The client side Javascript callback should be used to request the real payment status towards the merchant server. To avoid synchronization problem between the client request and the server side callback, the merchant server can use the paymentStatus to actively check the result of the payment.

  <!-- Inside the JSON configuration of the Seamless -->
  <script type="application/json">
    {
      "onPaymentResult":"executeMePlease"
    }
  </script>

  <script type="text/javascript">
    function executeMePlease(response) {
      // This function could be used to query the status of the payment from the server side and update the display accordingly.
      console.log(response); // JSON object with the API response
      document.getElementById("c2pResultText").appendChild(<p>You did it!!</p>); // You can fetch the result div and modify
    }
  </script>

Events

Seamless Checkout form can trigger Javascript events that you can use to better integrate the payment flow with your ecommerce:

validForm

This event triggers when customer finishes inputting all valid credit card information, can be catched like this:

  document.addEventListener('validForm', function (e) {
    console.log("Valid form!")
  })

payButtonClick

This event triggers when the payment button is clicked. Can be catched like this:

  document.addEventListener('payButtonClick', function (e) {
    console.log("Form was submitted! Let's add it to Google Analytics")
  })

Customization

The library can be customized in different ways, depending on the merchants’ needs.

<div id="payment-form-container">
  <script type="application/json">
    {
      "payButtonText": "Pay €10.99 now!",
      "language": "en",
      "fontFamily": "Helvetica"
    }
  </script>
</div>

Class injection

You can inject external classes directly into the form elements. This is useful is your website is using tools such as Bootstrap.

<div id="payment-form-container">
  <script type="application/json">
    {
      "payButtonText": "Pay €10.99 now!",
      "customClasses": {
        "cardHolderNameContainer": "form-group",
        "cardHolderName": "form-control",
        "cardNumberContainer": "form-group",
        "cardNumber": "form-control",
        "cardExpireDateShortContainer": "form-group",
        "cardExpireDateShort": "form-control",
        "cardSecurityCodeContainer": "form-group",
        "cardSecurityCode": "form-control"
      }
    }
  </script>
</div>
  <!-- The configuration above will result: -->

  <form class="cardForm__3-0Ac" novalidate="true" id="c2pCreditCardForm">
    <div class="form-group" id="c2pCardHolderNameContainer">
      <label id="c2pCardHolderNameLabel">
        Cardholder Name
      </label>
      <input type="text" class="formInput__3Sjbj form-control" id="c2pCardHolderName">
    </div>
    <div class=" form-group" id="c2pCardNumberContainer">
      <label id="c2pCardNumberLabel">
        Card Number
      </label>
      <input placeholder="____ ____ ____ ____" pattern="[1-9]/d+" class="formInput__3Sjbj cardNumber__5kHQE form-control" id="c2pCardNumber" type="tel">
    </div>
    ....
    ....
    ....
</form>

The skipDefaultStyle parameter will remove any default classes from the elements, so you can freely adapt the form with your own CSS classes.

Using id of top container

You can also customize the form defining the specific CSS for the tags of the container form:

<div id="payment-form-container">
</div>

<style type="text/css">
  #payment-form-container > input[type=text] {
    background-color: #FFF;
  }
</style>

Using ID selector of each element

Every element of the form has its own id, so you can select it and customize and modify as you want:

  <style type="text/css">
    #c2pCardHolderName {
      background-color: blue;
    }
  </style>

Here’s a list of the elements you can fetch

Element ID
c2pCreditCardForm
c2pCardHolderNameContainer
c2pCardHolderNameLabel
c2pCardHolderName
c2pCardNumberContainer
c2pCardNumberLabel
c2pCardNumber
c2pCardIconContainer
c2pCardInlineFields
c2pCardExpireDateShortContainer
c2pCardExpireDateShortLabel
c2pCardExpireDateShort
c2pCardSecurityCodeContainer
c2pCardSecurityCodeLabel
c2pCardSecurityCode
c2pSubmitButtonContainer
c2pSubmitButton
c2p3DSModal
c2pResultText

External Payment Button

This features allows merchant to use the Seamless Checkout solely as a credit card form. For example, if merchant add the credit card form in a different step of the checkout and has their own payment button in their Checkout page, they can configure the Seamless Checkout like:

<!-- Checkout button: -->
<div class="myCheckoutButtonContainer">
  <button id="myCheckoutButton">Let's Pay!</button>
</div>


<!-- Inside the JSON configuration of the Seamless -->
<script type="application/json"> 
{
  "externalPaymentButton":"myCheckoutButton"
}
</script

Accepted configuration parameters

Parameter Accepted Values Description
hideCardHolderName String Hides cardholder name field
onPaymentResult String Callback function to be triggered (don’t use parentheses or semicolons)
noModal Boolean If set, breaks out of modal when 3DSecure authentication step is reached.
fontFamily String Custom font if you like to customize the form
language en, fr, es Language of the labels and placeholders
enableApplePay Boolean Set to true to enable Apple Pay
labels   Object to change the labels of the form
labels.cardHolderName String Label for the CardHolderName input
labels.cardNumber String Label for the CardNumber input
labels.cardExpireDateShort String Label for the Expiration Date input
labels.cardSecurityCode String Label for the CSC input
payButtonText String Text for the payment button e.g. “Pay Now €10.99”
placeholders   Object to change the placeholders of the form’s inputs
placeholders.cardHolderName String Placeholder for the CardHolderName input
placeholders.cardNumber String Placeholder for the CardNumber input
placeholders.cardExpireDateShort String Placeholder for the Expiration Date input
placeholders.cardSecurityCode String Placeholder for the CSC input
customClasses   Object for customization of the elements
customClasses.cardHolderNameContainer String Classes will be applied into cardHolderName div container
customClasses.cardHolderNameLabel String Classes will be applied into cardHolderName label
customClasses.cardHolderName String Classes will be applied into cardHolderName input
customClasses.cardNumberContainer String Classes will be applied into cardNumber div container
customClasses.cardNumberLabel String Classes will be applied into cardNumber label
customClasses.cardNumber String Classes will be applied into cardNumber input
customClasses.cardExpireDateShortContainer String Classes will be applied into cardExpireDate div container
customClasses.cardExpireDateShortLabel String Classes will be applied into cardExpireDate label
customClasses.cardExpireDateShort String Classes will be applied into cardExpireDate input
customClasses.cardSecurityCodeContainer String Classes will be applied into cardSecurityCode div container
customClasses.cardSecurityCodeLabel String Classes will be applied into cardSecurityCode label
customClasses.cardSecurityCode String Classes will be applied into cardSecurityCode input