Monri Components - New Payment Integration - v1.2

Content

Versions

Version 1.4 - 01.09.2020.

HTTPS requirements

All submissions of payment info using Components are made via a secure HTTPS connection. However, to protect yourself from certain forms of man-in-the-middle attacks, and to prevent your customers from seeing Mixed Content warnings in modern browsers, you must serve the page containing the payment form over HTTPS as well.

In short, the address of the page containing Components must start with https:// rather than just http://. If you are not familiar with the process of buying SSL certificates and integrating them with your server to enable a secure HTTPS connection, check out our security documentation for more information.

Creating New Payment on Merchant’s Backend

This step is preferably executed when you have enough information to create customer’s order.

For simplicity we’ll show example using curl in PHP.

To create payment on our backend you’ll need:

Following fields are available/required:

field length type required description
amount 1-11 Integer YES amount is in minor units, ie. 10.24 USD is sent as 1024
order_number 2-40 String YES unique order identifier
currency 3 String YES One of supported currencies (BAM, HRK, EUR, USD, CHF etc)
transaction_type enum String YES possible values are: authorize or purchase
order_info 3-100 String YES short description of order being processed
scenario enum String NO possible values are: charge or add_payment_method
supported_payment_methodsnew predefined Array<String> NO An array of pan-tokens and/or card (see below for more details)

Scenario charge charges customer amount. Depending on transaction_type amount is reserved (authorize) or captured (purchase).

Scenario add_payment_method provides simple way to implement ‘Save card for future payments’ functionality.

Supported payment methods

supported_payment_methods in an array of valid payment methods.

Valid payment methods are:

Requirements / options:

Example of valid payment methods:

{
  "supported_payment_methods": ["f167252fecfeff8f134001bf8e7a700c53e8653f631362dd84913e23260199bf", "0df5d4eac4f36d0dee9f48c17ddac2f66c12e5edfc4f92a92d3e6085f31368ea","card"]
}

Setup above will result in:

Authorization

For request authentication we use Authorization header created from:

Parts above are joined by space, so Authorization header should be in this form:

Authorization: schema authenticity_token timestamp digest

Example: Authorization: WP3-v2 abc...def 1585229134 314d32d1...0b49

Request endpoint is <base_url>/v2/payment/new where base_url is:

TIP: Parametrize merchant_key, authenticity_token and base_url so it can be easily changed when you are ready for production environment.

Payment/new response contains:

Request example in PHP:

$data = [
  'amount' => 100, //minor units = 1EUR
  // unique order identifier
  'order_number' => 'random' . time(),
  'currency' => 'EUR',
  'transaction_type' => 'purchase',
  'order_info' => 'Create payment session order info',
  'scenario' => 'charge'
  'supported_payment_methods' => ['67f35b84811188a5c581b063c4f21bd6760c93b2a04d7ac4f8845dd5bbb3f5c6']
];
$body_as_string = Json::encode($data); // use php's standard library equivalent if Json::encode is not available in your code
$base_url = 'https://ipgtest.monri.com'; // parametrize this value
$ch = curl_init($base_url . '/v2/payment/new');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS, $body_as_string);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);

$timestamp = time();
$digest = hash('sha512', $key . $timestamp .$authenticity_token. $data_string);
$authorization = "WP3-v2 $authenticity_token $timestamp $digest";
            
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
    'Content-Type: application/json',
    'Content-Length: ' . strlen($data_string),
    'Authorization: ' . $authorization
  )
);

$result = curl_exec($ch);

if (curl_errno($ch)) {
  curl_close($ch);
  $response = ['client_secret' => null, 'status' => 'declined', 'error' => curl_error($ch)];
} else {
  curl_close($ch);
  $response = ['status' => 'approved', 'client_secret' => Json::decode($result)['client_secret']];
}

var_dump($response);

Confirm payment on merchant’s application

After you’ve created payment on a backend and sent client_secret back to your application you need to confirm payment using Monri Components.

Steps:

Creating a custom payment form

Creating a custom payment form with Components requires two steps

  1. Set up Monri Components
  2. Create your payment form

Step 1: Set up Monri Components

