Configuring Payments with Mesh Link
This guide explains how to configure Link to facilitate cryptocurrency payments within your application. You’ll learn how to define accepted payment assets, amounts, and how to apply fees.
web-sdk
Android
iOS
Flutter
Step 0: Getting a Link Token
Link token should be obtained from the GET /api/v1/linktoken endpoint. Api reference for this request is available here. Here you can configure your payment configurations such as destination address, amount in fiat currency, integrationId, and more. A sample query to the sandbox endpoint may look as such:const options = {
method: 'POST',
headers: {
'X-Client-Secret': '<Mesh Client Secret>',
'X-Client-Id': '<Mesh Client Id>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
userId: "Mesh",
restrictMultipleAccounts: "<true/false>",
transferOptions: {
toAddresses: [
{
networkId: "<Network Id>",
symbol: "<Currency Symbol (e.g. USDC)>",
address: "<Address>",
amount: "<Amount to transfer (e.g. 5)>"
}
],
clientFee: "<Client Fee (e.g. 0.001)>"
}
})
};
const res = await fetch('https://sandbox-integration-api.meshconnect.com/api/v1/linktoken', options);
const { link_token } = await res.json();
Step 1: Initializing Link
import { createLink } from '@meshconnect/web-link-sdk';
// ...
let accessToken = null;
const linkConnection = createLink({
clientId: '<Your Mesh Connect Client Id>',
onIntegrationConnected: (data: LinkPayload) => {
accessToken = data.accessToken
// use broker account data
},
onExit: (error?: string) => {
if (error) {
// handle error
} else {
// ...
}
}
});
Step 2: Transfer Assets
const linkXfer = createLink({
clientId: "<Mesh Client ID>",
accessTokens: accessToken, // Generated in Step 1
onTransferFinished: (p) => {
linkXfer.closeLink(); // close overlay
}
});
linkXfer.openLink(link_token); // second overlay (link_token Generated in Step 0)
Step 0: Getting a Link Token
Link token should be obtained from the GET /api/v1/linktoken endpoint. Api reference for this request is available here. Here you can configure your payment configurations such as destination address, amount in fiat currency, integrationId, and more. A sample query to the sandbox endpoint may look as such:Add the link and okhttp3 to your build.gradledependencies {
implementation 'com.meshconnect:link:2.0.0'
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
}
Sample linkToken retrieval.import okhttp3.*
import org.json.JSONObject
import java.io.IOException
// Function to request a link token
fun requestLinkToken() {
val client = OkHttpClient()
// Construct the JSON body
val transferOptions = JSONObject().apply {
put("toAddresses", listOf(
JSONObject().apply {
put("networkId", "<Network Id>")
put("symbol", "<Currency Symbol (e.g. USDC)>")
put("address", "<Address>")
put("amount", "<Amount to transfer (e.g. 5)>")
}
))
put("clientFee", "<Client Fee (e.g. 0.001)>")
}
val jsonBody = JSONObject().apply {
put("userId", "Mesh")
put("restrictMultipleAccounts", "<true/false>")
put("transferOptions", transferOptions)
}
val requestBody = RequestBody.create(
"application/json; charset=utf-8".toMediaTypeOrNull(),
jsonBody.toString()
)
val request = Request.Builder()
.url("https://sandbox-integration-api.meshconnect.com/api/v1/linktoken")
.addHeader("X-Client-Secret", "<Mesh Client Secret>")
.addHeader("X-Client-Id", "<Mesh Client Id>")
.addHeader("Content-Type", "application/json")
.post(requestBody)
.build()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
// Handle error
e.printStackTrace()
}
override fun onResponse(call: Call, response: Response) {
response.use {
if (!response.isSuccessful) {
// Handle unsuccessful response
println("Unexpected code $response")
} else {
val responseBody = response.body?.string()
val json = JSONObject(responseBody)
val linkToken = json.getString("link_token")
// Use linkToken as needed
println("Link Token: $linkToken")
}
}
}
})
}
Step 1: Initializing Link
Create a LinkConfigruation instance with essential callbacks:val configuration = LinkConfiguration(
linkToken = linkToken,
onIntegrationConnected = { payload ->
secureStorage.saveToken(payload.accessToken)
},
onTransferFinished = { payload ->
when (payload.status) {
"success" -> processSuccess(payload as TransferFinishedSuccessPayload)
"error" -> handleError(payload as TransferFinishedErrorPayload)
}
}
)
Define core callback handlers:private val onIntegrationConnected = { payload: LinkPayload ->
when (payload) {
is AccessTokenPayload -> {
// Store auth_token securely
Log.d("Mesh", "Connected: ${payload.accessToken}")
}
is DelayedAuthPayload -> {
// Handle delayed authentication
}
}
}
private val onTransferFinished = { payload: TransferFinishedPayload ->
when (payload.status) {
"success" -> {
val success = payload as TransferFinishedSuccessPayload
Log.d("Mesh", "Transfer ID: ${success.transferId}")
}
"error" -> {
val error = payload as TransferFinishedErrorPayload
Log.e("Mesh", "Transfer failed: ${error.message}")
}
}
}
Step 2: Transfer Assets
private val linkLauncher = registerForActivityResult(LaunchLink()) { result ->
when (result) {
is LinkSuccess -> handlePayloads(result.payloads)
is LinkExit -> handleExit(result.errorMessage)
}
}
private fun handlePayloads(payloads: List<LinkPayload>) {
payloads.forEach {
when (it) {
is AccessTokenPayload -> onIntegrationConnected(it)
is TransferFinishedPayload -> onTransferFinished(it)
}
}
}
private fun handleExit(error: String?) {
error?.let { Log.e("Mesh", "Flow exited: $it") }
}
Launch the UI Component:fun initiatePaymentFlow() {
try {
linkLauncher.launch(configuration)
} catch (e: Exception) {
Log.e("Mesh", "Launch failed: ${e.message}")
}
}
Step 3: Monitor Transfer Execution
Implement event tracking for granular control:private val onEvent: (String, Map<String, Any>?) -> Unit = { event, data ->
when (event) {
"transferStarted" ->
Log.d("Mesh", "Transfer initiated")
"transferPreviewed" ->
Log.d("Mesh", "User reviewed transfer details")
"credentialsEntered" ->
Log.d("Mesh", "Exchange authentication in progress")
else -> {}
}
}
Step 0: Getting a Link Token
Link token should be obtained from the GET /api/v1/linktoken endpoint. Api reference for this request is available here. Here you can configure your payment configurations such as destination address, amount in fiat currency, integrationId, and more. A sample query to the sandbox endpoint may look as such:import Foundation
let url = URL(string: "https://sandbox-integration-api.meshconnect.com/api/v1/linktoken")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("<Mesh Client Secret>", forHTTPHeaderField: "X-Client-Secret")
request.addValue("<Mesh Client Id>", forHTTPHeaderField: "X-Client-Id")
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
let body: [String: Any] = [
"userId": "Mesh",
"restrictMultipleAccounts": "<true/false>",
"transferOptions": [
"toAddresses": [
[
"networkId": "<Network Id>",
"symbol": "<Currency Symbol (e.g. USDC)>",
"address": "<Address>",
"amount": "<Amount to transfer (e.g. 5)>"
]
],
"clientFee": "<Client Fee (e.g. 0.001)>"
]
]
request.httpBody = try? JSONSerialization.data(withJSONObject: body)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
print("Error:", error ?? "Unknown error")
return
}
if let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
let linkToken = json["link_token"] as? String {
print("Link Token:", linkToken)
} else {
print("Invalid response")
}
}
task.resume()
Step 1: Initializing Link
Installation
Add package LinkSDK in your project’s Package Dependencies or download LinkSDK.xcframework.let configuration = LinkConfiguration(
linkToken: linkToken,
settings: LinkSettings?, // optional
onIntegrationConnected: onIntegrationConnected,
onTransferFinished: onTransferFinished,
onEvent: onEvent, // optional
onExit: onExit // optional
)
Define callback handlers:// callbacks
let onIntegrationConnected: (LinkPayload)->() = { linkPayload in
switch linkPayload {
case .accessToken(let accessTokenPayload):
print(accessTokenPayload)
// Store access token securely for transfers
case .delayedAuth(let delayedAuthPayload):
print(delayedAuthPayload)
}
}
private val onTransferFinished = { payloads: List<LinkPayload> ->
payloads.forEach { payload ->
when (payload) {
is TransferFinishedSuccessPayload -> {
Log.d("Mesh", "Transfer ID: ${payload.transferId}")
// Handle successful transfer
}
is TransferFinishedErrorPayload -> {
Log.e("Mesh", "Error: ${payload.message}")
// Handle transfer failure
}
}
}
}
let onEvent: (String) -> Void = { event in
switch event {
case "transferStarted":
print("Transfer initiated")
case "transferPreviewed":
print("Transfer details reviewed")
case "credentialsEntered":
print("Exchange authentication started")
default:
break
}
}
Step 2: Transfer Assets
let handler = try LinkHandler(
configuration: LinkConfiguration(
linkToken: linkToken,
onEvent: { event in
switch event {
case .transferExecutionError(let error):
Analytics.track(.transferFailed(error))
default: break
}
}
)
)
handler.present(in: self)
Step 0: Getting a Link Token
Link token should be obtained from the GET /api/v1/linktoken endpoint. Api reference for this request is available here. Here you can configure your payment configurations such as destination address, amount in fiat currency, integrationId, and more.import 'dart:convert';
import 'package:http/http.dart' as http;
Future<String> requestLinkToken() async {
final response = await http.post(
Uri.parse('https://sandbox-integration-api.meshconnect.com/api/v1/linktoken'),
headers: {
'X-Client-Secret': '<Mesh Client Secret>',
'X-Client-Id': '<Mesh Client Id>',
'Content-Type': 'application/json',
},
body: jsonEncode({
'userId': 'Mesh',
'restrictMultipleAccounts': true,
'transferOptions': {
'toAddresses': [
{
'networkId': '<Network Id>',
'symbol': '<Currency Symbol (e.g. USDC)>',
'address': '<Address>',
'amount': '<Amount to transfer (e.g. 5)>',
}
],
'clientFee': '<Client Fee (e.g. 0.001)>',
},
}),
);
final json = jsonDecode(response.body);
return json['content']['linkToken'];
}
Step 1: Initializing Link
import 'package:mesh_sdk_flutter/mesh_sdk_flutter.dart';
Future<void> initiatePaymentFlow(String linkToken) async {
final result = await MeshSdk.show(
context,
configuration: MeshConfiguration(
linkToken: linkToken,
onIntegrationConnected: (integration) {
// Store access token securely for future transfers
print('Connected: ${integration.accessToken}');
},
onTransferFinished: (transfer) {
print('Transfer finished: $transfer');
},
onEvent: (event) {
// Track user progress
print('Event: $event');
},
onExit: (error) {
if (error != null) {
print('Exit with error: $error');
}
},
),
);
switch (result) {
case MeshSuccess():
print('Payment flow completed successfully');
case MeshError():
print('Payment flow error: ${result.type}');
}
}
Step 2: Transfer Assets
The MeshSdk.show() method handles the complete transfer flow. Use the callbacks to track progress:
onIntegrationConnected: Called when user authenticates with an exchange
onTransferFinished: Called when the transfer completes (success or failure)
onEvent: Called for granular events like transferStarted, transferPreviewed, credentialsEntered
Running Steps: 0-2 will open the Mesh Payment flow allowing Users to select the payment option (if allowed) and confirm the transfer.
Understanding Payment Configuration
When using Link to process payments, you’ll utilize the TransferOptions object within your link token request to specify the parameters of the payment transaction. This includes defining the acceptable cryptocurrencies, their respective destination addresses, and the payment amount.
Request must be preformed from the server side because it requires the client secret. You will get the response in the following format:
{
"content": {
"linkToken": "{linktoken}"
},
"status": "ok",
"message": ""
}
Specifying Payment Assets and Destination Addresses
The ToAddresses array within TransferOptions allows you to define the cryptocurrencies users can pay with and the corresponding receiving addresses. Each object in this array represents a payment option.
Important: For each entry in the ToAddresses array, you must provide the Mesh-specific Unique Identifier (UID) for the network on which the supported token resides. Refer to the Mesh Connect documentation for a comprehensive list of supported tokens, networks, and their Mesh UIDs: Tokens | Networks | Integrations
###Accepting Payments with Multiple Cryptocurrencies
To allow users to pay with a variety of cryptocurrencies, include multiple objects within the ToAddresses array. For each object, specify the network, symbol, and destination address.
Link Token Request Body (Step 0):
{
"UserId": "unique_end_user_identifier",
"TransferOptions": {
"ToAddresses": [
{
"NetworkId": "e3c7fdd8-b1fc-4e51-85ae-bb276e075611",
"Symbol": "ETH",
"Amount": 0.0032,
"Address": "0x9Bf6207f8A3f4278E0C989527015deFe10e5D7c6"
},
{
"NetworkId": "e3c7fdd8-b1fc-4e51-85ae-bb276e075611",
"Symbol": "USDC",
"Amount": 10,
"Address": "0x9Bf6207f8A3f4278E0C989527015deFe10e5D7c6"
},
{
"NetworkId": "e3c7fdd8-b1fc-4e51-85ae-bb276e075611",
"Symbol": "USDT",
"Amount": 10,
"Address": "0x9Bf6207f8A3f4278E0C989527015deFe10e5D7c6"
},
{
"NetworkId": "7436e9d0-ba42-4d2b-b4c0-8e4e606b2c12",
"Symbol": "MATIC",
"Amount": 22.8,
"Address": "0x9Bf6207f8A3f4278E0C989527015deFe10e5D7c6"
}
],
"TransactionId": "unique_transaction_identifier"
}
}
NetworkId: The Mesh UID of the network.
Symbol: The cryptocurrency symbol.
Amount: The amount the user is expected to pay, in the cryptocurrency specified by the Symbol.
Address: The recipient’s cryptocurrency address.
TransactionId: A unique identifier for this payment transaction. This is crucial for mapping payments to orders or other internal systems.
Streamlined Payments with a Single Asset and Amount
If you want to pre-define a single payment option (one cryptocurrency and amount), Link can streamline the user flow. When you provide AmountInFiat and a single network/token/address combination, Link will bypass asset and network selection, taking the user directly to the payment preview.
Link Token Request Body (Step 0):
{
"UserId": "unique_end_user_identifier",
"TransferOptions": {
"ToAddresses": [
{
"NetworkId": "e3c7fdd8-b1fc-4e51-85ae-bb276e075611",
"Symbol": "ETH",
"Address": "0x9Bf6207f8A3f4278E0C989527015deFe10e5D7c6"
}
],
"AmountInFiat": 10,
"TransactionId": "unique_transaction_identifier"
}
}
AmountInFiat: The payment amount in fiat currency (e.g., USD).
Other parameters are the same as in Scenario 1.
Adding Payment Processing Fees
You can apply a client fee to payment transactions using the ClientFee parameter in the link token request.
Important:
This fee should only be used for Payments, where the transfer destination is an address owned by your business.
Do not use this for Deposits, where the transfer destination is an address owned by the end-user.
The ClientFee will override any default fee configured in your Mesh dashboard for the transaction.
- The
ClientFee will override any default fee configured in your Mesh dashboard for the transaction.
Link Token Request Body (with Client Fee):
{
"UserId": "unique_end_user_identifier",
"TransferOptions": {
"ToAddresses": [
{
"NetworkId": "e3c7fdd8-b1fc-4e51-85ae-bb276e075611",
"Symbol": "ETH",
"Address": "0x9Bf6207f8A3f4278E0C989527015deFe10e5D7c6"
},
{
"NetworkId": "e3c7fdd8-b1fc-4e51-85ae-bb276e075611",
"Symbol": "USDC",
"Address": "0x9Bf6207f8A3f4278E0C989527015deFe10e5D7c6"
},
{
"NetworkId": "e3c7fdd8-b1fc-4e51-85ae-bb276e075611",
"Symbol": "USDT",
"Address": "0x9Bf6207f8A3f4278E0C989527015deFe10e5D7c6"
},
{
"NetworkId": "7436e9d0-ba42-4d2b-b4c0-8e4e606b2c12",
"Symbol": "MATIC",
"Address": "0x9Bf6207f8A3f4278E0C989527015deFe10e5D7c6"
}
],
"AmountInFiat": 10,
"ClientFee": 0.025
}
}
{
"UserId": "unique_end_user_identifier",
"TransferOptions": {
"ToAddresses": [
{
"NetworkId": "e3c7fdd8-b1fc-4e51-85ae-bb276e075611",
"Symbol": "ETH",
"Address": "0x9Bf6207f8A3f4278E0C989527015deFe10e5D7c6"
}
],
"AmountInFiat": 10,
"TransactionId": "unique_transaction_identifier",
"ClientFee": 0.025
}
}
ClientFee: A decimal representing the fee percentage (e.g., 0.025 for 2.5%).
Enabling SmartFunding
Sometimes your user doesn’t have enough of the token you want to get paid in. And sometimes your user doesn’t have any of it. SmartFunding enables auto-top-ups of / auto-conversions to the payment token within in the user’s account before initiating the transfer.
To give your users the flexibility to fund their transfers using cryptocurrency, you need to include “FundingOptions”:{ "Enabled": true } during the initial setup of the Link UI. You do this by including the following setting in your Link initialization code:
{
"userId": "example_user123",
"restrictMultipleAccounts": true,
"transferOptions": {
"transactionId": "example_tx123",
"transferType": "payment",
"fundingOptions": {
"enabled": true
},
"isInclusiveFeeEnabled": false,
"toAddresses": [
{
"symbol": "USDT",
"address": "0x314838D6783865908456257c0b07Ea4Bc272cF98",
"networkId": "18fa36b0-88a8-43ca-83db-9a874e0a2288",
"amount": 99.99
}
]
},
"integrationId": "9226e5c2-ebc3-4fdd-94f6-ed52cdce1420"
}
👍 This is best suited for payment or onRamp transfer types and operate best with a single token and a passed amount parameter
With this option enabled, if a user doesn’t have enough balance for a transfer, Mesh can guide them through using the balances in their account, and payment methods linked to their account to top up their balance.