← Back

Mobilum widget integration

The Mobilum widget is a highly customisable widget that handles any/or of the following:

  • User order where the user-specified what he wants to buy and optionally to what address

  • User management (login, forgot password etc.) and KYC

  • Card processing

  • Cryptocurrency delivery

Based on the requirements, the flow and screens will change accordingly and thus there might be two different ways of calling the widget: If Mobilum will handle the ordering process, where the user will inform us what he wants to buy then a call to the GetWidget must be made. On the other hand, if the partner will handle the order selection screens, then a call to the widget/GetWidgetWithOrderPrice or widget /GetWidgetWithOrderAmount must be made.

The following table explains, based on the scenario what to call:

Mobilum Widget Endpoints by Use-case

Use-case

Method

Mobilum will handle order screen where a user can enter order details (amount, crypto currency etc.)

/widget/GetWidget

Mobilum will handle only user and KYC aspects of the transaction

/widget/GetWidget

Partner will send details (amount or price) and Mobilum will only extract payment from the card and inform of status

widget/GetWidgetWithOrderPrice or widget/GetWidgetWithOrderAmount

Partner will send details (amount or price) and Mobilum will only extract payment from the card and inform of status

widget/GetWidgetWithOrderPrice or widget/GetWidgetWithOrderAmount

Throughout the transaction process, the system will continuously update the price until the transaction is actually confirmed (card details are entered and executed). This is known as slippage and to minimize it the crypto-currency price will remain updated. If a call to the GetWidgetWithOrderPrice is made, the system will maintain the price constant (for example 250 USD) and update the quantity bought based on the latest price. On the other hand, if a call is made to GetWidgetWithOrderAmount then the system will maintain the crypto-currency amount (1 BTC) and update the price accordingly. If both price and amount are passed, the price will be used.

Basic setup - GetWidget

In this method, the system will display the order or login screen as the first screen. In order to integrate in such a scenario a call to https://api-dev.mobilum.com/widget/GetWidget (POST) must be made ensuring you set the following attribute in the header:

apiKey: { your API key }

Optionally the following parameters can be sent and they will set the widget with these properties

Parameter

Type

Description

walletAddress

string

The destination wallet where funds will be transferred.

cryptoCurrency

string

Ticker name for the crypto-currency (e.g., BTC, ETH).

additionalData

string

When making any callback from the widget, the system will add this information. It can be used, for example, as a key between the order on Mobilum side and the partner system.

fiatCurrency

string

The currency name (e.g., EUR, USD, CAD).

fiatAmount

decimal

The price in FIAT currency to extract from the user’s card.

amount

decimal

The amount of crypto-currency to deliver to the user.

If the request was successful then a response similiar (not identical) to the below should be expected:

{
"redirectUrl": "Order",
"responseType": null,
"followUrl": null,
"success": true,
"result": {
    "widgetBase64Html": "PHN0eWxlIGlkPSdtb2JpbHVtLXdpZGdldC1jc3MnPjwvc3R5bGU+PGxpbmsgaHJlZj0naHR0cHM6Ly93aWRnZXQtZGV2Lm1vYmlsdW0uY29tL2Nzcy9hcHAuY3NzJyByZWw9J3N0eWxlc2hlZXQnPjxkaXYgaWQ9J21vYmlsdW0td2lkZ2V0JyAgZGF0YS1hcGlrZXk9JzE2REU5RjhCLUI0MTQtNEM1MC1CM0M4LUNGODM1NTY4M0E0Mic+PGRpdiBpZD0ndXNlclVVaWQnIHN0eWxlPSdk aXNwbGF5Om5vbmUnPjwvZGl2PjxkaXYgaWQ9J2NyeXB0b0N1cnJlbmN5JyBzdHlsZT0nZGlzcGxheTpub25lJz5FVEg8L2Rpdj48ZGl2IGlkPSd3YWxsZXRBZGRyZXNzJyBzdHlsZT0nZGlzcGxheTpub25lJz4weGI5M0UzNThFMDM3QjJGNTNEY0Q1MWM5NUMzNTQ4ZkM4OTRjNTdmQTE8L2Rpdj48ZGl2IGlkPSdhZGRpdGlvbmFsRGF0YScgc3R5bGU9J2Rpc3BsYXk6bm9uZSc+S2V5RnJvbVBhcnRuZXJTaXRlPC9kaXY+PGRpdiBpZD0nd2lkZ2V0VXVpZCcgc3R5bGU9J2Rpc3BsYXk6bm9uZSc+YmJiMWUwNmEtMzI1OS00YzdmLWFmMGMtZDIwZTc0ZDY3ZGU1PC9kaXY+PC9kaXY+PHNjcmlwdCBzcmM9J2h0dHBzOi8vd2lkZ2V0LWRldi5tb2JpbHVtLmNvbS9qcy9hcHAuanMnPjwvc2NyaXB0Pg==",
    "widgetBase64ScriptUrl": "aHR0cHM6Ly93aWRnZXQtZGV2Lm1vYmlsdW0uY29tL2pzL2FwcC5qcw==",
    "widgetBase64CssUrl": "aHR0cHM6Ly93aWRnZXQtZGV2Lm1vYmlsdW0uY29tL2Nzcy9hcHAuY3Nz",
    "User": {
    "userUuid": null,
    "token": null
    },
    "WidgetUuid": "024dce56-0a6e-45de-96b6-507a1c9b06e5"
},
"errors": []
}

