Create and validate an Order
To accept payments with Montonio, you need to create an Order, redirect the customer, and then validate the status of the Order. This guide will walk you through the process.
Creating an Order
The Order object is the top-level object you create in Montonio. This should match one-to-one with an Order in your system. The Order object is necessary to create a Payment for it.
Complete example
This is a complete example of how to create an Order and redirect the customer to the gateway. You can use this as a starting point for your integration. The process is as follows:
- Gather the checkout data you will use to create the Order
- Specify the payment method the customer will use to pay for the Order
- Generate a JWT (token) from this data
- POST the JWT to the API to create the Order
- Redirect the customer to the
paymentUrlreturned by the API
/** * We recommend using the jsonwebtoken package to generate * Json Web Tokens. You can install it with npm: * > npm install jsonwebtoken * More information can be found at * https://www.npmjs.com/package/jsonwebtoken */import jwt from 'jsonwebtoken';import axios from 'axios';
/** * Note: Please make sure to make these calls from your server, * and not from the client. This is to prevent your secret key * from being exposed to the public. */
// 1. Gather the checkout dataconst payload = { "accessKey": "MY_ACCESS_KEY", "merchantReference": "MY-ORDER-ID-123", "returnUrl": "https://mystore.com/payment/return", "notificationUrl": "https://mystore.com/payment/notify", "currency": "EUR", "grandTotal": 99.99, "locale": "et", "billingAddress": { "firstName": "CustomerFirst", "lastName": "CustomerLast", "email": "customer@customer.com", "addressLine1": "Kai 1", "locality": "Tallinn", "region": "Harjumaa", "country": "EE", "postalCode": "10111" }, "shippingAddress": { "firstName": "CustomerFirstShipping", "lastName": "CustomerLastShipping", "email": "customer@customer.com", "addressLine1": "Kai 1", "locality": "Tallinn", "region": "Harjumaa", "country": "EE", "postalCode": "10111" }, "lineItems": [ { "name": "Hoverboard", "quantity": 1, "finalPrice": 99.99 } ],
// 2. Specify the payment method "payment": { "method": "paymentInitiation", "methodDisplay": "Pay with your bank", "methodOptions": { "paymentDescription": "Payment for order 123", // For international banks (e.g., Revolut, N26), ensure this matches // the country of the bank list you displayed to the customer. "preferredCountry": "EE", // This is the code of the bank that the customer chose at checkout. // See the GET /stores/payment-methods endpoint for the list of available banks. "preferredProvider": "LHVBEE22" }, "amount": 99.99, // Yes, this is the same as the grandTotal "currency": "EUR" // This must match the currency of the order. }}
// 3. Generate the tokenconst token = jwt.sign( payload, 'MY_SECRET_KEY', { algorithm: 'HS256', expiresIn: '10m' });
// console.log(token);// eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2Nlc3NLZXkiOiJNWV9BQ0NFU1NfS0VZIiwibWVyY2hhbnRSZWZlcmVuY2UiOiJNWS1PUkRFUi1JRC0xMjMiLCJyZXR1cm5VcmwiOiJodHRwczovL215c3RvcmUuY29tL3BheW1lbnQvcmV0dXJuIiwibm90aWZpY2F0aW9uVXJsIjoiaHR0cHM6Ly9teXN0b3JlLmNvbS9wYXltZW50L25vdGlmeSIsImN1cnJlbmN5IjoiRVVSIiwiZ3JhbmRUb3RhbCI6OTkuOTksImxvY2FsZSI6ImV0IiwiYmlsbGluZ0FkZHJlc3MiOnsiZmlyc3ROYW1lIjoiQ3VzdG9tZXJGaXJzdCIsImxhc3ROYW1lIjoiQ3VzdG9tZXJMYXN0IiwiZW1haWwiOiJjdXN0b21lckBjdXN0b21lci5jb20iLCJhZGRyZXNzTGluZTEiOiJLYWkgMSIsImxvY2FsaXR5IjoiVGFsbGlubiIsInJlZ2lvbiI6Ikhhcmp1bWFhIiwiY291bnRyeSI6IkVFIiwicG9zdGFsQ29kZSI6IjEwMTExIn0sInNoaXBwaW5nQWRkcmVzcyI6eyJmaXJzdE5hbWUiOiJDdXN0b21lckZpcnN0U2hpcHBpbmciLCJsYXN0TmFtZSI6IkN1c3RvbWVyTGFzdFNoaXBwaW5nIiwiZW1haWwiOiJjdXN0b21lckBjdXN0b21lci5jb20iLCJhZGRyZXNzTGluZTEiOiJLYWkgMSIsImxvY2FsaXR5IjoiVGFsbGlubiIsInJlZ2lvbiI6Ikhhcmp1bWFhIiwiY291bnRyeSI6IkVFIiwicG9zdGFsQ29kZSI6IjEwMTExIn0sImxpbmVJdGVtcyI6W3sibmFtZSI6IkhvdmVyYm9hcmQiLCJxdWFudGl0eSI6MSwiZmluYWxQcmljZSI6OTkuOTl9XSwicGF5bWVudCI6eyJtZXRob2QiOiJwYXltZW50SW5pdGlhdGlvbiIsIm1ldGhvZERpc3BsYXkiOiJQYXkgd2l0aCB5b3VyIGJhbmsiLCJtZXRob2RPcHRpb25zIjp7InBheW1lbnRSZWZlcmVuY2UiOiIxMDEwMTA5IiwicGF5bWVudERlc2NyaXB0aW9uIjoiUGF5bWVudCBmb3Igb3JkZXIgMTIzIiwicHJlZmVycmVkQ291bnRyeSI6IkVFIiwicHJlZmVycmVkUHJvdmlkZXIiOiJMSFZCRUUyMiJ9LCJhbW91bnQiOjk5Ljk5LCJjdXJyZW5jeSI6IkVVUiJ9LCJpYXQiOjE2NzYwNDQ3NTksImV4cCI6MTY3NjA0NTM1OX0.VichbCT2QQUBXE_yGNXAuYuXrwxrNELTHMDH7_dAPzw
axios.post('https://stargate.montonio.com/api/orders', { data: token}).then(response => { const { data } = response; console.log(data.paymentUrl); // https://gateway.montonio.com/some-random-uuid
/** * Note: After you have successfully created the order, * you may now pass the order.paymentUrl to the client-side. */
// 6. Redirect the customer to the checkout page (frontend) // window.location.href = data.paymentUrl;});<?php/** * We recommend using Firebase's php-jwt package to generate * Json Web Tokens. You can install it with composer: * > composer require firebase/php-jwt * More information can be found at * https://github.com/firebase/php-jwt */
// If you are using composer remember you need to autoload files like this// require __DIR__ . '/vendor/autoload.php';
use \Firebase\JWT\JWT;
// 1. Gather the checkout data$payload = [ 'accessKey' => 'MY_ACCESS_KEY', 'merchantReference' => 'MY-ORDER-ID-123', 'returnUrl' => 'https://mystore.com/payment/return', 'notificationUrl' => 'https://mystore.com/payment/notify', 'currency' => 'EUR', 'grandTotal' => 99.99, 'locale' => 'en', 'billingAddress' => [ 'firstName' => 'CustomerFirst', 'lastName' => 'CustomerLast', 'email' => 'customer@customer.com', 'addressLine1' => 'Kai 1', 'locality' => 'Tallinn', 'region' => 'Harjumaa', 'country' => 'EE', 'postalCode' => '10111', ], 'shippingAddress' => [ 'firstName' => 'CustomerFirstShipping', 'lastName' => 'CustomerLastShipping', 'email' => 'customer@customer.com', 'addressLine1' => 'Kai 1', 'locality' => 'Tallinn', 'region' => 'Harjumaa', 'country' => 'EE', 'postalCode' => '10111', ], 'lineItems' => [ [ 'name' => 'Hoverboard', 'quantity' => 1, 'finalPrice' => 99.99, ], ],];
// 2. Specify the Payment Method$payload['payment'] = [ 'method' => 'paymentInitiation', 'methodDisplay' => 'Pay with your bank', 'amount' => 99.99, // Yes, this is the same as order->grandTotal. 'currency' => 'EUR', // This must match the currency of the order. 'methodOptions' => [ 'paymentDescription' => 'Payment for order 123', // For international banks (e.g., Revolut, N26), ensure this matches // the country of the bank list you displayed to the customer. 'preferredCountry' => 'EE', // This is the code of the bank that the customer chose at checkout. // See the GET /stores/payment-methods endpoint for the list of available banks. 'preferredProvider' => 'LHVBEE22', ],];
// add expiry to payment data for JWT validation$payload['exp'] = time() + (10 * 60);
// 3. Generate the token using Firebase's JWT library$token = JWT::encode($payload, 'MY_SECRET_KEY', 'HS256');
// Remove this var_dump once you want the header (location) to work correctlyvar_dump($token);// eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhY2Nlc3NLZXkiOiJNWV9BQ0NFU1NfS0VZIiwibWVyY2hhbnRSZWZlcmVuY2UiOiJNWS1PUkRFUi1JRC0xMjMiLCJyZXR1cm5VcmwiOiJodHRwczovL215c3RvcmUuY29tL3BheW1lbnQvcmV0dXJuIiwibm90aWZpY2F0aW9uVXJsIjoiaHR0cHM6Ly9teXN0b3JlLmNvbS9wYXltZW50L25vdGlmeSIsImN1cnJlbmN5IjoiRVVSIiwiZ3JhbmRUb3RhbCI6OTkuOTg5OTk5OTk5OTk5OTk0ODg0MDkyMzAyNTI3Mjc4NjYxNzI3OTA1MjczNDM3NSwibG9jYWxlIjoiZW4iLCJiaWxsaW5nQWRkcmVzcyI6eyJmaXJzdE5hbWUiOiJDdXN0b21lckZpcnN0IiwibGFzdE5hbWUiOiJDdXN0b21lckxhc3QiLCJlbWFpbCI6ImN1c3RvbWVyQGN1c3RvbWVyLmNvbSIsImFkZHJlc3NMaW5lMSI6IkthaSAxIiwibG9jYWxpdHkiOiJUYWxsaW5uIiwicmVnaW9uIjoiSGFyanVtYWEiLCJjb3VudHJ5IjoiRUUiLCJwb3N0YWxDb2RlIjoiMTAxMTEifSwic2hpcHBpbmdBZGRyZXNzIjp7ImZpcnN0TmFtZSI6IkN1c3RvbWVyRmlyc3RTaGlwcGluZyIsImxhc3ROYW1lIjoiQ3VzdG9tZXJMYXN0U2hpcHBpbmciLCJlbWFpbCI6ImN1c3RvbWVyQGN1c3RvbWVyLmNvbSIsImFkZHJlc3NMaW5lMSI6IkthaSAxIiwibG9jYWxpdHkiOiJUYWxsaW5uIiwicmVnaW9uIjoiSGFyanVtYWEiLCJjb3VudHJ5IjoiRUUiLCJwb3N0YWxDb2RlIjoiMTAxMTEifSwibGluZUl0ZW1zIjpbeyJuYW1lIjoiSG92ZXJib2FyZCIsInF1YW50aXR5IjoxLCJmaW5hbFByaWNlIjo5OS45ODk5OTk5OTk5OTk5OTQ4ODQwOTIzMDI1MjcyNzg2NjE3Mjc5MDUyNzM0Mzc1fV0sInBheW1lbnQiOnsibWV0aG9kIjoicGF5bWVudEluaXRpYXRpb24iLCJtZXRob2REaXNwbGF5IjoiUGF5IHdpdGggeW91ciBiYW5rIiwiYW1vdW50Ijo5OS45ODk5OTk5OTk5OTk5OTQ4ODQwOTIzMDI1MjcyNzg2NjE3Mjc5MDUyNzM0Mzc1LCJjdXJyZW5jeSI6IkVVUiIsIm1ldGhvZE9wdGlvbnMiOnsicGF5bWVudFJlZmVyZW5jZSI6IlBBWU1FTlQtRk9SLU1ZLU9SREVSLUlELTEyMyIsInBheW1lbnREZXNjcmlwdGlvbiI6IlBheW1lbnQgZm9yIG9yZGVyIDM3IiwicHJlZmVycmVkQ291bnRyeSI6IkVFIiwicHJlZmVycmVkUHJvdmlkZXIiOiJMSFZCRUUyIn19LCJleHAiOjE2NzU4NjQwMTB9.sRq2ngH9eyYxNJy--s_SdpgL4bKYeTNhHrKYCkaHyDs
// 4. Send the token to the API$ch = curl_init();curl_setopt($ch, CURLOPT_URL, "https://sandbox-stargate.montonio.com/api/orders");curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);curl_setopt($ch, CURLOPT_POST, 1);curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([ 'data' => $token]));curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Content-Type: application/json']);$result = curl_exec($ch);$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);curl_close($ch);
if ($status >= 400) { var_dump($result); // "{"statusCode":401,"message":"STORE_NOT_FOUND","error":"Unauthorized"}" exit;}
// 5. Get the payment URL$data = json_decode($result, true);
// var_dump($data['paymentUrl']);// https://gateway.montonio.com/some-random-uuid
// 6. Redirect the customer to the checkout pageheader("Location: " . $data['paymentUrl']);exit;'''We recommend using the PyJWT package to generateJson Web Tokens. You can install it with pip:> pip3 install PyJWTMore information can be found athttps://pyjwt.readthedocs.io/en/latest/'''import jwtimport datetimeimport requestsimport webbrowser
# 1. Gather the checkout datapayload = { 'accessKey': 'MY_ACCESS_KEY', 'merchantReference': 'MY-ORDER-ID-123', 'returnUrl': 'https://merchant-new.com/return', 'notificationUrl': 'https://merchant-new.com/notify', 'currency': 'EUR', 'grandTotal': 99.99, 'locale': 'et', 'billingAddress': { 'firstName': 'CustomerFirst', 'lastName': 'CustomerLast', 'email': 'customer@customer.com', 'addressLine1': 'Kai 1', 'locality': 'Tallinn', 'region': 'Harjumaa', 'country': 'EE', 'postalCode': '10111' }, 'shippingAddress': { 'firstName': 'CustomerFirstShipping', 'lastName': 'CustomerLastShipping', 'email': 'customer@customer.com', 'addressLine1': 'Kai 1', 'locality': 'Tallinn', 'region': 'Harjumaa', 'country': 'EE', 'postalCode': '10111' }, 'lineItems': [ { 'name': 'Hoverboard', 'quantity': 1, 'finalPrice': 99.99 } ],
# 2. Specify the payment method 'payment': { 'method': 'paymentInitiation', 'methodDisplay': 'Pay with your bank', 'methodOptions': { 'paymentDescription': 'Payment for order 123', # For international banks (e.g., Revolut, N26), ensure this matches # the country of the bank list you displayed to the customer. 'preferredCountry': 'EE', # This is the code of the bank that the customer chose at checkout. # See the GET /stores/payment-methods endpoint for the list of available banks. 'preferredProvider': 'LHVBEE22' }, 'amount': 99.99, # Yes, this is the same as order['grandTotal']. 'currency': 'EUR' # This must match the currency of the order. },
'exp': datetime.datetime.now(timezone.utc) + datetime.timedelta(minutes=10)}
# 3. Generate the tokentoken = jwt.encode(payload, 'MY_SECRET_KEY', algorithm='HS256')
print(token)# eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2Nlc3NLZXkiOiJNWV9BQ0NFU1NfS0VZIiwibWVyY2hhbnRSZWZlcmVuY2UiOiJNWS1PUkRFUi1JRC0xMjMiLCJyZXR1cm5VcmwiOiJodHRwczovL215c3RvcmUuY29tL3BheW1lbnQvcmV0dXJuIiwibm90aWZpY2F0aW9uVXJsIjoiaHR0cHM6Ly9teXN0b3JlLmNvbS9wYXltZW50L25vdGlmeSIsImN1cnJlbmN5IjoiRVVSIiwiZ3JhbmRUb3RhbCI6OTkuOTksImxvY2FsZSI6ImV0IiwiYmlsbGluZ0FkZHJlc3MiOnsiZmlyc3ROYW1lIjoiQ3VzdG9tZXJGaXJzdCIsImxhc3ROYW1lIjoiQ3VzdG9tZXJMYXN0IiwiZW1haWwiOiJjdXN0b21lckBjdXN0b21lci5jb20iLCJhZGRyZXNzTGluZTEiOiJLYWkgMSIsImxvY2FsaXR5IjoiVGFsbGlubiIsInJlZ2lvbiI6Ikhhcmp1bWFhIiwiY291bnRyeSI6IkVFIiwicG9zdGFsQ29kZSI6IjEwMTExIn0sInNoaXBwaW5nQWRkcmVzcyI6eyJmaXJzdE5hbWUiOiJDdXN0b21lckZpcnN0U2hpcHBpbmciLCJsYXN0TmFtZSI6IkN1c3RvbWVyTGFzdFNoaXBwaW5nIiwiZW1haWwiOiJjdXN0b21lckBjdXN0b21lci5jb20iLCJhZGRyZXNzTGluZTEiOiJLYWkgMSIsImxvY2FsaXR5IjoiVGFsbGlubiIsInJlZ2lvbiI6Ikhhcmp1bWFhIiwiY291bnRyeSI6IkVFIiwicG9zdGFsQ29kZSI6IjEwMTExIn0sImxpbmVJdGVtcyI6W3sibmFtZSI6IkhvdmVyYm9hcmQiLCJxdWFudGl0eSI6MSwiZmluYWxQcmljZSI6OTkuOTl9XSwicGF5bWVudCI6eyJtZXRob2QiOiJwYXltZW50SW5pdGlhdGlvbiIsIm1ldGhvZERpc3BsYXkiOiJQYXkgd2l0aCB5b3VyIGJhbmsiLCJtZXRob2RPcHRpb25zIjp7InBheW1lbnRSZWZlcmVuY2UiOiIxMDEwMTA5IiwicGF5bWVudERlc2NyaXB0aW9uIjoiUGF5bWVudCBmb3Igb3JkZXIgMTIzIiwicHJlZmVycmVkQ291bnRyeSI6IkVFIiwicHJlZmVycmVkUHJvdmlkZXIiOiJMSFZCRUUyMiJ9LCJhbW91bnQiOjk5Ljk5LCJjdXJyZW5jeSI6IkVVUiJ9LCJpYXQiOjE2NzYwNDQ3NTksImV4cCI6MTY3NjA0NTM1OX0.VichbCT2QQUBXE_yGNXAuYuXrwxrNELTHMDH7_dAPzw
# 4. Send the token to the API and get the payment URLresponse = requests.post('https://stargate.montonio.com/api/orders', json={ 'data': token})data = response.json()payment_url = data['paymentUrl']
# 5. Redirect the customer to the checkout pagewebbrowser.open(payment_url)Now let’s break it down into step-by-step instructions.
1. Order data structure
First, you need to create an Order object from the checkout page’s data. Not all fields are required, but it is recommended to provide as much information as possible to improve the customer experience. The following table describes the fields that can be used to create an Order object.
The fields marked with an asterisk are required.
| Key | Type | Description | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| accessKey* | string | Your Access Key obtained from the Partner System. | ||||||||||||||||||||||||||||||||||||
| merchantReference* | string | The order reference in the merchant’s system (e.g. the WooCommerce Order ID). This value must be unique for each order of a store. If you use the same value for multiple orders, the existing order will be updated. To avoid unintended behavior and use the parameter correctly, read this help center article. | ||||||||||||||||||||||||||||||||||||
| returnUrl* | string | The URL where the customer will be redirected back to after completing or canceling a payment. See below for details on payment verification. | ||||||||||||||||||||||||||||||||||||
| notificationUrl* | string | The URL to send a webhook notification about Order updates, e.g when a payment is completed. See below for details on payment verification. | ||||||||||||||||||||||||||||||||||||
| grandTotal* | number | Order grand total up to 2 decimal places (e.g 19.99). | ||||||||||||||||||||||||||||||||||||
| currency* | string | Payment currency. Currently EUR and PLN are supported depending on the payment method provided. | ||||||||||||||||||||||||||||||||||||
| exp* | number | Expiration time of the token in Unix time. Set that to 10 minutes from the time of issuing the token. (e.g. 1675860645) | ||||||||||||||||||||||||||||||||||||
| payment* | object | The payment method to use.
method as this defines which payment gateway the customer will be redirected to. | ||||||||||||||||||||||||||||||||||||
| locale* | string | The preferred language of the payment gateway. Defaults to the merchant country’s official language. Available values are de, en, et, fi, lt, lv, pl, ru. | ||||||||||||||||||||||||||||||||||||
| expiresIn | number | The number of minutes that the order can be paid for. The default is 30 minutes, minimum 5 minutes, and maximum 44640 minutes (31 days). This is used to calculate the expiration time of the order. The order will be abandoned if it is not paid within this time frame. Keep in mind this is different from the JWT’s exp field which is the expiration time of the token itself, not the order. | ||||||||||||||||||||||||||||||||||||
| sessionUuid | string | This is only relevant and required for embedded card payments. It is the UUID of the Session created in step one of the Embedded Cards guide. Only include this field if you are creating an order for an embedded card payment. | ||||||||||||||||||||||||||||||||||||
| billingAddress | object | The customer’s billing address.
| ||||||||||||||||||||||||||||||||||||
| shippingAddress | object | The customer’s shipping address.
| ||||||||||||||||||||||||||||||||||||
| lineItems | array | The list of items in the order. All fields are required if the lineItems property exists in the data.
|
2. Payment method
Secondly, you need to specify the payment method to use. This information is passed in the payment key of Order object.
The payment method to use is identified by the method key. The following payment methods are currently supported:
Payment Initiation is a payment method that allows customers to pay with their bank account. The customer is redirected to their bank’s online banking service where they can authorize the payment. Payment Initiation is currently available in the EU.
Method Identifier:
paymentInitiationSee the Order data structure > Payment > Method to see how to pass the payment method to use.
Supported currencies:EUR,PLN
// See the full example aboveorder.payment = { "method": "paymentInitiation", "methodOptions": { // This is the code of the bank that the customer chose at checkout. // See the GET /stores/payment-methods endpoint for the list of available banks. "preferredProvider": "LHVBEE22", // For international banks (e.g., Revolut, N26), ensure this matches // the country of the bank list you displayed to the customer. "preferredCountry": "EE", "preferredLocale": "et", "paymentDescription": "Payment for order 123" }}// See the full example above$order["payment"] => [ "method" => "paymentInitiation", "methodOptions" => [ // This is the code of the bank that the customer chose at checkout. // See the GET /stores/payment-methods endpoint for the list of available banks. "preferredProvider" => "LHVBEE22", // For international banks (e.g., Revolut, N26), ensure this matches // the country of the bank list you displayed to the customer. "preferredCountry" => "EE", "preferredLocale" => "et", "paymentDescription" => "Payment for order 123" ]]# See the full example aboveorder["payment"] = { "method": "paymentInitiation", "methodOptions": { # This is the code of the bank that the customer chose at checkout. # See the GET /stores/payment-methods endpoint for the list of available banks. "preferredProvider": "LHVBEE22", # For international banks (e.g., Revolut, N26), ensure this matches # the country of the bank list you displayed to the customer. "preferredCountry": "EE", "preferredLocale": "et", "paymentDescription": "Payment for order 123" }}Method Options for Payment Initiation:
| Key | Type | Description |
|---|---|---|
| preferredProvider | string | The bank that the customer chose for this payment if you allow them to select their bank of choice in your checkout. Leave this blank to let the customer choose their bank in our interface. See the Payment methods guide on how to fetch the list of available banks. The value can be found in the code parameters of the response. It is highly recommended you send this to us as it improves the customer experience and saves time at checkout. |
| preferredCountry | string | The preferred country for the methods list of the payment gateway. Defaults to the merchant’s country. Available values are EE, LV, LT, FI, PL, DE.📝 Note: For international banks (e.g., Revolut, N26), you must set preferredCountry to match the country of the payment methods displayed to the customer.These banks can be identified via the GET /payment-methods endpoint as they often share the same code across multiple countries. Example: If customer sees Estonian banks and selects Revolut → use “preferredCountry”: “EE” |
| preferredLocale | string | The preferred language of the payment gateway. Defaults to the Order locale. Available values are de, en, et, fi, lt, lv, pl, ru. |
| paymentDescription | string | Description of the payment that will be relayed to the bank’s payment order. If not included, it will default to the value of merchantReference. |
| paymentReference | string | Structured payment reference number. This is a standardized reference number used for accounting purposes and will be validated by banks. Do not include if you do not use reference numbers to link payments in your accounting software. Banks validate this number strictly and payments will start failing if this number is not formatted correctly. |
Card Payments allow shoppers to make payments with their credit or debit card. Google Pay and Apple Pay are also available in this flow.
Method Identifier:
cardPaymentsSee the Order data structure > Payment > Method for more information on how to pass the payment method to use.
Supported currencies:EUR,PLN
// See the full example aboveorder.payment = { "method": "cardPayments", "methodOptions": { "preferredMethod": "wallet", }}// See the full example above$order["payment"] => [ "method" => "cardPayments", "methodOptions" => [ "preferredMethod" => "wallet", ]]# See the full example aboveorder["payment"] = { "method": "cardPayments", "methodOptions": { "preferredMethod": "wallet", }}Method Options for Card Payments:
| Key | Type | Description |
|---|---|---|
| preferredMethod | string | Enables to choose which payment method is shown first by default in the UI - Card payments or Wallets. Defaults to wallet meaning that by default Apple or Google Pay are selected (customer can still choose to enter card details and pay). Available values are wallet, card. |
MobilePay allows Finnish shoppers to make payments using the MobilePay app. Read more at mobilepay.fi.
Method Identifier:
mobilePaySee the Order data structure > Payment > Method for more information on how to pass the payment method to use.
Supported currencies:EUR
// See the full example aboveorder.payment = { "method": "mobilePay"}// See the full example above$order["payment"] => [ "method" => "mobilePay"]# See the full example aboveorder["payment"] = { "method": "mobilePay"}BLIK is a payment method that allows customers to pay with their bank account through the BLIK mobile app. The customer is redirected to the BLIK app where they can authorize the payment. BLIK is currently available only in Poland. To allow BLIK, a separate onboarding needs to be completed in the partner system.
Method Identifier:
blikSee the Order data structure > Payment > Method for more information on how to pass the payment method to use.
Supported currencies:PLN
// See the full example aboveorder.payment = { "method": "blik", "methodOptions": { "preferredLocale": "pl", }}// See the full example above$order["payment"] => [ "method" => "blik", "methodOptions" => [ "preferredLocale" => "pl", ]]# See the full example aboveorder["payment"] = { "method": "blik", "methodOptions": { "preferredLocale": "pl", }}Method Options for BLIK:
| Key | Type | Description |
|---|---|---|
| preferredLocale | string | The preferred language of the payment gateway. Defaults to the Order locale. Available values are de, en, et, fi, lt, lv, pl, ru. |
Hire Purchase is a payment method that allows customers to pay in instalments. The customer is redirected to our partner Inbank’s website where the financing application is filled out and the financing contract signed. Upon successful signing of the contract, the user is redirected to the success page of the store. Read more about the method in the Financing guide.
Method Identifier:
hirePurchaseSee the Order data structure > Payment > Method to see how to pass the payment method to use.
Supported currencies:EUR
// See the full example aboveorder.payment = { "method": "hirePurchase",}// See the full example above$order["payment"] => [ "method" => "hirePurchase",]# See the full example aboveorder["payment"] = { "method": "hirePurchase",}Buy Now Pay Later is a payment method that allows customers to pay for their order later or split the payment. In the case of splitting, the payment can be split in either two or three parts. The customer is redirected to our partner Inbank’s website where the financing application is filled out and the financing contract signed. Upon the successful signing of the contract the user is redirected to the success page of the store. Read more about the method in the Financing guide.
Method Identifier:
bnplSee the Order data structure > Payment > Method to see how to pass the payment method to use.
Method options:periodThe Buy Now Pay Later period is defined using this parameter. It can either be1,2, or3
Supported currencies:EUR
// See the full example aboveorder.payment = { "method": "bnpl", "methodOptions": { "period": 1 // can be either 1, 2, or 3, depending on the split period. }}// See the full example above$order["payment"] => [ "method" => "bnpl", "methodOptions": => [ "period" => 1 // can be either 1, 2, or 3. Depending on the split period. ]]# See the full example aboveorder["payment"] = { "method": "hirePurchase", "methodOptions": { "period": 1 # can be either 1, 2, or 3. Depending on the split period. }}Method Options for Buy Now Pay Later
| Key | Type | Description |
|---|---|---|
| period | integer | The period of the Buy Now Pay Later payment. Can be either |
3. Generating the JWT
Now, you need to generate a JWT (JSON Web Token) from the Order object. We use JWT to securely pass the Order object to the API and use the JWT’s signature to verify the integrity of the request.
The exact implementation of how to generate the JWT varies by programming language and you can see some examples in the Complete Example section.
- The payload of the JWT is the Order object.
- The JWT is signed with your
Secret Keyusing HMAC SHA256 (HS256). - The headers of the JWT must contain the
algandtypkeys. They are described below.
| Key | Required | Type | Description |
|---|---|---|---|
| alg | yes | string | Must be set to HS256 |
| typ | yes | string | Must be set to JWT |
4. Submitting the token
After you have generated the token, you can submit it to Montonio’s API to create an Order. The API endpoint is:
POST /ordersAn example request using curl is shown below:
curl --location --request POST 'https://stargate.montonio.com/api/orders' \--header 'Content-Type: application/json' \--data-raw '{ "data": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2Nlc3NLZXkiOiJNWV9BQ0NFU1NfS0VZIiwibWVyY2hhbnRSZWZlcmVuY2UiOiJNWS1PUkRFUi1JRC0xMjMiLCJyZXR1cm5VcmwiOiJodHRwczovL215c3RvcmUuY29tL3BheW1lbnQvcmV0dXJuIiwibm90aWZpY2F0aW9uVXJsIjoiaHR0cHM6Ly9teXN0b3JlLmNvbS9wYXltZW50L25vdGlmeSIsImN1cnJlbmN5IjoiRVVSIiwiZ3JhbmRUb3RhbCI6OTkuOTksImxvY2FsZSI6ImV0IiwiYmlsbGluZ0FkZHJlc3MiOnsiZmlyc3ROYW1lIjoiQ3VzdG9tZXJGaXJzdCIsImxhc3ROYW1lIjoiQ3VzdG9tZXJMYXN0IiwiZW1haWwiOiJjdXN0b21lckBjdXN0b21lci5jb20iLCJhZGRyZXNzTGluZTEiOiJLYWkgMSIsImxvY2FsaXR5IjoiVGFsbGlubiIsInJlZ2lvbiI6Ikhhcmp1bWFhIiwiY291bnRyeSI6IkVFIiwicG9zdGFsQ29kZSI6IjEwMTExIn0sInNoaXBwaW5nQWRkcmVzcyI6eyJmaXJzdE5hbWUiOiJDdXN0b21lckZpcnN0U2hpcHBpbmciLCJsYXN0TmFtZSI6IkN1c3RvbWVyTGFzdFNoaXBwaW5nIiwiZW1haWwiOiJjdXN0b21lckBjdXN0b21lci5jb20iLCJhZGRyZXNzTGluZTEiOiJLYWkgMSIsImxvY2FsaXR5IjoiVGFsbGlubiIsInJlZ2lvbiI6Ikhhcmp1bWFhIiwiY291bnRyeSI6IkVFIiwicG9zdGFsQ29kZSI6IjEwMTExIn0sImxpbmVJdGVtcyI6W3sibmFtZSI6IkhvdmVyYm9hcmQiLCJxdWFudGl0eSI6MSwiZmluYWxQcmljZSI6OTkuOTl9XSwicGF5bWVudCI6eyJtZXRob2QiOiJwYXltZW50SW5pdGlhdGlvbiIsIm1ldGhvZERpc3BsYXkiOiJQYXkgd2l0aCB5b3VyIGJhbmsiLCJtZXRob2RPcHRpb25zIjp7InBheW1lbnRSZWZlcmVuY2UiOiJQQVlNRU5ULUZPUi1NWS1PUkRFUi1JRC0xMjMiLCJwYXltZW50RGVzY3JpcHRpb24iOiJQYXltZW50IGZvciBvcmRlciAzNyIsInByZWZlcnJlZENvdW50cnkiOiJFRSIsInByZWZlcnJlZFByb3ZpZGVyIjoiTEhWQkVFMjIifSwiYW1vdW50Ijo5OS45OSwiY3VycmVuY3kiOiJFVVIifSwiaWF0IjoxNjc1OTMxMzMzLCJleHAiOjE2NzU5MzE5MzN9.PBgJswAac6e1LRXSwGTAPpb_ghjoCQZZ-uIzxxBCX2E"}'The response will be a JSON object with the following structure:
{ "uuid": "12228dce-2f7c-4db5-8d28-5d82a19aa3b6", // This is the Montonio Order UUID. Use this to identify the order in the future and query its status. "paymentStatus": "PENDING", "locale": "et", "merchantReference": "MY-ORDER-ID-123", "merchantReferenceDisplay": "MY-ORDER-ID-123", "merchantReturnUrl": "https://mystore.com/payment/return", "merchantNotificationUrl": "https://mystore.com/payment/notify", "grandTotal": "99.99", "currency": "EUR", "paymentMethodType": "paymentInitiation", "storeUuid": "703a60eb-ed19-4fc3-8a62-4474150a962a", // This is the UUID of your store in the Montonio Partner System. "paymentIntents": [ { "uuid": "302f259c-7501-49ed-b3aa-39a4bbcafec1", "paymentMethodType": "paymentInitiation", "paymentMethodMetadata": { "preferredLocale": "et", "preferredCountry": "EE", "paymentDescription": "Payment for order 123" }, "amount": "99.99", "currency": "EUR", "status": "PENDING", "serviceFee": null, "serviceFeeCurrency": "EUR", "createdAt": "2023-12-13T12:02:35.522Z" } ], "refunds": [], "availableForRefund": 0, "isRefundableType": false, "lineItems": [ { "name": "Hoverboard", "quantity": 1, "finalPrice": 99.99 } ], "billingAddress": { "firstName": "CustomerFirst", "lastName": "CustomerLast", "email": "customer@customer.com", "phoneNumber": null, "phoneCountry": null, "addressLine1": "Kai 1", "addressLine2": null, "locality": "Tallinn", "region": null, "country": "EE", "postalCode": "10111", "companyName": null, "companyLegalName": null, "companyRegCode": null, "companyVatNumber": null }, "shippingAddress": { "firstName": "CustomerFirstShipping", "lastName": "CustomerLastShipping", "email": "customer@customer.com", "phoneNumber": null, "phoneCountry": null, "addressLine1": "Kai 1", "addressLine2": null, "locality": "Tallinn", "region": null, "country": "EE", "postalCode": "10111", "companyName": null, "companyLegalName": null, "companyRegCode": null, "companyVatNumber": null }, "expiresAt": null, "createdAt": "2023-02-08T13:32:55.099Z", "storeName": "My Store Name", "businessName": "My Business Name", "paymentUrl": "https://stargate.montonio.com/e694c95b-0335-493d-a713-af95720f885d"}Some errors which might occur during the request are listed below:
| Code | Description |
|---|---|
400 | Bad request. Please double-check the request body and the JWT contents. There are issues with some fields in the JWT body. You will get a more detailed error message. |
401 | STORE_NOT_FOUND. Please double check you are passing accessKey in the token and using the correct environment. |
403 | Forbidden. Please double check you are using the correct secretKey for signing and using the correct environment. |
500 | Internal server error. Something went wrong on our side. |
5. Redirecting the customer
Once you have created the Order, it’s time to redirect the customer to Montonio’s gateway. Notice the paymentUrl in the response. This is the URL that you should redirect the customer to.
An example redirect would look like this:
window.location.href = "http://stargate.montonio.com/a9816a11-f08e-4fcf-bf7c-2dd04ccbb6dd";That’s really all there is to it. The customer will be redirected to Montonio’s payment gateway where they can complete the payment.
Validating the payment
Redirecting back to the store
Once the customer completes the payment, they will be redirected back to the returnUrl that you specified in the Order JWT, with a new order-token appended to the URL as a query parameter. Validate the contents and the signature of this token to confirm the order.
Example return URL:
https://my-store.com/orders/27731773/thank_you?order-token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoidGhlLW1vbnRvbmlvLW9yZGVyLXV1aWQiLCJhY2Nlc3NLZXkiOiJNWV9BQ0NFU1NfS0VZIiwibWVyY2hhbnRSZWZlcmVuY2UiOiJNWS1PUkRFUi1JRC0xMjMiLCJtZXJjaGFudFJlZmVyZW5jZURpc3BsYXkiOiJNWS1PUkRFUi1JRC0xMjMiLCJwYXltZW50U3RhdHVzIjoiUEFJRCIsImdyYW5kVG90YWwiOjk5Ljk5LCJjdXJyZW5jeSI6IkVVUiIsIm1lcmNoYW50X3JlZmVyZW5jZSI6Ik1ZLU9SREVSLUlELTEyMyIsIm1lcmNoYW50X3JlZmVyZW5jZV9kaXNwbGF5IjoiTVktT1JERVItSUQtMTIzIiwicGF5bWVudF9zdGF0dXMiOiJQQUlEIn0.X6Ym70AA1bYIsKyNc1NL4NpznKXCrGX5xacqc1ovtuEWebhook notification
In addition to redirecting the customer, we will send a POST request to the notificationUrl parameter you specified in the Order JWT. You can retrieve the orderToken from the request’s body. Validate the contents and the signature of this token to confirm the order. Read more about responding to webhooks below.
This is used to confirm the order in the background, without relying on the customer to return to the merchant’s website, to prevent the issue where a customer closes the browser window before the redirect happens and the order is not confirmed, even though the payment was successful.
🛠️ Testing locally
To test webhooks locally, there are a few options, but currently the easiest one is to use a service like ngrok to expose your local server to the internet or webhook.site for quick troubleshooting.
We have written a more detailed guide in our Help Center. Have a look at it if you need more information.
These are the contents of the webhook:
{ "orderToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiYzhlMzZkMTMtNTIzMC00NTczLTk3MGYtMGE3N2ZmODIyYTBjIiwiYWNjZXNzS2V5IjoiWU9VUl9BQ0VTU19LRVkiLCJncmFuZFRvdGFsIjoyMiwibWVyY2hhbnRSZWZlcmVuY2UiOiI5OTk5OSIsIm1lcmNoYW50UmVmZXJlbmNlRGlzcGxheSI6Ijk5OTk5IiwicGF5bWVudFN0YXR1cyI6IlBBSUQiLCJwYXltZW50TWV0aG9kIjoicGF5bWVudEluaXRpYXRpb24iLCJwYXltZW50UHJvdmlkZXJOYW1lIjoiU0VCIEVzdG9uaWEiLCJzZW5kZXJJYmFuIjoiRUU0NzEwMDAwMDEwMjAxNDU2ODUiLCJzZW5kZXJOYW1lIjoiSm9obiBEb2UiLCJjdXJyZW5jeSI6IkVVUiIsIm1lcmNoYW50X3JlZmVyZW5jZSI6Ijk5OTk5IiwibWVyY2hhbnRfcmVmZXJlbmNlX2Rpc3BsYXkiOiI5OTk5OSIsInBheW1lbnRfc3RhdHVzIjoiUEFJRCIsImlhdCI6MTY5MjE4MDUyMywiZXhwIjoxNjkyMTk0OTIzfQ.yoy7KPvMYhXINZpA2UDfEW5Zz82Wpd1L-f5qhYFW7xU"}See the following section for how to validate the Order Token.
Validating the returned Order Token
The Order Token contains information about the order and the payment.
- In the case of a redirect, the token is appended to the
returnUrlas the query parameterorder-token. - In the case of a webhook notification, the
orderTokencan be found in the body of the POST request.
If you decode the token, you will get the following contents:
{ "uuid": "the-montonio-order-uuid", "accessKey": "MY_ACCESS_KEY", "merchantReference": "MY-ORDER-ID-123", "merchantReferenceDisplay": "MY-ORDER-ID-123", "paymentStatus": "PAID", "paymentMethod": "paymentInitiation", "grandTotal": 99.99, "currency": "EUR", // Both senderIban and senderName, will be filled if the bank provides us the info and the order status is "PAID" "senderIban": "EE471000001020145685", "senderName": "John Doe", // Can be string or null. If the payment method is "paymentInitiation" then it contains the bank name used in the transaction. "paymentProviderName": "New Wave Bank Group", "iat": 1632967333, "exp": 1632967333,
// Fields that will be removed in the future. "merchant_reference": "MY-ORDER-ID-123", "merchant_reference_display": "MY-ORDER-ID-123", "payment_status": "PAID"}The merchantReferenceDisplay is an internal reference that we use for stores that needs multiple or human-readable order references. If you don’t pass it in the Order JWT, it will be the same as the merchantReference… And you can just ignore it 🤭
The token is signed with your Secret Key and needs to be validated.
See the example below for validating the token:
/* * We recommend using the jsonwebtoken package to verify * Json Web Tokens. You can install it with npm: * > npm install jsonwebtoken * More information can be found at * https://www.npmjs.com/package/jsonwebtoken */import jwt from 'jsonwebtoken';
// fetched from the URL for returnUrl and from POST body->orderToken when it's a notificationconst orderToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoidGhlLW1vbnRvbmlvLW9yZGVyLXV1aWQiLCJhY2Nlc3NLZXkiOiJNWV9BQ0NFU1NfS0VZIiwibWVyY2hhbnRSZWZlcmVuY2UiOiJNWS1PUkRFUi1JRC0xMjMiLCJtZXJjaGFudFJlZmVyZW5jZURpc3BsYXkiOiJNWS1PUkRFUi1JRC0xMjMiLCJwYXltZW50U3RhdHVzIjoiUEFJRCIsImdyYW5kVG90YWwiOjk5Ljk5LCJjdXJyZW5jeSI6IkVVUiIsIm1lcmNoYW50X3JlZmVyZW5jZSI6Ik1ZLU9SREVSLUlELTEyMyIsIm1lcmNoYW50X3JlZmVyZW5jZV9kaXNwbGF5IjoiTVktT1JERVItSUQtMTIzIiwicGF5bWVudF9zdGF0dXMiOiJQQUlEIn0.X6Ym70AA1bYIsKyNc1NL4NpznKXCrGX5xacqc1ovtuE';
// The Order ID you got from Montonio as a response to creating the orderconst montonioOrderId = 'the-montonio-order-uuid';
// Use your secret key to verify the orderTokenconst decoded = jwt.verify(orderToken, 'MY_SECRET_KEY'); // can throw
if ( decoded.paymentStatus === 'PAID' && decoded.uuid === montonioOrderId && decoded.accessKey === 'MY_ACCESS_KEY') { // payment completed} else { // payment not completed}<?php/** * We recommend using Firebase's php-jwt package to verify * Json Web Tokens. You can install it with composer: * > composer require firebase/php-jwt * More information can be found at * https://github.com/firebase/php-jwt */use \Firebase\JWT\JWT;use \Firebase\JWT\Key;
// Fetched from the URL for returnUrl and from POST body->orderToken when it's a notification$orderToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoidGhlLW1vbnRvbmlvLW9yZGVyLXV1aWQiLCJhY2Nlc3NLZXkiOiJNWV9BQ0NFU1NfS0VZIiwibWVyY2hhbnRSZWZlcmVuY2UiOiJNWS1PUkRFUi1JRC0xMjMiLCJtZXJjaGFudFJlZmVyZW5jZURpc3BsYXkiOiJNWS1PUkRFUi1JRC0xMjMiLCJwYXltZW50U3RhdHVzIjoiUEFJRCIsImdyYW5kVG90YWwiOjk5Ljk5LCJjdXJyZW5jeSI6IkVVUiIsIm1lcmNoYW50X3JlZmVyZW5jZSI6Ik1ZLU9SREVSLUlELTEyMyIsIm1lcmNoYW50X3JlZmVyZW5jZV9kaXNwbGF5IjoiTVktT1JERVItSUQtMTIzIiwicGF5bWVudF9zdGF0dXMiOiJQQUlEIn0.X6Ym70AA1bYIsKyNc1NL4NpznKXCrGX5xacqc1ovtuE';
// The Order ID you got from Montonio as a response to creating the order$montonioOrderId = 'the-montonio-order-uuid';
// Add a bit of leeway to the token expiration timeJWT::$leeway = 60 * 5; // 5 minutes
// Use your secret key to verify the orderToken$decoded = JWT::decode( $orderToken, new Key('MY_SECRET_KEY', 'HS256'),);
if ( $decoded->paymentStatus === 'PAID' && $decoded->uuid === $montonioOrderId && $decoded->accessKey === 'MY_ACCESS_KEY') { // Payment completed} else { // Payment not completed}?>'''We recommend using the PyJWT package to verifyJson Web Tokens. You can install it with pip:> pip3 install PyJWTMore information can be found athttps://pyjwt.readthedocs.io/en/latest/'''import jwt
# Fetched from the URL for returnUrl and from POST body->orderToken when it's a notificationorderToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoidGhlLW1vbnRvbmlvLW9yZGVyLXV1aWQiLCJhY2Nlc3NLZXkiOiJNWV9BQ0NFU1NfS0VZIiwibWVyY2hhbnRSZWZlcmVuY2UiOiJNWS1PUkRFUi1JRC0xMjMiLCJtZXJjaGFudFJlZmVyZW5jZURpc3BsYXkiOiJNWS1PUkRFUi1JRC0xMjMiLCJwYXltZW50U3RhdHVzIjoiUEFJRCIsImdyYW5kVG90YWwiOjk5Ljk5LCJjdXJyZW5jeSI6IkVVUiIsIm1lcmNoYW50X3JlZmVyZW5jZSI6Ik1ZLU9SREVSLUlELTEyMyIsIm1lcmNoYW50X3JlZmVyZW5jZV9kaXNwbGF5IjoiTVktT1JERVItSUQtMTIzIiwicGF5bWVudF9zdGF0dXMiOiJQQUlEIn0.X6Ym70AA1bYIsKyNc1NL4NpznKXCrGX5xacqc1ovtuE'
# The Order ID you got from Montonio as a response to creating the ordermontonioOrderId = 'the-montonio-order-uuid'
try: decoded = jwt.decode( orderToken, 'MY_SECRET_KEY', algorithms=['HS256'] )
except jwt.exceptions.InvalidSignatureError as identifier: pass # Token validation failed
if ( decoded['paymentStatus'] == 'PAID' and decoded['uuid'] == montonioOrderId and decoded['accessKey'] == 'MY_ACCESS_KEY'): pass # Payment completedelse: pass # Payment not completedThe values for paymentStatus are described here: Lifecycle of an Order.
Responding to the webhook
Respond to our webhooks with HTTP status code 200 OK or 201 Created. If you don’t respond with 200 or 201, we will retry the webhook 13 times over the next 48 hours until a successful response is received.