Components is available as part of Monri.js. To get started, include the following script on your pages. This script must always load directly from monri.com in order to remain PCI compliant—you can’t include it in a bundle or host a copy of it yourself.

<script src="https://ipgtest.monri.com/dist/components.js"></script>

Next, create an instance of Components:

var monri = Monri('<authenticity-token>');
var components = monri.components({"clientSecret": "<client-secret>"});

Replace <authenticity-token> with value provided in merchant dashboard.
Replace <client-secret> with value obtained in step Creating New Payment on Merchant's Backend

When you’re ready to accept live card payments, replace the authenticity_token and merchant_key with your production authenticity_token and merchant_key.

Monri Options

Additional options can be passed as optional second parameter to Monri constructor, which additionally can customise Monri Components.
Currently supported options are:

Step 1.1: localise components

Components can be localised via specifying locale in Monri Options. For example, to use hr locale in Monri Components (and all underlying components), {locale: 'hr'} can be specified as a second parameter.

var monri = Monri('authenticity-token', {locale: 'hr'});

As of now, supported locales are:

By “locale” here is assumed “language” only. Number format, date format, etc., as of time of writing, are not supported. By default, en locale is used.

Step 1.2: style components

Components can be stylised via additionally provided fonts in Monri Options. Fonts are provided as an array of FontFace objects, with these properties:

The family and src properties are required, others are optional and have default values.

List of fonts is passed as fonts property in Monri Options, like:

var monri = Monri('<authenticity-token>', {
        fonts: [
            {
                family: 'Rubik-Light',
                src: '/static/fonts/rubik-light.ttf'
            },
            {
                family: 'Rubik-Regular',
                src: 'https://some-site.com/fonts/roboto.ttf',
                weight: '900'
            }
        ]
    }
);

Localization options and style option can be, of course, combined, like:

var monri = Monri('<authenticity-token>', {
        locale: 'hr',
        fonts: [...]
    }
);

Step 2: Create your payment form

To securely collect card details from your customers, Components creates UI components for you that are hosted by Monri. They are then placed into your payment form, rather than you creating them directly.

To determine where to insert these components, create empty DOM elements (containers) with unique IDs within your payment form. We recommend placing your container within a <label> or next to a <label> with a for attribute that matches the unique id of the Element container. By doing so, the Element automatically gains focus when the customer clicks on the corresponding label.

For example:

<form action="" method="post" id="payment-form">
  <div class="form-row">
    <label for="card-element">
      Credit or debit card
    </label>
    <div id="card-element">
      <!-- A Monri Component will be inserted here. -->
    </div>

    <!-- Used to display Component errors. -->
    <div id="card-errors" role="alert"></div>
  </div>

  <button>Submit Payment</button>
</form>

(Note: if inserted payment form is invisible, this may be a CSS issue. Start diagnostics by removig class form-row, the only one CSS in example)

When the form above has loaded, create an instance of an Component and mount it to the Component container created above:

// Custom styling can be passed to options when creating an Component.
var style = {
  base: {
    // Add your base input styles here. For example:
    fontSize: '16px',
    color: '#663399',
  }
};
// Create an instance of the card Component.
var card = components.create('card', {style: style});
// Add an instance of the card Component into the `card-element` <div>.
card.mount('card-element');

The card Component simplifies the form and minimises the number of fields required by inserting a single, flexible input field that securely collects all necessary card details.

Styling components

Components can be styled via style property of second parameter in create method, as shown above in example.
For all styling options please see section Components - style

Validation & listening to changes

Validating component

Components validates user input as it is typed. To help your customers catch mistakes, you should listen to change events on the card Component and display any errors:

card.onChange(function(event) {
  var displayError = document.getElementById('card-errors');
  if (event.error) {
    displayError.textContent = event.error.message;
  } else {
    displayError.textContent = '';
  }
});

Reacting to input element change - addChangeListener

We support adding changeListener via addChangeListener method.
To add change listener simply invoke:

card.addChangeListener('<input_element>',function(event) {
  console.log(event.data)
  console.log(event.message)
  console.log(event.valid)
  console.log(event.element)
});

It’s possible to add change listener for:

Card Number