In order to understand how to process the response, please go to the section. Processing the Response

Processing the response

The response is made up of the following important elements:

Element

Description

widgetBase64Html

Contains the HTML for the whole page in base64 encoding, including the script and style URLs.

widgetBase64ScriptUrl

Contains only the script URL encoded in base64.

widgetBase64CssUrl

Contains only the script URL encoded in base64.

Below is an example of a successful decoded form

// Inject the Mobilum widget CSS placeholder
const styleTag = document.createElement('style');
styleTag.id = 'mobilum-widget-css';
document.head.appendChild(styleTag);

// Load the Mobilum widget stylesheet
const linkTag = document.createElement('link');
linkTag.rel = 'stylesheet';
linkTag.href = 'https://widget-dev.mobilum.com/css/app.css';
document.head.appendChild(linkTag);

// Build the widget container
const widget = document.createElement('div');
widget.id = 'mobilum-widget';
widget.setAttribute('apikey', '***');

// Hidden inputs for widget configuration
const userUuid = document.createElement('div');
userUuid.id = 'userUUid';
userUuid.style.display = 'none';
widget.appendChild(userUuid);

const cryptoCurrency = document.createElement('div');
cryptoCurrency.id = 'cryptoCurrency';
cryptoCurrency.style.display = 'none';
cryptoCurrency.textContent = 'ETH';
widget.appendChild(cryptoCurrency);

const walletAddress = document.createElement('div');
walletAddress.id = 'walletAddress';
walletAddress.style.display = 'none';
walletAddress.textContent = '0xb93E358E037B2F53DcD51c95C3548fC894c57fA1';
widget.appendChild(walletAddress);

const additionalData = document.createElement('div');
additionalData.id = 'additionalData';
additionalData.style.display = 'none';
additionalData.textContent = '******';
widget.appendChild(additionalData);

const widgetUuid = document.createElement('div');
widgetUuid.id = 'widgetUuid';
widgetUuid.style.display = 'none';
widgetUuid.textContent = 'bbb1e06a-3259-4c7f-af0c-d20e74d67de5';
widget.appendChild(widgetUuid);

// Attach the widget to the page
document.body.appendChild(widget);

// Load the Mobilum widget script
const scriptTag = document.createElement('script');
scriptTag.src = 'https://widget-dev.mobilum.com/js/app.js';
document.body.appendChild(scriptTag);

In some languages, HTML containig <script> and <style> cannot be added directly in the body. In this case we provide seperate elements so they can be added in the <head> section while integration.

The final step is to inject the resultant HTML into an element and the widget should load accordingly. Below is an example of the layout once the widget has loaded although the actual layout can change.

Below is an example of the layout once the widget has loaded although the actual layout can change.

../_images/widget-integeration-1.png

Example

Below is an example, using vanilla HTML and javascript to demonstrate the above. Adaptation of the below into any langugage should ba straightforward process. Please not that the functionality of the below is based on a specific scenario and might not work for the setup of your API key.

