Introduction


Ripio Fintech Widget allows partners to enable Buying and Selling crypto assets for their end users by writing a few lines of code.

The partner can customize the widget’s UI’s look and feel with their own styles to match their product’s branding and design manual.

The widget is developed to provide its functionality in any browser, web framework (Reactjs, Nextjs, Vuejs, Vuetify, Angularjs, etc), or mobile (React Native) that supports native web components.

If you are going to use our B2B Sandbox test environment, keep in mind that it will be unavailable for maintenance from 01:00 GMT to 06:00 GMT. You can chek the equivalence in your local time here.

Integration


Integration consists on the following steps:

  1. Authentication and integration: Obtaining a JWT token to interact with all services provided by Ripio B2B, and configuring Ripio’s web component.

  2. Webhooks: Implementing an (HTTPS) endpoint that Ripio will use to send the following requests:

    • Purchase Approval: Request approval of a given user’s purchase transaction. This allows for confirming if the user has enough money to make the purchase, freezing funds, or any other type of business logic that needs to be executed.

    • Transaction Result: Report the result of an operation, be it BUY or SALE, for a given user. This allows to update the user’s balance or any other type of business logic that needs to be executed.

Authentication: Obtain JWT Token


In order to obtain a JWT Token, the partner should:

  • Have a client_id and client_secret, which are credentials provided by Ripio after signing the corresponding agreements and contracts. It is the partner’s responsibility to secure these credentials. They uniquely identify the partner as a Ripio client.

  • Provide a unique id that represents the user logged into your system. It must be a unique and unrepeated identifier of a user of your system. Later, this value is used in the request for approval of a purchase operation or when reporting the result of an operation (see Webhooks). In the documentation we refer to it as the external_ref.

To obtain a JWT token it is necessary to have: client_id, client_secret y external_ref.

To generate the token send a request using the HTTP POST method to the following endpoint: https://sandbox-b2b.ripio.com/w/api/v1/auth

The request must:

  • Define Content-Type: Content-Type: application/x-www-form-urlencoded

  • Send the username field with the format: username=<client_id>:<external_ref>

  • Send the password field with the format: password=<client_secret>

The value of the username field is a concatenation of client_id + “:” + external_ref.

An example using CURL in sandbox environment:

curl \
  --location --request POST 'https://sandbox-b2b.ripio.com/w/api/v1/auth' \
  --header 'Content-Type: application/x-www-form-urlencoded' \
  --data-urlencode 'username={client_id}:{external_ref}' \
  --data-urlencode 'password={client_secret}'

Token expiration date is 10 hours. Once the token has expired, the widget displays an error on the session expired screen.

Integration: Widget Configuration


The widget is a native web component. To use it, in an HTML template you must:

  • Import the widget script:
<script type="module" src="https://d2pneqdaei3b3x.cloudfront.net/" />
  • Use tag <ripio-widget /> and pass token JWT as a parameter, example:
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
  </head>
  <body>
    <div>
       <ripio-widget token="fake.jwt.token" currency="USD" lang="ENG" />
    </div>
    <script type="module" src="https://d2pneqdaei3b3x.cloudfront.net/"></script>
  </body>
</html>

For the widget to be responsive, it is very important to configure the viewport as shown in the previous example.

In addition to the token, the web component <ripio-widget /> supports the following parameters:

  • currency: It is the FIAT money currency that is read from the user settings. It will be used as the basis for the transactions carried out by the user.

  • lang: It is the language in which the UI texts will be displayed in the widget.

Passing parameters in the URL


To pass parameters via URL, the following query parameters must be used

  • _to: for JWT token
  • _la: for language (lang)
  • _cu: for currency

Webview integration


The following method is recommended to be used only via Webview. Please note that using the following method outside of a Webview is a security risk. The full URL may become available in the browser history, be tracked by ISPs, and others.

If your implementation uses any of the following: React Native, Ionic, Angular, Android, or another framework that provides a Webview component, you can easily integrate with B2B widget by pointing Webview component’s URL to the following:

https://d2pneqdaei3b3x.cloudfront.net/index.html

Here is an example on how to integrate with the widget, configuring parameters in the URL, using react-native-webview:

import React from "react";
import { WebView } from "react-native-webview";

export default function App() {
  const baseUrl = "https://d2pneqdaei3b3x.cloudfront.net/index.html"; // URL where `index.html` os served
  const qToken = "_to=fake.jwt.token"; // JWT Token
  const qLanguage = "_la=es"; // Query param that defines LANGUAGE
  const qCurrency = "_cu=ars"; // Query param that defines base FIAT CURRENCY
  const qFontName = "_fn=Roboto+Mono"; // Font Name
  const qFontOpts = "_fo=Roboto+Mono%26display=swap"; // Font Style options 
  const qFontDefault = "_fd=fantasy";  // Default Font if "_fn" is not available
  const uri = `${baseUrl}?${qFontName}&${qFontOpts}&${qFontDefault}&${qLanguage}&${qCurrency}&${qToken}`; // Complete webview's target URL
  return <WebView source={{ uri: `${uri}` }} javaScriptEnabled useWebKit />;
}

