How To: Implement Purchases with USDC and Credits

Payers can use existing USDC and Credits in their EVM wallets to complete purchases.

This guide shows how merchants can let payers use existing USDC and credits in their EVM wallets to complete a purchase. To allow credit purchases before redemption,see this guide. For USDC purchases, payers must use an exchange.

  1. Get USDC Authorization Message
    Get an authorization message that must be signed by the payer’s EVM wallet. This authorizes spending the defined amount of USDC in the payer’s wallet.

    Request
    1curl --request POST \
    2 --url https://api-sandbox.coinflow.cash/api/checkout/evm/usdc-authorization \
    3 --header 'accept: application/json' \
    4 --header 'content-type: application/json' \
    5 --header 'x-coinflow-auth-blockchain: polygon' \
    6 --header 'x-coinflow-auth-wallet: 0xd11cc1D037B49098130BDeB592d468E3fe131240' \
    7 --data '
    8{
    9 "amount": {
    10 "cents": 200
    11 }
    12}
    13'
    Response
    1{
    2 "message": "{\"types\":{\"EIP712Domain\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"version\",\"type\":\"string\"},{\"name\":\"chainId\",\"type\":\"uint256\"},{\"name\":\"verifyingContract\",\"type\":\"address\"}],\"TransferWithAuthorization\":[{\"name\":\"from\",\"type\":\"address\"},{\"name\":\"to\",\"type\":\"address\"},{\"name\":\"value\",\"type\":\"uint256\"},{\"name\":\"validAfter\",\"type\":\"uint256\"},{\"name\":\"validBefore\",\"type\":\"uint256\"},{\"name\":\"nonce\",\"type\":\"bytes32\"}]},\"domain\":{\"name\":\"USDC\",\"version\":\"2\",\"chainId\":80002,\"verifyingContract\":\"0x41E94Eb019C0762f9Bfcf9Fb1E58725BfB0e7582\"},\"primaryType\":\"TransferWithAuthorization\",\"message\":{\"from\":\"0xd11cc1D037B49098130BDeB592d468E3fe131240\",\"to\":\"0xfcc779B1bc3b6C05406244107bAe04B858E7ED38\",\"value\":\"2000000\",\"validAfter\":\"0\",\"validBefore\":\"1754349007\",\"nonce\":\"0x23dfed622fcdea1a16019dea5d2e303905b79a290bb7ecdfb17dd40c76757bda\"}}",
    3 "messageData": {
    4 "from": "0xd11cc1D037B49098130BDeB592d468E3fe131240",
    5 "to": "0xfcc779B1bc3b6C05406244107bAe04B858E7ED38",
    6 "value": "2000000",
    7 "validAfter": "0",
    8 "validBefore": "1754349007",
    9 "nonce": "0x23dfed622fcdea1a16019dea5d2e303905b79a290bb7ecdfb17dd40c76757bda"
    10 }
    11}
  2. Sign the USDC Auth Message
    This signs the message that authorizes spending usdc from the payer’s wallet.

    1const { ethers } = require("ethers");
    2
    3const POLYGON_TESTNET_RPC = "https://rpc-amoy.polygon.technology";
    4const provider = new ethers.providers.JsonRpcProvider(POLYGON_TESTNET_RPC);
    5
    6const privateKey = '123456789abcdefg'; // Replace with signers wallet private key
    7const wallet = new ethers.Wallet(privateKey, provider);
    8const message = {
    9 "types": {
    10 "EIP712Domain": [
    11 { "name": "name", "type": "string" },
    12 { "name": "version", "type": "string" },
    13 { "name": "chainId", "type": "uint256" },
    14 { "name": "verifyingContract", "type": "address" }
    15 ],
    16 "TransferWithAuthorization": [
    17 { "name": "from", "type": "address" },
    18 { "name": "to", "type": "address" },
    19 { "name": "value", "type": "uint256" },
    20 { "name": "validAfter", "type": "uint256" },
    21 { "name": "validBefore", "type": "uint256" },
    22 { "name": "nonce", "type": "bytes32" }
    23 ]
    24 },
    25 "domain": {
    26 "name": "USDC",
    27 "version": "2",
    28 "chainId": 80002,
    29 "verifyingContract": "0x41E94Eb019C0762f9Bfcf9Fb1E58725BfB0e7582"
    30 },
    31 "primaryType": "TransferWithAuthorization",
    32 "message": {
    33 "from": "0xd11cc1D037B49098130BDeB592d468E3fe131240",
    34 "to": "0xfcc779B1bc3b6C05406244107bAe04B858E7ED38",
    35 "value": "2000000",
    36 "validAfter": "0",
    37 "validBefore": "1754349007",
    38 "nonce": "0x23dfed622fcdea1a16019dea5d2e303905b79a290bb7ecdfb17dd40c76757bda"
    39 }
    40}
    41
    42;
    43
    44async function signMessage(privateKey, message) {
    45 const domain = message.domain;
    46 const types = { CreditsAuthorization: message.types.CreditsAuthorization };
    47 const messageData = message.message;
    48
    49 // Sign the typed data (EIP-712)
    50 const signedMessage = await wallet._signTypedData(domain, types, messageData);
    51
    52 // Split signature into v, r, s
    53 const { v, r, s } = ethers.utils.splitSignature(signedMessage);
    54
    55 return {
    56 signedMessage,
    57 v,
    58 r,
    59 s
    60 };
    61}
    62
    63signMessage(privateKey, message)
    64 .then(({ signedMessage, v, r, s }) => {
    65 console.log('Signed Message:', signedMessage);
    66 console.log('v:', v);
    67 console.log('r:', r);
    68 console.log('s:', s);
    69 })
    70 .catch((error) => {
    71 console.error('Error signing message:', error);
    72 });
  3. Get Credits Authorization Message
    Get an authorization message that must be signed by the payer’s EVM wallet. This authorizes spending the defined amount of credits in the payer’s wallet.

    Request
    1curl --request POST \
    2 --url https://api-sandbox.coinflow.cash/api/redeem/evm/creditsAuthMsg \
    3 --header 'accept: application/json' \
    4 --header 'content-type: application/json' \
    5 --header 'x-coinflow-auth-blockchain: polygon' \
    6 --header 'x-coinflow-auth-wallet: 0xd11cc1D037B49098130BDeB592d468E3fe131240' \
    7 --data '
    8{
    9 "subtotal": {
    10 "currency": "USD",
    11 "cents": 300
    12 },
    13 "transactionData": {
    14 "type": "token",
    15 "destination": "0x70bd2A7a9eedE3aCb62cF839cEa1008b72FF7844"
    16 }, // Replace with any transaction data that suites your use case. This assumes we are settling direct to a merchants EVM wallet
    17 "merchantId": "mello",
    18 "usdcAmount": {
    19 "cents": 200
    20 }
    21}
    22'
    Response
    1{
    2 "message": "{\"domain\":{\"name\":\"Coinflow Credits Contract\",\"version\":\"1\",\"chainId\":80002,\"verifyingContract\":\"0xfcc779B1bc3b6C05406244107bAe04B858E7ED38\"},\"message\":{\"customerWallet\":\"0xd11cc1D037B49098130BDeB592d468E3fe131240\",\"creditSeed\":\"mello\",\"amount\":3000000,\"validBefore\":\"1754432077\",\"nonce\":\"0x2c8f63dfc74e880d4581563efd45f6216a26457dde384d6060bb75a05a1d7684\"},\"primaryType\":\"CreditsAuthorization\",\"types\":{\"EIP712Domain\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"version\",\"type\":\"string\"},{\"name\":\"chainId\",\"type\":\"uint256\"},{\"name\":\"verifyingContract\",\"type\":\"address\"}],\"CreditsAuthorization\":[{\"name\":\"customerWallet\",\"type\":\"address\"},{\"name\":\"creditSeed\",\"type\":\"string\"},{\"name\":\"amount\",\"type\":\"uint256\"},{\"name\":\"validBefore\",\"type\":\"uint256\"},{\"name\":\"nonce\",\"type\":\"bytes32\"}]}}",
    3 "validBefore": "1754432077",
    4 "nonce": "0x2c8f63dfc74e880d4581563efd45f6216a26457dde384d6060bb75a05a1d7684",
    5 "creditsRawAmount": 3000000
    6}
  4. Sign Credits Authorization Message
    This signs the message that authorizes spending credits from the payer’s wallet.

    Text
    1const { ethers } = require("ethers");
    2
    3// Create RPC connection
    4const POLYGON_TESTNET_RPC = "https://rpc-amoy.polygon.technology";
    5const provider = new ethers.providers.JsonRpcProvider(POLYGON_TESTNET_RPC);
    6
    7const privateKey = '123456789abcdefg'; // Replace with payer's wallet private key
    8const wallet = new ethers.Wallet(privateKey, provider);
    9const message = {
    10 "domain": {
    11 "name": "Coinflow Credits Contract",
    12 "version": "1",
    13 "chainId": 80002,
    14 "verifyingContract": "0xfcc779B1bc3b6C05406244107bAe04B858E7ED38"
    15 },
    16 "message": {
    17 "customerWallet": "0xd11cc1D037B49098130BDeB592d468E3fe131240",
    18 "creditSeed": "mello",
    19 "amount": 3000000,
    20 "validBefore": "1754432077",
    21 "nonce": "0x2c8f63dfc74e880d4581563efd45f6216a26457dde384d6060bb75a05a1d7684"
    22 },
    23 "primaryType": "CreditsAuthorization",
    24 "types": {
    25 "EIP712Domain": [
    26 {
    27 "name": "name",
    28 "type": "string"
    29 },
    30 {
    31 "name": "version",
    32 "type": "string"
    33 },
    34 {
    35 "name": "chainId",
    36 "type": "uint256"
    37 },
    38 {
    39 "name": "verifyingContract",
    40 "type": "address"
    41 }
    42 ],
    43 "CreditsAuthorization": [
    44 {
    45 "name": "customerWallet",
    46 "type": "address"
    47 },
    48 {
    49 "name": "creditSeed",
    50 "type": "string"
    51 },
    52 {
    53 "name": "amount",
    54 "type": "uint256"
    55 },
    56 {
    57 "name": "validBefore",
    58 "type": "uint256"
    59 },
    60 {
    61 "name": "nonce",
    62 "type": "bytes32"
    63 }
    64 ]
    65 }
    66}
    67;
    68
    69async function signMessage(privateKey, message) {
    70 const domain = message.domain;
    71 const types = { CreditsAuthorization: message.types.CreditsAuthorization };
    72 const messageData = message.message;
    73
    74 // Sign the typed data (EIP-712)
    75 const signedMessage = await wallet._signTypedData(domain, types, messageData);
    76 return signedMessage;
    77}
    78
    79signMessage(privateKey, message)
    80 .then((signedMessage) => {
    81 console.log('Signed Message:', signedMessage);
    82 })
    83 .catch((error) => {
    84 console.error('Error signing message:', error);
    85 });
  5. Send redeem transaction
    By calling our redeem endpoint, you are creating a transacting and passing the signed credits authorization message and signed usdc authorization message. Upon sending to the blockchain, the payer’s credit balance and usdc balance in their wallet will decrement.

    Request
    1curl --request POST \
    2 --url https://api-sandbox.coinflow.cash/api/redeem/evm/sendGaslessTx \
    3 --header 'accept: application/json' \
    4 --header 'content-type: application/json' \
    5 --header 'x-coinflow-auth-blockchain: polygon' \
    6 --header 'x-coinflow-auth-wallet: 0xd11cc1D037B49098130BDeB592d468E3fe131240' \
    7 --data '
    8{
    9 "subtotal": {
    10 "currency": "USD",
    11 "cents": 300
    12 },
    13 "transactionData": {
    14 "type": "token",
    15 "destination": "0x70bd2A7a9eedE3aCb62cF839cEa1008b72FF7844"
    16 },
    17 "signedMessages": {
    18 "customerUsdcAuthData": {
    19 "s": "0x4f2d037027479a7c728bab0bcc6fc08d8cd27cde3a73dd7be2384be411ecd3b8",
    20 "r": "0x709795a8260afdd500c392203b9e9fb48071e69c49d7e8350c552af6519b59c0",
    21 "v": 27,
    22 "nonce": "0x23dfed622fcdea1a16019dea5d2e303905b79a290bb7ecdfb17dd40c76757bda",
    23 "validBefore": "1754349007",
    24 "validAfter": "0",
    25 "value": "2000000",
    26 "to": "0xfcc779B1bc3b6C05406244107bAe04B858E7ED38",
    27 "from": "0xd11cc1D037B49098130BDeB592d468E3fe131240"
    28 },
    29 "permitCredits": "0x5aecc8c89be221d0e1e0266252a49d6cca597ceabf52def39866e67858eaf3a50256deed3815265acf4ba410a60950a683269562e7e5f4b46f8183cfd466008d1b"
    30 },
    31 "usdcAmount": {
    32 "cents": 200
    33 },
    34 "validBefore": "1754432077",
    35 "nonce": "0x2c8f63dfc74e880d4581563efd45f6216a26457dde384d6060bb75a05a1d7684",
    36 "creditsRawAmount": 3000000,
    37 "merchantId": "mello"
    38}
    39'
    Response
    1{
    2 "hash": "0x1d6ba92970cf0285c40f167e7f5c030a7a8177e046caf270ca455ba9d040e734"
    3}
  6. Optional Implementation:Get Balances
    This endpoint will allow you to get the balance of credits/usdc in the payer’s wallet.