To add card number change listener execute:
card.addChangeListener('card_number',function(event) {
  console.log(event.data)
  console.log(event.data.bin)
  console.log(event.data.brand)
  console.log(event.message)
  console.log(event.valid)
  console.log(event.element)
});
Event details
Example event data:
{
   "data":{
      "bin":"411111",
      "brand":"visa"
   },
   "element":"card_number",
   "message":null,
   "valid":true
}

Expiry Date

To add expiry date change listener execute:
card.addChangeListener('expiry_date',function(event) {
  console.log(event.data)
  console.log(event.data.month)
  console.log(event.data.year)
  console.log(event.message)
  console.log(event.valid)
  console.log(event.element)
});
Event details
Example event data:
{
   "data":{
      "month":11,
      "year":2021
   },
   "element":"expiry_date",
   "message":null,
   "valid":true
}

Confirm Payment using Monri Components

Step 1: Invoke confirmPayment when you are ready to confirm order

The payment details collected using Components can be used to confirm payment.

Although payment can be confirmed at any moment it is a good practice to intercept form submit and then invoke monri.confirmPayment.

confirmPayment accepts two arguments:

All transaction params fields are validated using rules defined in Variables - names, lengths and formats

Create an event handler that handles the submit event on the form. The handler invokes confirmPayment which confirms transaction and returns Result<PaymentResult>.


type MonriError = {
    message: string
}

type Result<PaymentResult> = {
	result: PaymentResult | null,
    error: MonriError | null
}

type PaymentResult = {
    status: string, // approved or declined
    currency: string,
    amount: number, // amount in minor units, eg 10.24 USD is 1024
    order_number: string,
    pan_token: string | null, // pan token representing tokenized card
    created_at: string,
    transaction_type: string, // authorize or purchase, depending on trx type used in payment/new
    payment_method: SavedCardPaymentMethod | null, // available if card is tokenized for future payments, null if not
    errors: Array<string> | null // errors if transaction is declined
}

type SavedCardPaymentMethod = {
    type: string,
    data: SavedCardPaymentMethodData
}

type SavedCardPaymentMethodData = {
    brand: string,
    issuer: string,
    masked: string,
    expiration_date: string,
    token: string
}

IMPORTANT 3DS authentication is automatically handled by Monri Components library.

// Confirm payment or display an error when the form is submitted.
var form = document.getElementById('payment-form');
form.addEventListener('submit', function(event) {
  event.preventDefault();
  const transactionParams = {
                           address: "Adresa",
                           fullName: "Test Test",
                           city: "Sarajevo",
                           zip: "71000",
                           phone: "+38761000111",
                           country: "BA",
                           email:"[email protected]",
                           orderInfo: "Testna trx"
            }

  monri.confirmPayment(card, transactionParams).then(function(result) {
    if (result.error) {
      // Inform the customer that there was an error.
      var errorElement = document.getElementById('card-errors');
      errorElement.textContent = result.error.message;
    } else {
      handlePaymentResult(result.result)
    }
  });
});

Step 2: Handle Payment Result

The last step is to handle payment result. This step completely depends on application use case. Response can be submitted to the merchant’s backend or handled in application.

If you want secure and stable transaction processed notification continue to the section Getting payment result on merchant's backend

function handlePaymentResult(paymentResult) {
	  // Handle PaymentResult
	  if(paymentResult.status == 'approved') {
		  alert("Transaction approved")
	  } else {
		  alert("Transaction declined")
	  }
}

Getting payment result on merchant’s backend

Although you can easily collect payment result directly in application via onSuccess call it’s better to implement callback listener (WebHook) on your backend.

Requirements:

How it works:

Example of POST request sent to callback endpoint:

Body:

{
  "id": 214,
  "acquirer": "integration_acq",
  "order_number": "3159daf002e3809",
  "amount": 100,
  "currency": "HRK",
  "ch_full_name": "John Doe",
  "outgoing_amount": 100,
  "outgoing_currency": "HRK",
  "approval_code": "687042",
  "response_code": "0000",
  "response_message": "approved",
  "reference_number": "000003036888",
  "systan": "000214",
  "eci": "06",
  "xid": null,
  "acsv": null,
  "cc_type": "visa",
  "status": "approved",
  "created_at": "2020-03-26T11:09:17.959+01:00",
  "transaction_type": "purchase",
  "enrollment": "N",
  "authentication": null,
  "pan_token": null,
  "masked_pan": "411111-xxx-xxx-1111",
  "issuer": "xml-sim",
  "number_of_installments": null,
  "custom_params": "{a:b, c:d}"
}