This is an example of a full URL:

https://d2pneqdaei3b3x.cloudfront.net/index.html?_cu=cop&_la=es&_to=un.token.valido

Try copy-pasting this URL in your browser to see an error screen with customized font style (this error is due to an invalid token):

The widget uses the Font Family configured through the URL params _fo and _fn

Available Fonts


You can use any font supported by web browsers (eg Arial, Fantasy, or any other that is listed when using your browser’s inspector).

To define a default Font Family use the following URL param:

  • _fd: Default Font Name. Supported Font Families to be used with _fd ar thoe built in the browser:

Aditionally, you can use any font defined in Browse Fonts - Google Fonts. To do that, use the following URL params:

  • _fn: Font Name (ex: Roboto+Mono)
  • _fo: Font Options.

Example: Roboto+Mono from Google Fonts @import option:

<style>
  @import url('https://fonts.googleapis.com/css2?family=Roboto+Mono&display=swap');
</style>
font-family: 'Roboto+Mono', monospace;

This leaves the full URL as:

  • _fn=Roboto+Mono (be sure to replace empty space character “ “ for “+”)

  • _fo=Roboto+Mono%26display=swap (be sure to replace all occurrences of ampersand character “&“ for “%26”)

Example URL: https://d2pneqdaei3b3x.cloudfront.net/index.html?_fn=Roboto+Mono&_fo=Roboto+Mono%26display=swap

Webhooks


Ripio reports events related to the operation of a user invoking webhooks that must be provided/implemented by the partner.

Messages always have the following JSON structure:

{
  "message": {},
  "message_type": "",
  "signed_message": none
}

Messages are always sent using HTTP POST method.

signed_message field it’s a JWT and its token are message and message_type. In order to use this feature, you must provide us with a pre_shared_key (it’s a secret (string) shared between both parties that we use to create/cipher the message). This adds a layer of security that let’s our partners verify that Ripio is who created the message and that it wasn’t modified in transit.

Ripio sends a request in the following cases:

  • Purchase Operation Approval (BUY): A request is sent with the HTTP POST method to ask the partner for approval before executing a trade. The payload is the following.
{
  "message": {
    "op_type": "BUY",
    "id": "df9d41d8-ac80-4125-9738-ce1fcb8dc523",
    "quote_id": "cf6c57ea-03a1-41dc-99e8-b91cc1b7333e",
    "amount": "1500",
    "external_ref": "00010203-0405-0607-0809-0a0b0c0d0e0f"
  }, 
  "message_type": "transaction_approval_request",
  "signed_message": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtZXNzYWdlIjp7InN1Y2NlZWQiOnRydWUsInF1b3RlIjp7ImlkIjoiNGY5ZGE1N2UtOTZkMC00NGM3LTg3NGUtY2E5ZTAyODg3M2M4IiwicXVvdGVfaWQiOiIzYjJkZWMyZi1iZGZkLTRiNTgtOGU4MC0xODY1NzY4NTE3MjUiLCJ0eG5faWQiOiJiNTcxMDg2OS0wOGU1LTQxZDMtOWQxOS04YmU4MTE2ZTFhYmYiLCJyYXRlIjoiODgzNjQuMDE0MTE0MDIiLCJjaGFyZ2VkX2ZlZSI6IjMwLjYxMTk0NTQxIiwiYmFzZV9hbW91bnQiOiIwLjAxNzMyMTY3IiwicXVvdGVfYW1vdW50IjoiMTUwMC4wMDAwMDAwMCIsImJhc2VfYXNzZXQiOiJCVEMiLCJxdW90ZV9hc3NldCI6IkNPUCIsImNyZWF0ZWRfYXQiOiIyMDIzLTAxLTE2VDE2OjAxOjA1LjI3NTcyM1oiLCJvcF90eXBlIjoiU0VMTCIsImV4dGVybmFsX3JlZiI6ImIyYi10ZXN0cyJ9fSwibWVzc2FnZV90eXBlIjoidHJhbnNhY3Rpb25fcmVzdWx0In0.Xe5wNQE7On0-qiS3EabrzocvdELWSHS8Llc1VFIohnY"
}

