Guides

Stablecoin payments

Accept stablecoin payments in your app with Execute.

Your customer pays an executable address from any wallet or exchange, and the address passes exactly the price on to your treasury. You never ask them to connect a wallet, and they sign nothing but a transfer.

  1. Get an API key

    Create one in the dashboard, and keep it on your server. From a web page, use a client ID instead.

  2. The contracts

    Everything happens on Base. The intent calls these contracts, and nothing else:

    ContractAddressWhy
    USDC0x8335…2913The token your customer pays in, and the contract called to pass it on.
  3. The intent

    The token is USDC, and the amount is in its base units: USDC has 6 decimals, so 100 USDC is "100000000". Once the address holds exactly the amount, it makes this call, as itself, in one transaction:

    1transfer(address to, uint256 amount)on USDC

    The transfer every ERC-20 has. The executable address calls it as itself, so it moves the payment from its own balance to your treasury.

    ArgumentValueWhy
    to$TREASURYYour treasury: where the payment ends up.
    amount$AMOUNTThe price. It equals the intent’s amount, so the call spends all of it, as every intent must.

    recovery is $CUSTOMER: your customer’s address, so an overpayment or a late payment goes straight back to them. If you don’t know it, use your own address and refund by hand.

  4. Create the address

    Post the intent to Create an address. The expiry is 5 minutes away and the salt is random, so every run gives a new address:

    Request
    AMOUNT=25000000   # the price in base units: 25 USDC
    TREASURY=0x…      # your treasury
    CUSTOMER=0x…      # your customer’s address, for refunds
    
    curl https://api.execute.cash/v1/address/create \
      -H "Authorization: Bearer $EXECUTE_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "token": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
        "amount": "'$AMOUNT'",
        "calls": [
          {
            "target": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
            "function": "transfer(address to, uint256 amount)",
            "args": ["'$TREASURY'", "'$AMOUNT'"]
          }
        ],
        "expirationTimestamp": '$(( $(date +%s) + 300 ))',
        "recovery": "'$CUSTOMER'",
        "salt": "0x'$(openssl rand -hex 32)'",
        "chainId": 8453
      }'

    The response’s address is the executable address. Check it as the quickstart shows before you show it to anyone.

  5. Show it to your user

    Show the address with the exact amount, the token and the chain, in words, and a way to copy each. Beside them, a QR code of an EIP-681 payment request lets your user’s wallet fill in the chain, the token and the amount by itself:

    Payment request
    // address and intent, from the create response
    const request = `ethereum:${intent.token}@${intent.chainId}/transfer?address=${address}&uint256=${intent.amount}`;

    uint256 is in the token’s base units, like the intent’s amount. Say under the code what it fills in, and offer a plain code of the address alone for wallets that can’t read a request; your user then picks the token and chain and enters the amount themselves. The address’s page, execute.cash/address/<address>, shows your user what paying it will do.

    Your user pays with an ordinary transfer, from any wallet or exchange. They connect nothing and approve nothing.

  6. Track it

    Poll the address’s status, or get webhooks. Once it’s settled, the price is in your treasury, and the transaction is the payment’s receipt.