Crosschain Payments

Pay requests using stablecoins from any supported network, without manual bridging or token swaps.

Talk to an expert Discover how Request Network API can enhance your app's features - book a call with us.

Crosschain payments allow users to pay a request using a stablecoin from a different blockchain network than the one specified on the request. For example, a payer can pay a request for USDC on Base using USDT from their Optimism wallet.

Benefits

  • Flexibility: Payers can pay with their preferred currency.

  • Cost-Effective: Automated routing balances cost and speed.

  • Time-Saving: Payers don't need to swap or bridge tokens manually.

  • Simplified UX: Payment settlement requires only 1 or 2 signatures from the Payer.

Supported Networks

Crosschain payments are supported on the following blockchain networks:

  • Base

  • Optimism

  • Arbitrum

  • Ethereum

Supported Stablecoins

The following stablecoins are supported for crosschain payments on both the sending and receiving networks.

  • USDC

  • USDT

  • DAI

How it works

1. Request creation

To enable crosschain payments, the request must be created with the following parameters:

  • paymentCurrency included in the Supported Stablecoins and Supported Networks.

  • amount greater than 1 - executing crosschain payments under 1 stablecoins is not allowed, even though creating requests has no restrictions on amount .

For more details about creating requests, please see the POST /v2/request endpoint.

2. Payment route fetching

To display a list of possible routes for a given request and payer address, use the GET /v2/request/{requestId}/routes endpoint. It returns all of the possible routes based on the payer's token balances.

Route Ranking

The API automatically ranks available payment routes based on the following factors:

  • Transaction fees

  • Processing speed

Routes that offer a balanced combination of lower fees and faster processing times are ranked higher in the results.

Fee breakdown

When fetching payment routes, each route displays the total estimated fees in the payment currency. This fee represents the combined costs associated with processing the transaction, including:

  1. Gas Fees:

    The total fee includes all gas costs incurred by the payment processor wallet for processing the transaction. This covers:

    - Transferring tokens from the payer's wallet.

    - Approving the payment execution smart contract.

    - Executing the crosschain payment transaction.

    For tokens supporting EIP-2612:

    - The payment processor wallet also covers for the onchain permit transaction.

    For tokens that do not support EIP-2612:

    - The payer must perform an onchain approval transaction and pay for the gas fee directly. This fee is not included in the total fee shown for the route.

  2. Service Fees:

    The total fees also include any service fees charged by the crosschain infrastructure for facilitating transfers or swaps between different blockchains.

Samechain routes

The API may return samechain routes if the payer address has supported currencies on the same chain as the paymentCurrency .

  • Example: paymentCurrency is USDC on Base, and the payer has USDT on Base

  • Gasless transactions - the transaction fees are added on top of the request amount

  • No native token (ETH, etc..) needed for gas

Get payment routes

get

Get the payment routes for a request

Path parameters
requestIdstringRequired

The requestId of the request

Example: 01e273ecc29d4b526df3a0f1f05ffc59372af8752c2b678096e49ac270416a7cdb
Query parameters
walletstringRequired

The wallet address of the payer

Example: 0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7
amountstringOptional

The amount to pay, in human readable format

feePercentagestringOptional

Fee percentage to apply at payment time (e.g., '2.5' for 2.5%)

feeAddressstringOptional

Address to receive the fee

Header parameters
x-api-keystringRequired

API key for authentication