To identify this request type, validate message_type: transaction_approval_request

  • Operation Result: A request is sent with the HTTP POST method informing the partner of the result of a transaction, so that it can update the information corresponding to the user such as balances, and other relevant business logic. In this case, the payload is as follows.
{
  "message": {
    "succeed": True,
    "quote": {
      "id": "df9d41d8-ac80-4125-9738-ce1fcb8dc523",
      "quote_id": "cf6c57ea-03a1-41dc-99e8-b91cc1b7333e",
      "txn_id": "57b526a5-092b-4d61-a394-b80e4bad5fcf",
      "rate": "100290.76161713",
      "charged_fee": "628.74685436",
      "base_amount": "0.00868727",
      "quote_amount": "1500.00000000",
      "base_asset": "BTC",
      "quote_asset": "ARS",
      "op_type": "BUY",
      "external_ref": "00010203-0405-0607-0809-0a0b0c0d0e0f",
      "created_at": "2022-11-02T15:37:22.765995Z"
    }
  },
  "message_type": "transaction_result",
  "signed_message": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtZXNzYWdlIjp7InN1Y2NlZWQiOnRydWUsInF1b3RlIjp7ImlkIjoiNGY5ZGE1N2UtOTZkMC00NGM3LTg3NGUtY2E5ZTAyODg3M2M4IiwicXVvdGVfaWQiOiIzYjJkZWMyZi1iZGZkLTRiNTgtOGU4MC0xODY1NzY4NTE3MjUiLCJ0eG5faWQiOiJiNTcxMDg2OS0wOGU1LTQxZDMtOWQxOS04YmU4MTE2ZTFhYmYiLCJyYXRlIjoiODgzNjQuMDE0MTE0MDIiLCJjaGFyZ2VkX2ZlZSI6IjMwLjYxMTk0NTQxIiwiYmFzZV9hbW91bnQiOiIwLjAxNzMyMTY3IiwicXVvdGVfYW1vdW50IjoiMTUwMC4wMDAwMDAwMCIsImJhc2VfYXNzZXQiOiJCVEMiLCJxdW90ZV9hc3NldCI6IkNPUCIsImNyZWF0ZWRfYXQiOiIyMDIzLTAxLTE2VDE2OjAxOjA1LjI3NTcyM1oiLCJvcF90eXBlIjoiU0VMTCIsImV4dGVybmFsX3JlZiI6ImIyYi10ZXN0cyJ9fSwibWVzc2FnZV90eXBlIjoidHJhbnNhY3Rpb25fcmVzdWx0In0.Xe5wNQE7On0-qiS3EabrzocvdELWSHS8Llc1VFIohnY"
}

In case the transaction could not be completed properly, then the message sent will have a body similar to this:

{
  "message": {
    "succeed": False,
    "failure_result": {
      "id": "df9d41d8-ac80-4125-9738-ce1fcb8dc523",
      "quote_id": "cf6c57ea-03a1-41dc-99e8-b91cc1b7333e",
      "code": "20001",
      "base_amount": "0.00868727",
      "quote_amount": "1500.00000000",
      "op_type": "BUY",
      "external_ref": "00010203-0405-0607-0809-0a0b0c0d0e0f",
      "created_at": "2022-11-02T15:37:22.765995Z"
    }
  },
  "message_type": "transaction_result",
  "signed_message": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtZXNzYWdlIjp7InN1Y2NlZWQiOnRydWUsInF1b3RlIjp7ImlkIjoiNGY5ZGE1N2UtOTZkMC00NGM3LTg3NGUtY2E5ZTAyODg3M2M4IiwicXVvdGVfaWQiOiIzYjJkZWMyZi1iZGZkLTRiNTgtOGU4MC0xODY1NzY4NTE3MjUiLCJ0eG5faWQiOiJiNTcxMDg2OS0wOGU1LTQxZDMtOWQxOS04YmU4MTE2ZTFhYmYiLCJyYXRlIjoiODgzNjQuMDE0MTE0MDIiLCJjaGFyZ2VkX2ZlZSI6IjMwLjYxMTk0NTQxIiwiYmFzZV9hbW91bnQiOiIwLjAxNzMyMTY3IiwicXVvdGVfYW1vdW50IjoiMTUwMC4wMDAwMDAwMCIsImJhc2VfYXNzZXQiOiJCVEMiLCJxdW90ZV9hc3NldCI6IkNPUCIsImNyZWF0ZWRfYXQiOiIyMDIzLTAxLTE2VDE2OjAxOjA1LjI3NTcyM1oiLCJvcF90eXBlIjoiU0VMTCIsImV4dGVybmFsX3JlZiI6ImIyYi10ZXN0cyJ9fSwibWVzc2FnZV90eXBlIjoidHJhbnNhY3Rpb25fcmVzdWx0In0.Xe5wNQE7On0-qiS3EabrzocvdELWSHS8Llc1VFIohnY"
}