<head>
  <script>
    (function(){
      var data = {};
      var requestOptions = {
        method: 'POST',
        headers: {
          apiKey: '16de9f8b-b414-4c50-b3c8-cf8355683a42',
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(data),
        params: {
          additionalData: ''
        }
      };
      fetch('https://api-dev.mobilum.com/widget/GetWidget', requestOptions)
        .then(response => response.json())
        .then(
          async (data = { result }) => {
            var element = document.getElementById('widgetContainer');
            if (element) {
              element.innerHTML = atob(data.result.widgetBase64Html);
              var script = document.createElement('script');
              script.async = true;
              script.src = atob(data.result.widgetBase64ScriptUrl);
              element.appendChild(script);
            }
          },
        );
    })();
  </script>
</head>
<body style="background-color:#d4e6f1">
  <div id="widgetContainer"></div>
</body>

Error Codes All error codes are handled directly by the widget and the appropriate error message is displayed to the client. The below list defines the low level error codes which are split into two: API level error codes and payment error codes.

Mobilum API Error Messages

Message

Error Code

Invalid username or password

E001

Email already exist

E002

Missing API key

E007

3DS not supported by the card

E013

User not found

E014

Network error

E020

Invalid currency

E021

Invalid email provided

E027

KYC process error

E036

Unsupported currency pair

E044

Region is blocked

E050

Transaction rejected

E0150

Transaction rejected, currency not supported

E0151

Transaction rejected, issuer not supported

E0152

Transaction declined, problem with MIP connection

E0153

Failed to fetch transaction details

E0154

Transaction not exist

E0155

Transaction rejected, terminal not supported 3ds

E0156

Transaction rejected, card not supported 3ds

E0157

Transaction rejected, maximum transaction amount limit exceeded

E0158

Transaction rejected. Data center card is expired.

E01580

Transaction rejected. Not found Card Authentication (3DS 2.X)

E01581

Transaction rejected. Card Authentication not match to transaction type (3DS 2.X)

E01582

Transaction rejected, cof card not found in db

E01590

Transaction rejected, initial cof transaction not approved

E01591

Transaction rejected, recurring card not found in db

E01592

Transaction rejected, initial recurring transaction not approved

E01593

Transaction rejected, transactionXId parameter had registered for another transaction

E01594

Transaction rejected, terminal blocks transactions with card issued in given card country

E01596

Transaction rejected, 3DS version of terminal are not compatible with 3DS version defined in request

E01597

Transaction rejected, ECI value is not available for card provider

E01598

Transaction declined, processing timeout

E01599

Transaction declined, 3DS 2.X flow invoked for other card number than specified in the request

E01600

Transaction rejected, terminal is blocked for provider

E01601

Installment Payment initial not found

E05000

Installment Payment already confirmed

E05001

Installment Payment already cancelleded

E05002

Installment Payment already pay in full

E05003

Installment Payment not confirmed in required time

E05004

Installment Payment Initial was failed

E05005

Installment Payment could not find valid installment plan for requested uuid

E05006

Installment Payment Option not allowed for transaction

E05007

Installment Payment missing plan uuid

E05008

Installment Payment missing request number of installments

E05009

Installment Payment requested number of installments is outside of allowed range

E05010

Installment Payment operation not allowed, installment type allows ISSUER_FINANCED installments

E05011

Installment Payment outside 3ds request is required for terminal

E05014

Installment Payment unsupported card provider VISA

E05015

Clearing of the payment is procesing

E0170

Can’t clear payment

E0171

Can’t clear payment, the time is over

E0172

Payment is automatically cleared

E0173

Payment is already cleared

E0174

Can’t clear payment, clearing amount is to low

E0175

Can’t clear payment, clearing amount is to high

E0176

Reversal of the payment is procesing

E0160

Can’t reversal payment

E0161

Payment is already reversed

E0162

Payment is already refunded

E0163

Installment Payment reverse not allowed

E05012

Refund rejected, no MC/VISA response

E0180

Refund rejected by MC/VISA

E0181

Refund transaction amount to low

E0182

Refund transaction amount to high

E0183

Transaction is refunded

E0184

Transaction not cleared, can’t refund

E0185

Refund is already processing

E0186

Transaction is chargeback reported

E0187

Installment Payment refund not allowed

E05013

Transaction rejected, card is blocked

E0200

Transaction rejected, bin is blocked

E0201

Transaction rejected, terminal is blocked

E0202

Transaction rejected, token is blocked

E0203

Transaction rejected, user is on aml list

E0204

Transaction rejected, bank is blocked

E0205

Transaction rejected, to many reject transactions and card is blocked

E0206

Error mpi V2 - failed internal

E0300

Three DS Method Data mismatch

E0301

Error on parsing Three DS Method Data

E0302

Card number not Enrolled in 3DS v2

E0303

Invalid card authentication id

E0304

Issuer card is not supported

E0305

Invalid card number

E0306

Card Authentication not found

E0307

Terminal not exists

E0132

Protocol version is not supported

E0309

Invalid Three DS Method Data

E0310

Mobilum widget WebHooks

Introduction

The widget is a self-contained process that runs on the server, with all information flowing directly from the client to the widget, bypassing the hosting site. Therefore, callbacks are the mechanism by which the widget communicates with the hosting site when a particular event has occurred.

Events

A callback is sent from the widget on the following events:

  • Login

  • Register

  • UpdatedKycStatus

  • UpdatedTransactionStatus

  • TransactionComplete

Although these events are supported, each API key can subscribe to one or more events. During setup, please inform our integration team which events you would _not_ like to subscribe to.

Security

There are 3 important security mechanisms employed by the system

1 There is no sensitive information passing through the callbacks, nor any information by which a user can be identified. The callbacks flow from one direction, that is from the widget to the endpoint URL. For example, the callbacks will send userUuid 123 successfully logged in but there is no way to know userUuid is loui mercieca.

2 All callbacks are post methods over HTTPS to ensure channel security

3 In the body of the callback there is signature field that ensures that the body has not been tampered with. The signature is generated as follow:

a. Load the JSON body excluding the signature field.

b. Base64 encode the result of step 1.

c. MD5 hash the result of step 2.

The result should be compared with the signature field. If it matches that the body is in the original formal. If it doesn’t then the whole callback should be ignored.

Webhook body

The webhook body is made up of a standard response, containing 4 sections:

Webhook Body Sections

Section

Description

Notes

General Information

Contains generic information such as Action and ExternalIdentifier

The ExternalIdentifier contains the same value passed inside the additionalData parameter when calling getWidget, getWidgetWithOrderAmount or getWidgetWithOrderPrice

User

Contains user-specific information such as status of KYC

Order

Contains information such as amount/price ordered and currencies

Transaction

Contains information about the transaction such as status

During each phase of the order, one or more sections can be filled. For example, in the login action only the user information is present whereas after a transaction is complete all sections will be filled.

Please find below what sections are used for each action.

Callback Events Coverage

Action

General Information

User

Order

Transaction

Login

Yes

Yes

No

No

Register

Yes

Yes

No

No

UpdatedKycStatus

Yes

Yes

No

No

UpdatedTransactionStatus

Yes

Yes

Yes

Yes

TransactionComplete

Yes

Yes

Yes

Yes

The response object information can be found also at the (at the bottom of the page in the object called CallbackData) API documentation

Callback data

The following is an example of a complete callback

{
  "user": {
    "UserUUid": "d20ceaf6-8ef5-4acb-a2b0-2b6b7e8dc778",
    "KycComplete": true,
    "KycVerfied": true
  },
  "transaction": {
    "Uuid": "8252c1ce-5fb5-4dd3-a81d-5ee7ca81b238",
    "Status": "CLEARED",
    "Completed": true
  },
  "order": {
    "Uuid": "19e9b264-3ba1-445d-b655-df56d42e0542",
    "Crypto": "BTC",
    "Amount": "0.0061337000",
    "Currency": "USD",
    "QuotedPrice": "200.000000",
    "Wallet": "3FZbgi29cpjq2GjdwV8eyHuJJnkLtktZc5",
    "Fee": {
      "Id": 1906,
      "FlatRate": 0,
      "CommissionAmount": 0.2,
      "CommissionRate": 0.1,
      "Margin": 0.2
    }
  },
  "Action": "TransactionComplete",
  "ApiKey": "16de9f8b-b414-4c50-b3c8-cf8355683a42",
  "ExternalIdentifier": "",
  "Signiture": "F2B807D9352718E7738A30EBE9FA8ACD"
}

Mobilum widget UI/UX modifications

Default CSS

Widget is delivered with default CSS and can be displayed without any design work required. But it is possible to upload modified CSS to change widget look and feel (see Mobilum widget integration).

../_images/widget-integeration-1.png

Widget layout is made to be responsive to adapt screen width. Breaking points, when layout changes are < 500px, 500px - 766px, 767px <. Although any CSS can be adjusted to fit your design, it’s highly unrecommended to change the general layout and structure of elements, as it can give unexpected results in different resolutions.

When widget is injected in site, your custom CSS is placed in an element with an ID “ “. Keep in mind, that rest of the mobilum-widget-css widget, together with its own default CSS, is placed after that element, which means, that if you have your own custom CSS selector, which matches selector in widget default CSS, your style will be overridden.

To make sure that your custom CSS is given a priority when browser renders the content, you can either use your selector to be more precise in terms of targeting an element, than selector in widget CSS. For example:

Widget CSS:

.MBMWidget_title {
  font-size: 16px;
}

Your CSS:

.MBMWidget_General .MBMWidget_title {
  font-size: 18px;
}

Whole widget is wrapped in a DIV element with a class “MBMWidget_General”, you can use that to target any element more specifically in the scope of whole widget. Each separate page in the widget then is wrapped inside DIV with unique class name starting with “MBMWidget_” + page name, for example “MBMWidget_Login”, “MBMWidget_Register” etc. Use those if you wish to style any element only for one page.

Updated CSS version 1

This modification is very simple. It changes colors and size of some elements.

../_images/widget-integeration-2.png

CSS modifications shown below.

button.MBMWidget_GeneralButton.MBMWidget_full-width.MBMWidget_Exchange_ButtonCheckout.MBMWidget_defaultAppButton {
  height: 40px;
  outline-width: 0;
  border-width: 0;
  border-radius: 4px;
  transition: .5s;
  color: #fff;
  font-size: 16px;
  font-weight: 500;
  background-color: #ff4000 !important;
  padding: 7px 10px !important;
}

.MBMWidget_GeneralInput input {
  height: 36px !important;
}

.MBMWidget_Exchange_form label {
  color: #ff4000 !important;
}

select.MBMWidget_my-2.MBMWidget_full-width {
  width: 80px;
  border-color: #ff4000 !important;
  color: #fff;
  outline-width: 1px !important;
  height: 38px !important;
  font-size: 16px !important;
  font-weight: 500 !important;
  border-radius: 4px !important;
  outline-color: #ff4000 !important;
  background-color: #ff4000 !important;
  padding: 0 0 0 9px !important;
  margin: 0 !important;
}

Updated CSS version 2

CSS update can change shape of the form fields.

../_images/widget-integeration-3.png

CSS modifications shown below.

.MBMWidget_GeneralInput input {
  height: 42px!important;
  border-radius: 0px!important;
}
.MBMWidget_Exchange_form label {
  color: black!important;
  font-size: 16px!important;
}
select.MBMWidget_my-2.MBMWidget_full-width {
  width: 82px;
  border-color: #6b0606!important;
  color: #fff;
  outline-width: 1px!important;
  height: 42px!important;
  font-size: 16px!important;
  font-weight: 500!important;
  border-radius: 0px!important;
  outline-color: #6b0606!important;
  background-color: #6b0606!important;
  padding: 0px 0px 0px 9px!important;
  margin: 0px!important;
}
button.MBMWidget_GeneralButton.MBMWidget_full-width.MBMWidget_Exchange_ButtonCheckout.MBMWidget_defaultAppButton {
  background-color: #6b0606!important;
}
.MBMWidget_GeneralInput input:focus {
  box-shadow: 0 0 0 1px lightgrey;
  border-color: lightgray!important;
  outline: 0px!important;
  border: 1px solid lightgray!important;
}
.MBMWidget_Exchange_General .MBMWidget_GeneralSelect select option {
  background-color: white;
  color: black;
}

Updated CSS version 3

Background of the widget can be changed too.

../_images/widget-integeration-4.png

CSS modifications shown below.

.MBMWidget_General {
  background: linear-gradient(45deg, black, #f30c0c9e)!important;
}

.MBMWidget_title {
  font-size: 18px!important;
  color: white!important;
}

.MBMWidget_Exchange_form label {
  color: white!important;
  font-size: 16px!important;
}

.MBMWidget_Exchange_preview span {
  font-size: 14px!important;
  color: white!important;
}

span.MBMWidget_Exchange_FormExchangeSelectedCryptoCurrency.MBMWidget_btc_green {
  color: white!important;
}

.MBMWidget_General a {
  color: white!important;
}

.MBMWidget_Exchange_General .MBMWidget_Verify_customSelect {
  position: absolute;
  right: 25px;
  bottom: unset!important;
  transform: translateY(34px)!important;
}

select.MBMWidget_my-2.MBMWidget_full-width {
  width: 82px;
  border-color: #6b0606!important;
  color: #fff;
  outline-width: 1px!important;
  height: 42px!important;
  font-size: 16px!important;
  font-weight: 500!important;
  border-radius: 0px!important;
  outline-color: #6b0606!important;
  background-color: #6b0606!important;
  padding: 0px 0px 0px 9px!important;
  margin: 0px!important;
}

.MBMWidget_GeneralInput input {
  height: 40px!important;
  border-radius: 0px!important;
}

button.MBMWidget_GeneralButton.MBMWidget_full-width.MBMWidget_Exchange_ButtonCheckout.MBMWidget_defaultAppButton {
  background-color: #6b0606!important;
}

.MBMWidget_GeneralInput input:focus {
  box-shadow: 0 0 0 1px lightgrey;
  border-color: lightgray!important;
  outline: 0px!important;
  border: 1px solid lightgray!important;
}

.MBMWidget_Exchange_General .MBMWidget_GeneralSelect select option {
  background-color: white;
  color: black;
}

Widget integrations as standalone KYC service

Following are instructions and examples, how to create Widget integration, which would work as standalone KYC verification system in other projects.

Prerequisite:

Site has to be created in database with “FirstPage” set to “PerformKYC” and “KycProvider” to “NULL”

When site is created, Widget has to be called with endpoint, containing data about the user, to whom the KYC will be performed. GetWidget() In request body, following user fields are required:

{
  "Email": "string",
  "Name": "string",
  "Surname": "string",
  "Password": "string (MD5 hash)"
}

Response should contain newly created user UUID, by whom the further status of KYC process can be checked:

{
  "result": {
    "User": {
      "userUuid": "string"
    }
  }
}

Checking status

GET GetUserKycCompletedStatus(string userUUid)

Returns true/false if user has completed KYC (regardless of the result).

GET GetUserKycVerifiedStatus(string userUUid)

Returns true/false if user has completed KYC and has been verified.

If user has completed KYC, but is not verified, there is still a chance, that the process can be repeated in the same session and status can change. For example, blurry photo of ID document can be fixed by resubmitting a new photo in the same session.

Full example of such integration:

<head>
  <script>
    (function(){
      var data = {
        Email: '[email protected]',
        Name: 'Name',
        Surname: 'Surname',
        Password: 'f9227cc1d2ea2ddf8e1435942a610380'
      };
      var requestOptions = {
        method: 'POST',
        headers: {
          apiKey: 'YOUR-API-KEY',
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(data)
      };
      fetch('https://api-dev.mobilum.com/widget/GetWidget', requestOptions)
        .then(response => response.json())
        .then(async (data = { result }) => {
          var element = document.getElementById('widgetContainer');
          if (element) {
            element.innerHTML = atob(data.result.widgetBase64Html);
            var script = document.createElement('script');
            script.async = true;
            script.src = atob(data.result.widgetBase64ScriptUrl);
            element.appendChild(script);
          }
        });
    })();
  </script>
</head>
<body>
  <div id="widgetContainer"></div>
</body>