Skip to main content

Examples

In this section, we'll demonstrate the technologies we use for our home page and the payment request form.

Creating a transaction

Creating a transaction is the first step to receiving tokens through ChainPay. You define the token you want to receive, the amount, and the recipient to whom the tokens should be forwarded. Note that payments can only be processed if the recipient is either the signer or a smart contract registered with ChainPay, where the signer is also registered as the contract's signer.

Viem.sh

To create a payment request using Viem, you just need to specify which tokens you want to receive and pass the relevant information to the Viem WalletClient. Typically, creating a payment request with Viem is done on the client side by users looking to initiate a transaction.

import chainpay, { type TransactionType } from 'chainpay';

const transaction: TransactionType = await chainpay.createTransaction({
walletClient, // user's wallet client
account: walletAccount, // user's wallet address
token: '0xe9e7cea3dedca5984780bafc599bd69add087d56', // BUSD
amount: BigInt('10000000000000000000'), // $10 equivalent
data_string: Date.now().toString(), // custom data, like user ID or another identifier
type: 'viem',
});

// Share transaction.encoded, which contains all the data for the transaction as a string

Ethers.js

Creating a payment request using Ethers.js is typically done on the server side, when the developer wants to initiate a payment.

import chainpay, { type TransactionType } from 'chainpay';
import { Wallet } from 'ethers';

// Import your private key
const wallet = new Wallet('0x...');

const transaction: TransactionType = await chainpay.createTransaction({
signer: wallet,
recipient: undefined, // optional, defaults to `wallet.address` if not specified
token: '0xe9e7cea3dedca5984780bafc599bd69add087d56', // BUSD
amount: BigInt('10000000000000000000'), // $10 equivalent
data_string: Date.now().toString(), // custom data, like user ID or another identifier
type: 'ethers',
});

// Share transaction.encoded, which contains all the data for the transaction as a string

Do it yourself

If you'd prefer to handle things manually, whether you're using a different library or simply because you're a coding wizard, here's an example:

import chainpay from 'chainpay';

const { data, messageHash } = chainpay.createTransactionRaw({
recipient: '0x7982985F05a9dabD3F26dC81CB161f440BE48eE5', // my wallet address (hehe)
token: '0xe9e7cea3dedca5984780bafc599bd69add087d56', // BUSD
amount: BigInt('10000000000000000000'), // $10 equivalent
data_string: '', // either `data_string` or `data_raw` must be set
});

Once this is done, you can review the data (which will be your input converted to hex), and the sign messageHash.

Displaying a transaction

Our widget (redirect)

One of the simplest ways to allow your users to make a payment is by redirecting them to our page. The TransactionType object, returned when creating a transaction, includes a pay key. This pay key contains a URL where the user can be redirected to complete the payment.

import chainpay, { type TransactionType } from 'chainpay';

// Retrieve the transaction object, for example:
const transaction: TransactionType =
chainpay.utils.decodeTransaction(encodedTransaction);

window.open(transaction.pay, '_blank');

Our widget (iframe)

Alternatively, you can display our payment widget directly within your webpage using an iframe. The ChainPay library provides built-in support for this, and it’s very easy to implement.

<iframe id="pay_me"></iframe>
import chainpay, { type TransactionType } from 'chainpay';

// Retrieve the transaction object, for example:
let transaction: TransactionType =
chainpay.utils.decodeTransaction(encodedTransaction);

await transaction.widget('#pay_me');
// "Paid" message received from iframe!

In this example, the widget is embedded into the iframe you created. The widget will notify your webpage once the payment is completed. That's why the code uses await—it waits until the payment is processed before continuing with any subsequent logic.

Do it yourself

So, you're feeling adventurous and want to take control of things yourself again, huh? Well, we can't hold your hand through this part, but there's one thing you really need to watch out for:

import chainpay from 'chainpay';

let transaction: TransactionType;
let userSelectedToken = '0x...'; // The token your user wants to use for payment

const pool = const chainpay.utils.findPool(transaction.token, userSelectedToken);

It's crucial to ensure that the token your user has selected is supported by ChainPay. Currently, ChainPay is still in its early stages, and not all token pools are supported. Running the findPool function provides important information. If the result is undefined, the token combination is not supported by ChainPay at this time. Otherwise, it returns an object containing the feeTier, which will be essential for the next steps.

Paying a transaction

Currently, this process is only available in "do it yourself" format. If you prefer a more straightforward approach, consider using the widgets described above. The pay function provides all the necessary data to send a transaction. Below is an example of how to use it.

import chainpay, { type TransactionType } from 'chainpay';

// Retrieve the transaction object, for example:
let transaction: TransactionType =
chainpay.utils.decodeTransaction(encodedTransaction);

const data: PaymentData<typeof chainpay.abi> = await chainpay.pay({
transaction, // You can directly input the TransactionType object or the encoded transaction
token: '0x...', // The token you or your user wants to use for payment
amount: BigInt('98457367349'), // The amount to be paid
feeTier: undefined, // Optional: if not set, the library will automatically run `utils.findPool`
chainpayContract: chainpay.constants.CHAINPAY_CONTRACT_ADDRESS,
type: 'raw', // Currently, only 'raw' type is supported
});

if (data.approve) {
// Approve token spending for the `CHAINPAY_CONTRACT_ADDRESS` with the specified token and amount
}

// Example with viem.sh:
const payResult = await publicClient.simulateContract({
address: data.chainpayContract,
abi: data.abi,
functionName: data.functionName,
account: walletAccount, // Retrieve this from viem
args: data.args,
value: data.value as any,
chain: bsc, // Import this from 'viem/chains'
});

The data variable will now contain all the data you need to pay the transaction yourself. Please check the API docs for further information on what it returns and how to use it.

You may have noticed that you need to specify the amount even if the user is paying with a different token. This means you will need to calculate how much of tokenB the user needs to send in order for you to receive the correct amount of tokenA. Be sure to include some slippage to prevent the transaction from failing unexpectedly. Don’t worry—any excess tokens sent by the user will be automatically refunded by the ChainPay contract.

Verifying a transaction payment

It is always recommended to verify whether the transaction has been fully paid, even if the iframe widget indicates that payment was successful. Since this confirmation happens on the client side, it could be tampered with. To ensure security, verify the payment on your server using the original signature.

import chainpay from 'chainpay';

const paid: boolean = chainpay.isPaid(
chainpay.constants.CHAINPAY_CONTRACT_ADDRESS,
signature
);
  • The signature can either be saved on your end when the transaction is created or extracted from the transaction.signature field.
  • If you extract the signature from the transaction, ensure that the signature is indeed yours and that the transaction data is accurate before proceeding with the verification.