To identify this request type, validate message_type: transaction_result You can correlate all events using the value of the key “id”. See in the examples above, that all requests sent to the Webhook contain the same value: id: df9d41d8-ac80-4125-9738-ce1fcb8dc523.

Fields

  • id: UUID4. Used in approval of a purchase operation and when reporting the result of the operation (whether buy or sale). It is the same value in all requests.
  • quote_id: UUID4. ID of the quote (either buy or sale) accepted by the user.
  • txn_id: UUID4. Transaction ID.
  • created_at: Transaction creation date in UTC.

Generally speaking, it is enough for the partner to persist the id field. In case of any problem, this is the value we use to reference a transaction.

Events

B2B Fintech widget emits the following events:

React Native You can listen to all events that the widget emits using the parameter onMessage of WebView.

  • Transaction completed: When completed correctly, the following JSON payload is emitted.
{
    "opType": "receipt",
    "data": {
        "txn_id": "3df33806-f37a-4fa4-a52c-0aeeec7ea5e7",
        "base_asset": "USDC",
        "base_amount": "0.00847813",
        "quote_asset": "ARS",
        "quote amount": "1,500.00000000",
        "created_at": "2022-11-01T03: 30: 47.262141Z",
        "rate": "100153.86156513",
        "status": "Completed"
    }
}

Enabled Operations

BUY: Purchasing Crypto Assets

  1. When a user performs a BUY operation, the widget sends a request with the HTTP POST method asking the Partner for authorization to carry out the operation (see Purchase Operation Approval (BUY)).

  2. To approve the operation, the partner must respond with HTTP 200 status code and the following JSON payload:

{
  "approved": "true"
}

Any response other than the above will be interpreted as a Denied Operation

Then the widget sends a request informing the Operation Result.

SELL: Selling Crypto Assets

  1. When a user performs a SELL operation, the widget sends a request with HTTP POST method informing the Operation Result.

Since in this case, Ripio is responsible for the crypto holdings, the partner does not need to approve the operation, but they do need to update the user’s information after receiving it.

Styles


It is possible to define the CSS styles of the widget so that they adjust to the Partner’s brand manual, and to the UX of their platform. To do this, the partner must send Ripio the CSS rules/classes corresponding to their brand manual, the following elements being those that can be configured:

  • Buttons style. You can configure the appearance of all the widget buttons, eg: Buy, Continue, Back, Cancel, Download receipt, etc. It is possible to apply any type of CSS rule (even CSS pseudo-classes like :active, :hover, :focus, etc.)

  • Fonts. The widget takes the font configured in the body element of the HTML template where it is embedded. In the following example we see a condensed version of what could be a <head /> block where a style sheet style.css is imported and where in the style sheet it is defined to use the Montserrat font:

   <head>
    ... <!-- Some other stuff that usually goes here, then... -->
    <link rel="stylesheet" href="style.css" />
  </head>

In line 17 font-family of the body is set to “Montserrat”. The widget will use this font family as well.

An example of CSS styles to be sent to Ripio:

.btn-primary:active {
  color: #ffffff;
}
.btn-primary {
  display: inline-block;
  min-width: 100px;
  height: 44px;
  background-color: rgb(238, 243, 246);
}

Use your browser’s devtools to inspect the widget’s markup. CSS class names that we use in buttons are the following:

  • btn-primary
  • btn-secondary
  • btn-tertiary

The following is an example of the look and feel without any styling being applied to the widget elements: fintech-balance-default Now a look and feel with styles applied according to Ripio’s brand manual: fintech-balance-customized And this is a screenshot portraying styles of a fictional fintech partner using its own brand manual.

Buttons have CSS pseudo-class :hover

Responsiveness

The widget is responsive, so it can be used in mobile applications. In general, the integration is done using a webview. Examples according to some frameworks are:

It is extremely important to define a viewport in the HTML template embedding the widget:

A screenshot of the widget used in a mobile implementation of a fictional partner

Buttons

The different button options are the following (the versions without style are shown on the left and with the styles of the Ripio brand manual on the right as an example):

Primary Button Secondary Button Tertiary Button Receipt Button Dropdown Button

UI Examples

Here are more UI examples for the different flows enabled for the widget, and their styled and unstyled versions. (Screenshots taken from an implementation in Spanish):

Quote Form Quote Result Operation Receipt Alert Message Loading Message

Fonts


Lastly, if you want to add a custom font or import it, you need to do it in the HTML template, by adding the following lines inside the <head />

<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Lato:wght@400;700;900&display=swap" rel="stylesheet">