Monri Components - New Payment Integration

Versions

Version 17.05.2021

Content

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.

You can find a demo on this link Demo Components

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: chargeor 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.
----------------------------------------------
WARNING - add_payment_method scenario will automatically execute refund or void depending on transaction type. ONLY use for saving cards.
----------------------------------------------

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:

You can check digest on this link Calculate Digest

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. $body_as_string);
$authorization = "WP3-v2 $authenticity_token $timestamp $digest";
            
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
    'Content-Type: application/json',
    'Content-Length: ' . strlen($body_as_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.

In addition to the card component, we also offer a saved card component and cvv component. They can be easily created like as the card mentioned above.
For example:

var savedCard = components.create('saved_card', {style: style});
var cvvComponent = components.create('cvv', {style: style});

Default payment method is provided via supported payment_methods to change it on cvv component you can call setActivePaymentMethod
where you have to provide valid token for saved card.

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
}

CVV Change Event

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

Installments Event

To add selected installment change listener execute:
card.addChangeListener('installments', function (event) {
    console.log(event.data)
    console.log(event.data.selectedInstallment)
    console.log(event.message)
    console.log(event.valid)
    console.log(event.element)
});
Event details
Example event data:
{
  "data": {
    "selectedInstallment": 11
  },
  "element": "cvv",
  "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.

Card Component

// 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")
    }
}

Crypto Currencies

Paycek payments

Step 1: Set up PayCek Component

PayCek component 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.

Test Environment

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

Production Environment

<script src="https://ipg.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 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: localize components

Components can be localized 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.

For more details check localize components

Step 2: Create your payment form

To determine where to insert these components, create empty DOM elements (containers) with unique IDs within your
payment form. For example:


<form action="" method="post" id="pay-cek-payment-form-form">
    <div class="form-row">
        <div id="pay-cek-element">
            <!-- A Monri PayCek Component will be inserted here. -->
        </div>
    </div>

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

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

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

// Custom styling can be passed to options when creating PayCek Component.
var options = {
    payCekOptions: {size: "small"}
};
// Create an instance of the card Component.
var payCek = components.create("pay-cek", options)
    .onStartPayment(() => {
        // This is invoked after user clicks on PayCek button
        // Here you can:
        // 1. Collect user's info such as name, email address
        // 2. Invoke confirmPayment with PayCek
        const transactionParams = {
            address: "Adresa",
            fullName: "Test Test",
            city: "Sarajevo",
            zip: "71000",
            phone: "+38761000111",
            country: "BA",
            email: "[email protected]",
            orderInfo: "Testna trx"
        }
        monri
            // Confirm payment does few things
            // 1. it opens a popup window where and option to pick between supported Crypto currencies is shown
            // 2. after user selects one of currencies QR code with wallet address is shown
            // 3. Approved or declined transaction response is then send as Promise
            .confirmPayment(payCek, params)
            .then(e => {
                if (e.error) {
                    // Inform the customer that there was an error.
                    alert(e.error.message)
                } else {
                    // Handle approved or declined result
                }
            })
            .catch(e => {
                alert(e);
            })
                        
    })
// Add an instance of the PayCek component into the `pay-cek-element` <div>.
payCek.mount("pay-cek-element")

Confirm Payment

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

Although payment can be confirmed at any moment it is a good practice to 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.

const transactionParams = {
    address: "Adresa",
    fullName: "Test Test",
    city: "Sarajevo",
    zip: "71000",
    phone: "+38761000111",
    country: "BA",
    email: "[email protected]",
    orderInfo: "Testna trx"
}
payCek.onStartPayment(() => {
    monri.confirmPayment(payCek, transactionParams)
        .then(e => {
            if (e.error) {
                // Inform the customer that there was an error.
                alert(e.error.message)
            } else {
                handlePaymentResult(e.result)
            }
        })
        .catch(e => {
            alert(e);
        })
    
})

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:

Additionaly, as shown in example below, style property of label elements, input elements, remember card label element
and select payment method element can be set explicitly.

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:

Attribute CSS attribute Description
fontSize font-size size of the font
color color component element color
fontFamily font-family family of the font
fontSmoothing font-smoothing selected font smoothing
fontVariant font-variant variant of the font
fontWeight font-weight font weight
letterSpacing letter-spacing font letter spacing
textDecoration text-decoration component element text decoration
textShadow text-shadow component element text shadow
textTransform text-transform component element text transform
border border component element border
borderTop border-top component element top border
borderRight border-right component element right border
borderBottom border-bottom component element bottom border
borderLeft border-left component element left border
borderRadius border-radius component element border radius
padding padding component element padding
margin margin component element margin
lineHeight line-height component element line height
textIndent text-indent component element text indent
position position component element position
top top component element top
bottom bottom component element bottom
left left component element left
right right component element right
width width component element width
backgroundColor background-color component element background color
height height component element height
boxShadow box-shadow, moz-box-shadow, webkit-box-shadow component element height

Example of custom style:

var style = {
    base: {
        fontFamily: 'Rubik-Regular'
    },
    invalid: {
        color: 'red'
    },
    complete: {
        color: 'blue'
    },
    label: {
        base: {
            color: 'blue',
            textTransform: 'none'
        },
        invalid: {
            color: 'gray'
        },
        complete: {
            color: 'green'
        }
    },
    input: {
        base: {
            fontSize: '15px',
            color: "#663399",
            borderBottom: "1px solid purple"
        }
    },
    rememberCardLabel: {
        base: {
            fontSize: '15px',
            color: "#663399"
        }
    },
    selectPaymentMethod: {
        base: {
            fontSize: '15px',
            color: "#663399"
        }
    }
};

Style object has structure like the one below, where empty fields can be omitted:

var style = {
    base: {},
    invalid: {},
    complete: {},
    empty: {},
    label: {
        base: {},
        invalid: {},
        complete: {},
        empty: {}
    },
    input: {
        base: {},
        invalid: {},
        complete: {},
        empty: {}
    },
    rememberCardLabel: {
        base: {},
        invalid: {},
        complete: {},
        empty: {}
    },
    selectPaymentMethod: {
        base: {},
        invalid: {},
        complete: {},
        empty: {}
    }
}

Component specific options

Card Component Options

Card component options extends shared component options with:

option type description default value
tokenizePan boolean tokenize PAN when the client enters card info false
tokenizePanOffered boolean offer the client to tokenize his PAN false
showInstallmentsSelection boolean show installments select if installments are enabled for merchant false

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.

Option: showInstallmentsSelection

If enabled & merchant has enabled installments then dropdown for installments selection will be shown.

Example:

var style = {} // define styling options for card component
var card = components.create("card", {
	style: style,
	showInstallmentsSelection: true // this will enable installments selection
});

NOTICE - if set to true and not shown then installments for merchant are disabled. Contact [email protected]

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