Headers:

header value
accept-encoding gzip;q=1.0,deflate;q=0.6,identity;q=0.3
authorization WP3-callback d5e4528ad8a0e0f4262e518c663d5ff83cd4a8f381db68f9d30f99961409ceebb719c16d423757fc36c532b902c987012f5825dc8d32dde3a9b7ed95876be77a
content-type application/json
http_authorization WP3-callback d5e4528ad8a0e0f4262e518c663d5ff83cd4a8f381db68f9d30f99961409ceebb719c16d423757fc36c532b902c987012f5825dc8d32dde3a9b7ed95876be77a
user-agent Faraday v0.15.4
content-length 621
connection keep-alive

Where authorization and http_authorization headers are created as:

digest = sha512(merchant_key + body)

authorization_header_value = WP3-callback digest

To check if request is valid check:

Viewport meta tag requirements

In order to provide a great user experience for 3D Secure on all devices, you should set your page’s viewport width to device-width with the the viewport meta tag. There are several other viewport settings, and you can configure those based on your needs. Just make sure you include width=device-width somewhere in your configuration.

For instance the following will work with Components:

<meta name="viewport" content="width=device-width, initial-scale=1" />

If you already have this tag on your page and it includes width=device-width, then you are all set.

Components

All Components accept a common set of options, and then some Component-specific options.

Style

Customize appearance using CSS properties. Styles are passed via the style property of options.

Style property consist of four properties which represent the states of the component elements (e.g. card fields). There is base property which defines style that
is applied to each component element, and there are three additional properties which, if specified, will override the base style.
These properties are:

Styles are applied to all component elements based on their state which had focus at least once. Currently, available style properties are as follows, all of string type and all optional:

Example of custom style:

var style = {
  base: {
    fontSize: '16px',
    color: '#663399',
    fontWeight: '900'
  },
  invalid: {
      color: 'red' //will override base.color for 'invalid' state
  },
  empty: {
      fontSize: '12px', //overrides base.fontSize
      color: 'grey'     //and base.color
  },
  complete: {
      color: 'blue'
  }
};

Component specific options

Card Component Options

Card component options extends shared component options with:

Option: tokenizePan

If enabled, PAN is tokenizen upon transaction approval and pan_token value is populated in transaction response.

Example:

var style = {} // define styling options for card component
var card = components.create("card", {
	style: style,
	tokenizePan: true // this will tokenize pan upon transaction approval
});

Option: tokenizePanOffered

If enabled save card for future payments checkbox is presented to a customer. If user checks the checkbox, PAN is tokenized upon transaction approval and pan_token value is populated in transaction response.

Example:

var style = {} // define styling options for card component
var card = components.create("card", {
	style: style,
	tokenizePanOffered: true // this will enable 'save card for future payments' checkbox in form
});

Notice - if tokenizePan is set to true then tokenizePanOffered is ignored.
Notice - setting tokenizePan to true requires user consent beforehand, in form of accepting terms and conditions or alike.

Transactions API

Variables - names, lengths and formats

Here are the variables and their definitions used when generating JSON documents for API calls:

Buyer’s profile

name length format additional info
ch_full_name 3-30 alphanumeric buyer’s full name
ch_address 3-100 alphanumeric buyer’s address
ch_city 3-30 alphanumeric buyer’s city
ch_zip 3-9 alphanumeric buyer’s zip
ch_country 2-3 alphanumeric buyer’s country in alpha2, alpha3 letter code or 3 digit ISO numeric code
ch_phone 3-30 alphanumeric buyer’s phone
ch_email 3-100 alphanumeric buyer’s email

Order details

name length format additional info
order_info 3-100 alphanumeric short description of order being processed
order_number 1-40 printable characters unique identifier
amount 3-11 integer amount is in minor units, ie. 10.24 USD is sent as 1024
currency predefined alphabetic possible values are USD, EUR, BAM or HRK

List of response codes

Here is the list of response codes and their description: