Merchant API
Take the Xend Finance services to your customers via a RESTful API and webhooks.
Who is a merchant
A merchant is a registered business that performs savings operations on behalf of their customers. Basically, your account is referred to as a merchant account and you can create your customers’ accounts as sub accounts. Each sub account has access to a personal wallet address and you can perform transactions (transfer, deposit, save, withdraw, and more) on each account.
What is this?
This API is intended for Finance Applications that wish to leverage the Xend Finance savings service and offer it to their end users. You can, via the API, create an account for each of your customers and manage their accounts just the same way a normal user would inside the Xend Finance mobile application to save, earn interest, transfer money, and much more.
Link to Merchant Swagger doc: https://apiprostaging.xend.finance/swagger/merchant/index.html/ Sample nodeJs application: https://github.com/xendfinance/merchant-api-sample
Becoming a merchant
Staging/sandbox environment
Create a merchant account for the sandbox environment by calling the merchant auth endpoint
/public/create/sandbox/account
with the required email and password. Please check out the swagger documentation for more clarity on the API endpoint.
Production environment
The steps below are for setting up a merchant account in a production environment.
Download the Xend Finance Mobile app.
Register and verify your account using the mobile app with your company email address.
Follow the steps in the mobile app and complete your user profile.
Next, on the mobile app, proceed to the profile section. Then, under Account Security, enable your 2-Factor Authentication.
Next, complete the KYC Verification still in the profile section of the mobile app using CEO profile.
Complete the KYB and business documentation: A link will be provided where you complete our KYB process by uploading your business profile and documents. Ref: https://xend.finance/mobileapp.html
Verify all your provided data for accuracy and authenticity
The return data of the /public/create/sandbox/account
and /public/register
API will have information on apiKey and apiSecret which you will use to access the rest of the API endpoints for merchants. You can keep them in a safe place where you can easily use them for subsequent merchant API calls.
API Access
To access any authenticated endpoints from the Merchant API you have to add the following properties to the request headers.
authentication: JWT key that is returned to you after logging in.
apisignature: Api signature calculation from the data used to make the HTTP request. An example of how it is calculated can be found in the section "API Signature Calculation"
timestamp: this should be the current timestamp in milliseconds UTC
memberemail: email of the sub-user of the merchant's platform.
merchantcode: merchant user id generated after creating your account.
userlanguage: the language that the responses from the API should be. Use the ISO language name as the value of the field.
authorization: serves as the API captcha. For the test environment, the value is Basic parole.
API Signature Calculation
Merchant API endpoints make use of HMAC SHA256 signatures for authenticating API requests. Hash-based message authentication code (HMAC) is used to verify that a request to our endpoint is coming from a trusted source and that the request has not been tampered with on transit from the source of the request to our servers.
The parameters needed to calculate the API signature include timestamp, clientToken, clientSecret, and the payload of the request. All these values are concatenated in URL encoded format and the hash is calculated with the SHA256 hashing algorithm.
HMAC with Postman
Postman has a feature to add a pre-request script for every request it makes. We have an example of the script to calculate the API signature and add it to your request headers. Remember to add the following variables to Postman, hmac, timestamp, token, accessSecret. https://gist.github.com/nCally/508d449fb5ae8bfee62cde290850d112
JSON Response
Every response body is expected to be JSON data but the API gateway could return unrecognized HTML content with Gateway errors when something goes wrong.
data: the data returned by the response can be user profile and token, for example, in the login endpoint. The data can be an empty object {}, empty array [], or null.
status: The action status enum (success, failed). E.g. if login fail, status will be failed status_code: this is the request header status code. A failed request can have any error code based on section 1 A.
error_code: Error code based on error documentation, each error for example if during registration we discover a user trying to use a temporary email address, the error code is documented such that even the public can read our error documentation and know what it means. So, invalid email address can be error code 0001. Ref: Error Code Documentation
action: this defines an action that could be taken after an error is returned. E.g. load_2fa_login REF: Response Action Documentation. Secondly, it can return the field, causing the error. E.g. if you are doing authentication and providing email and password. The action can be =”password” meaning that the error is caused by param “password”.
message: this is the error message that is shown to the user. This can be, e.g., “wrong username and password”. It is returned for every request.
response_details: here comes the debug message and log for the developer; this should never be shown to the user, as the developer can easily discover what he is doing wrong here. E.g., if you pass the user phone number as a string instead of number and the API rejects it, the ‘message’ will say: “something is wrong, try again later.”, but the ‘response_details’ will say, “Please phone number must be set as number,”. The response detail actually comes with error logs.
message_language_code: every response comes with language code which is the response.message (front-end can use the language code to switch the message shown to the user based on language preference of the user). The API may return only English ‘message’ for the user – with this code corresponding to the language code doc so the code can be used for translation.
Translation: for languages, users can pass language code in every request and the API will return the message in their language (see language code documentation). Also, the language code is returned in the response. Front-end can use the language code to make translations on the app. REF: language code doc
Language filter is passed thus:
User.profile.userlanguage = eng (user must have set language in his profile)
Post.Header.userlanguage = eg (Pass language in header in each request to return error messages in that language.) NB: language in header overrides user language in profile.
Request Body and Query
Every POST/PUT/PATCH/DELETE Requests will reject Query Strings.
Pass Query Strings to GET Request only.
Also, POST/PUT/PATCH/DELETE Requests do not want to expect URL params or query strings.
NB: what is query string, query param, body param?
Here: https:website.com/url/{query_param}/?query_string=id
Date in body param must be posted in this format: eg expiry_time: "2022-04-09 06:05:05"
Limits and quotas on API requests
50,000 requests per project per day, which can be increased
10 queries per second (QPS) per IP address. (And 100 requests per 100 seconds per member) — when your request limit is reached, header status can be 503
API request is limited/hr you are advised to manage how you query the API to avoid exhausting the limits.
Response header status
The response header status codes are described in the table below.
Status code | Type | Description | Details |
---|---|---|---|
200 | Ok | The status code 200 does not always mean that your action is complete. Check the doc for JSON Response object describing actions | It means your http request is complete and ok. |
417 | Expectation Failed | Seen when a user posts the data that is beyond the validation. E.g. if you post amount 20,000 when max amount is stated to be 10,000 | The JSON Response sample exist in APP_INFO section of swagger doc. |
400 | Bad Request | Not usually caused by the user. Basically, if this is seen, the programmer did not read documentation. I advise you to read them. They don’t bite | This can happen for example if you make a request to an endpoint without providing all the required params in header or body. |
404 | Not Found | The 404 response code does not means the data you request is not found. It's basically the route not found and this status code can come with json object or even HTML data; don't be surprised | |
500 | Internal Server Error | Can be returned if something is wrong on the server or you, the programmer, has configured and posted unwanted data that attempt to confuse us. This status code can come with JSON OBJECT or even HTML data; dont be taken unawares. | |
522 | Timeout | As we use cloudflare DNS, this header error code can be seen. It can return HTML, or any other sophisticated data types that you have not seen before | |
401 | Not Authorized | When the Member authentication keys are not set or have expired | |
403 | Forbidden | When you do what you are not supposed to do. | If you try to do 2fa login when the user has not enabled 2fa, you can get this code |
503 | Service Unavailable | Sometimes while we are installing updates on the server, or your request limit is exhausted, this could happen | Wait for a second to continue. |
429 | Too Many Requests | When bots or users are sending too many requests to the API endpoint. | Response data can be mixed format. Follow standard and identify Retry Time in header |
Pagination
Every endpoint with pagination will have data, thus:
Switching page in pagination
Set query params:
pageId | INT (the page you wish to load: default is 1)
perPage | INT (number of items you want to return on each page.. Default is 18, or 20)
sort | String (ENUM >> desc,asc) (sort order for the items) default = desc
NB: Please check swagger swagger/doc/index.html/#/APP_INFO for sample pagination data (GET/info/app/pagination/sample THANKS)
Auto Save (Flexible & Fixed)
Auto save means the user sets his auto save configuration such that once funds enter the wallet we can auto save it to the savings plan either flexible or fixed.
NB: auto fixed savings overrides flexibles savings (should the user enable both on any wallet)
Globally, we can enable or disable auto saving in the entire system.
On each Currency we can enable / disable auto save too.
How autosave works
User sets auto save config in his wallets. Any time the fund enters the user wallet we create a job to auto save the fund in 12 hrs. If in 12 hrs time we try to auto save the fund and the fund is no longer there, we drop the job process. *Fixed savings require the user to select the savings profile that the auto save will top up the fund or leave it empty for a new one to be created.
*users are not allowed to select fixed saving profile yet
Auto save config settings
Field | Type | Description | |
---|---|---|---|
autoSaveFixed | boolean | Whether to save to fixed when money enter the wallet | This when set to true overrides autoSaveFlexible config |
autoSaveFlexible | boolean | ||
autoSaveReInvestFixed | boolean | Whether to reinvest the income from fixed savings when it matures. | |
autoSaveFormatAllFunds | boolean | Whether to save all the funds in the wallet or the amount received when the trigger happen | Default = true |
autoSavDurationDays | number | Should the auto save be for fixed savings, we need duration to use | Multiple of 30 |
Call back & Webhooks
Edit profile and set your webhook URL. We enable only one URL with module filters. What we offer here is an Unauthenticated POST RESTful webhook over HTTPS only.
NB: callbacks are not guaranteed.
Webhook parameters
Parameter | Module / Service | Description | Available Examples |
---|---|---|---|
service | All | The service module. You can get a webhook when a user account is funded with |
|
secret | All | We will pass the secret you provided in all callback | All module means all the callback we give you must have this param passed which is “secret” same with “service” all webhook will have “service” |
amount | wallet | Amount funded in wallet when deposit is received in the blockchain. | |
currencySymbol | wallet | could be BUSD etc | |
publicKey | wallet | Could be the crypto address that got the credit. | |
userEmail | All | Associated user Email | If it’s wallet, then it could be the user that received the funding |
WhatsApp Integration Community
Join now! You can get instant answers and also get a test BUSD on SANDBOX. https://chat.whatsapp.com/KH7sK3082GP4igCqySC81F
Notes
The database time zone is UTC.
Here's the link to the swagger documentation.
A sample node.js app of the Merchant API integration.
Last updated