Responses
200
Available payment routes
application/json
get
GET /v2/request/{requestId}/routes HTTP/1.1
Host: api.request.network
x-api-key: text
Accept: */*
{
  "routes": [
    {
      "id": "REQUEST_NETWORK_PAYMENT",
      "fee": 0,
      "speed": "FAST",
      "price_impact": 0,
      "chain": "MAINNET",
      "token": "REQ"
    }
  ]
}

3. Getting payment calldata

Once the route is selected, the payer needs to fetch the unsigned payment calldata or intents. If the selected route is a crosschain payment, the GET /v2/request/{requestId}/pay endpoint returns an unsigned payment intent. It will also return an unsigned approval permit or unsigned approval calldata, depending on whether the paymentCurrency supports EIP-2612 Permit. For crosschain payments, this endpoint is NOT approval aware - it will return an approval permit or approval calldata even if approval has already been granted.

If the selected route is a direct payment, the GET /v2/request/{requestId}/pay returns an unsigned payment calldata. It may also return an approval calldata. For direct payments, this endpoint IS approval aware - it will omit the approval calldata if sufficient approval has already been granted.

Get payment calldata

get

Get the calldata needed to pay a request. For cross-chain payments, returns a payment intent that needs to be signed. For same-chain payments, returns transaction calldata. For off-ramp payments, use the query parameters clientUserId and paymentDetailsId.

Path parameters
requestIdstringRequired

The requestId of the request

Example: 01e273ecc29d4b526df3a0f1f05ffc59372af8752c2b678096e49ac270416a7cdb
Query parameters
walletstringOptional

The wallet address of the payer.

Example: 0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7
chainstring · enumOptional

The source chain of the cross chain payment

Possible values:
tokenstring · enumOptional

The source token of the cross chain payment

Possible values:
amountstringOptional

The amount to pay, in human readable format

clientUserIdstringOptional

Optional client user ID for off-ramp payments

Example: user-123
paymentDetailsIdstringOptional

Optional payment details ID for off-ramp payments

Example: fa898aec-519c-46be-9b4c-e76ef4ff99d9
feePercentagestringOptional

Fee percentage to apply at payment time (e.g., '2.5' for 2.5%)

Example: 0.02
feeAddressstringOptional

Address to receive the fee

Example: 0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7
Header parameters
x-api-keystringRequired

API key for authentication

Responses
200
Payment calldata retrieved successfully
application/json
get
GET /v2/request/{requestId}/pay HTTP/1.1
Host: api.request.network
x-api-key: text
Accept: */*
{
  "transactions": [
    {
      "data": "0xb868980b...00",
      "to": "0x11BF2fDA23bF0A98365e1A4e04A87C9339e8687",
      "value": {
        "type": "BigNumber",
        "hex": "0x038d7ea4c68000"
      }
    }
  ],
  "metadata": {
    "stepsRequired": 1,
    "needsApproval": false,
    "approvalTransactionIndex": null,
    "hasEnoughBalance": true,
    "hasEnoughGas": true
  }
}

4. Signing the payment intent

The intents and calldata returned by the GET /v2/request/{requestId}/pay endpoint in the previous step must be signed by the payer's wallet to authorize the crosschain payment. The process for signing the approval varies depending on whether the paymentCurrency supports EIP-2612 Permit, indicated by the metadata response parameter.

    "metadata": {
        "supportsEIP2612": true
    }

If the token does not support EIP-2612 Permit, the payer must sign and submit a standard ERC20 approval transaction.

import { ethers } from "ethers";

const ethersProvider = new ethers.providers.Web3Provider(
  // Connected wallet provider
  walletProvider as ethers.providers.ExternalProvider,
);
const signer = await ethersProvider.getSigner();

// Response from the `GET /request/{requestId}/pay` endpoint
const response = ...

const paymentIntent = JSON.parse(paymentData.paymentIntent);
const supportsEIP2612 = paymentData.metadata.supportsEIP2612;
let approvalSignature = undefined;
let approval = undefined;

if (supportsEIP2612) {
  approval = JSON.parse(paymentData.approvalPermitPayload);

  approvalSignature = await signer._signTypedData(
    approval.domain,
    approval.types,
    approval.values,
  );
} else {
  const tx = await signer.sendTransaction(paymentData.approvalCalldata);
  await tx.wait();
}

const paymentIntentSignature = await signer._signTypedData(
  paymentIntent.domain,
  paymentIntent.types,
  paymentIntent.values,
);

const signedData = {
  signedPaymentIntent: {
    signature: paymentIntentSignature,
    nonce: paymentIntent.values.nonce.toString(),
    deadline: paymentIntent.values.deadline.toString(),
  },
  signedApprovalPermit: approvalSignature
    ? {
      signature: approvalSignature,
      nonce: approval.values.nonce.toString(),
      deadline: approval?.values?.deadline
        ? approval.values.deadline.toString()
        : approval.values.expiry.toString(),
    }
    : undefined,
};

5. Sending the signed data

Finally, the signed payment intent (and possibly the signed approval permit) are sent back to execute the crosschain payment via the POST /v2/request/payment-intents/{paymentIntentId} endpoint. It will handle all the necessary steps to complete the payment. A payment.complete event will be sent to the platform's webhooks when the payment is completed.

Send a payment intent

post

Send a payment intent

Path parameters
paymentIntentIdstringRequired

The payment intent ID

Example: 01JNZYZPK7B4YBPD44TM72NDNJ
Header parameters
x-api-keystringRequired

API key for authentication

Body
Responses
401
Unauthorized
post
POST /v2/request/payment-intents/{paymentIntentId} HTTP/1.1
Host: api.request.network
x-api-key: text
Content-Type: application/json
Accept: */*
Content-Length: 154

{
  "signedPaymentIntent": {
    "signature": "text",
    "nonce": "text",
    "deadline": "text"
  },
  "signedApprovalPermit": {
    "signature": "text",
    "nonce": "text",
    "deadline": "text"
  }
}

No content

Custom fee configuration

It will be possible in the future to add a custom fee to the payment, this is currently under development.

Last updated

Was this helpful?