Integration Guide

Add stablecoin checkout to your website.

Include one script, configure your wallet, and start accepting payments. No backend integration required.

3-step setupNo backend requiredWebhook supportLive demo

Prerequisites

Set up your wallet to receive payments.

One-time setup. Your wallet address establishes the settlement endpoint for all USDC transactions.

Quick start

Three steps to accepting payments.

No backend integration required. Add the script, configure your wallet, and create payment buttons.

01

Include the script

Add this line just before the closing </body> tag.

<script src="https://app.instantescrow.nz/conduit-checkout.js"></script>
02

Initialize with your wallet

Configure the checkout with your merchant wallet address.

<script> ConduitCheckout.init({ // REQUIRED: Your wallet address to receive payments sellerAddress: '0xYourWalletAddressHere', // REQUIRED: Base URL of checkout page baseUrl: 'https://app.instantescrow.nz', // RECOMMENDED: Auto-send verified payment to your backend webhookUrl: 'https://yoursite.com/api/conduit-webhook', webhookSecret: 'your-secret-key', // Optional. Not the check that matters — see below. // Optional: Default token ('USDC' or 'USDT') tokenSymbol: 'USDC', // Optional: Days until auto-release (default: 7) expiryDays: 7, // Optional: Display mode ('popup' or 'redirect') mode: 'popup', // Success callback (webhook already sent!) onSuccess: function(data) { console.log('Payment verified!', data); alert('Thank you! Order #' + data.orderId + ' confirmed!'); }, // Error callback onError: function(error) { console.error('Payment failed:', error); alert('Payment failed: ' + error); }, // Cancel callback onCancel: function() { console.log('Payment cancelled'); } }); </script>
03

Add payment buttons

Create buttons that open the checkout.

<button onclick="ConduitCheckout.open({ amount: '50.00', description: 'Premium Product' })"> Pay $50 with USDC </button>

Display modes

Choose how checkout appears.

Popup window

Opens in a centered popup. Best for minimal disruption to the shopping experience.

mode: 'popup'

Examples

Common integration patterns.

Basic product payment

Simple checkout for a single product with order tracking.

<button onclick="ConduitCheckout.open({ amount: '29.99', description: 'Premium Widget - Blue', orderId: 'ORDER-12345' })"> Buy Now - $29.99 </button>

Reference

Configuration options.

ConduitCheckout.init(options)

OptionTypeRequiredDefaultDescription
sellerAddressstringYes-Your wallet address to receive payments
baseUrlstringYes-Base URL of checkout page
webhookUrlstringNo-Webhook URL for payment verification
webhookSecretstringNo-Optional HMAC secret for webhook signatures. Set in the browser, so confirm against /api/results before fulfilling
tokenSymbolstringNo'USDC''USDC' or 'USDT'
expiryDaysnumberNo7Days until auto-release to seller
modestringNo'popup''popup' or 'redirect'
onSuccessfunctionNo-Callback when payment completes
onErrorfunctionNo-Callback when payment fails
onCancelfunctionNo-Callback when user cancels

ConduitCheckout.open(params)

ParameterTypeRequiredDescription
amountstring/numberYesPayment amount (e.g., '50.00')
descriptionstringYesPayment description (max 160 chars)
orderIdstringNoYour internal order/transaction ID
emailstringNoCustomer email address
tokenSymbolstringNoOverride default token for this payment
expiryDaysnumberNoOverride default expiry
webhookUrlstringNoOverride webhook URL for this payment
metadataobjectNoCustom metadata to include

Backend integration

Webhook-based order fulfillment.

Get told when a payment completes, then confirm it yourself. The webhook says something happened; /api/results is the source of truth, and it is a public endpoint you can query from your server with no key and no setup.

01

Configure SDK with webhook

The SDK handles verification and webhook delivery automatically. No manual fetch() calls needed.

ConduitCheckout.init({ sellerAddress: '0xYourWalletAddress', baseUrl: 'https://app.instantescrow.nz', // Webhook config webhookUrl: 'https://yoursite.com/api/conduit-webhook', // Optional. It signs the POST, but this file is served to every visitor, so treat // the signature as a hint and let the /api/results lookup below decide. webhookSecret: 'your-secret-key', onSuccess: function(verifiedData) { // Webhook already sent! Just show UI confirmation console.log('Payment verified!', verifiedData); window.location.href = '/thank-you?order=' + verifiedData.orderId; }, onError: function(error) { alert('Payment failed: ' + error); } });
02

Confirm it, then fulfil

Ask /api/results what really happened before you ship anything. It reads the same record the checkout polls, so it settles the amount, the currency and who was paid — and a request that never arrives changes nothing.

// POST /api/conduit-webhook app.post('/api/conduit-webhook', async (req, res) => { try { const { contractId, orderId } = req.body; // 1. ASK THE SOURCE OF TRUTH. Never fulfil on the request body alone — // this is the only thing here that your own server has checked. const lookup = await fetch('https://api.stabledrop.me/api/results', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ contractid: contractId }) }); const { count, results } = await lookup.json(); if (count === 0) { return res.status(404).json({ error: 'No such payment' }); } const payment = results[0]; // 2. CHECK IT IS THE PAYMENT YOU WERE EXPECTING const order = await db.orders.findUnique({ where: { id: orderId } }); // `state` is what says the money arrived. List the states that mean it did, // rather than ruling out the ones that mean it did not: a state you have not // heard of must not be read as payment. Waiting on one that turns out to be // fine costs another poll; clearing one that is not costs you the goods. // // ⚠️ chainAddress is NOT that signal. An escrow's address is worked out from // its terms and recorded when the buyer opens the payment page, before they // send anything — so it is present on unpaid orders too. const FUNDED_STATES = ['ACTIVE', 'DISPUTED', 'RESOLVED', 'CLAIMED', 'COMPLETED']; const paidToYou = payment.sellerWalletId.toLowerCase() === YOUR_WALLET.toLowerCase(); const amount = payment.currency.toLowerCase().startsWith('micro') ? Number(payment.amount) / 1000000 : Number(payment.amount); if (!paidToYou) return res.status(400).json({ error: 'Not your wallet' }); if (!FUNDED_STATES.includes(payment.state)) return res.status(409).json({ error: 'Not funded yet' }); if (Math.abs(amount - order.total) > 0.001) { return res.status(400).json({ error: 'Amount mismatch' }); } // 3. NOW IT IS SAFE TO FULFIL await db.orders.update({ where: { id: orderId }, data: { status: 'PAID', paymentContractId: contractId, paymentChainAddress: payment.chainAddress, paymentAmount: amount, paidAt: new Date() } }); await fulfillment.ship(orderId); await email.sendConfirmation(order.email, orderId); res.json({ received: true }); } catch (error) { console.error('Webhook error:', error); res.status(500).json({ error: 'Internal server error' }); } });
03

Check a payment yourself

The same query, on its own. Public, unauthenticated, no key to request — give it a contractid and it tells you what was paid and to whom. Useful for reconciliation, for support, and for replaying anything a webhook missed.

curl -X POST https://api.stabledrop.me/api/results \ -H 'Content-Type: application/json' \ -d '{"contractid": "507f1f77bcf86cd799439011"}'

Response:

{ "count": 1, "results": [ { "contractid": "507f1f77bcf86cd799439011", "chainAddress": "0x1234567890abcdef1234567890abcdef12345678", "sellerWalletId": "0xYourWalletAddress", "amount": 50000000.0, "description": "Order 1234", "currency": "microUSDC", "state": "CLAIMED", "chainId": "8453", "createdate": 1705318200, "maturity": 1705404600 } ] }

Query fields

FieldTypeDescription
contractidstringExact match on the contract id the checkout returns
chainAddressstringExact match on the escrow address
chainAddressesstring[]The batch form. OR within the list, so one round trip fills in a whole page of contracts instead of one call each. Case-insensitive, because addresses read from event logs are lower case while records may be checksummed
sellerWalletIdstringMatches seller OR buyer, case-insensitively — this is how you list everything paid to your wallet

All optional, but at least one is required — an empty body returns 400. Multiple fields combine with AND.

Response fields

FieldTypeDescription
contractidstringThe contract id
chainAddressstringThe escrow address. Assigned when the buyer opens the payment page, so its presence says nothing about whether they paid — read state for that
sellerWalletIdstringWho gets paid. Compare against your own wallet
amountnumberIn the units of currency below, so divide by 1,000,000 for a micro currency
descriptionstringWhat the payment was for
currencystringe.g. microUSDC. There is no separate currencySymbol field
statestringWhether the money arrived. ACTIVE, DISPUTED, RESOLVED, CLAIMED and COMPLETED all mean it did; treat anything else as not yet paid, including states you do not recognise
chainIdstringe.g. 8453 for Base
createdatenumberUnix seconds when the record was made
maturitynumberUnix seconds when the cashflow unlocks. Distinct from createdate

count: 0 means no such payment — treat it as unpaid, never as an error. You can also query by chainAddress, or by sellerWalletId to list everything paid to your wallet. Amounts in a micro-prefixed currency are in millionths, so divide by 1,000,000. state is the escrow's lifecycle, not a pass/fail — a completed payment reads CLAIMED, so check for the failure states rather than for one success value.

Webhook payload

{ "contractId": "507f1f77bcf86cd799439011", "chainAddress": "0x5678...", "seller": "0xYourWalletAddress", "amount": 50.00, "amountRaw": 50000000, "currencySymbol": "USDC", "currencyRaw": "microUSDC", "description": "Order 1234", "state": "OK", "verified": true, "verifiedAt": "2026-01-15T10:30:00.000Z", "orderId": "1234", "email": "buyer@example.com", "metadata": {}, "timestamp": 1705318200 }

Verification

What verified data means.

FieldMeaning
verified: trueBackend confirmed payment exists in blockchain
state: "ACTIVE"Funds are locked in escrow contract
seller: "0x..."Matches your wallet (verified by SDK)
amount: 50.0Matches expected amount (verified by SDK)
chainAddressBlockchain contract address (permanent record)

Once you receive verified data, it is safe to fulfil the order. The SDK has confirmed everything on-chain.

Platform

Built-in protection for every transaction.

Buyer protection

  • Funds held in escrow smart contract
  • Time-delayed release (default 7 days)
  • Dispute mechanism
  • Admin arbitration

No gas fees

  • Platform covers all blockchain fees
  • Users pay 1% platform fee
  • Minimum payment: $1.001
  • Fee included in payment amount

Client-side security

  • Users sign with own wallet
  • No custody of user funds
  • HTTPS required
  • Open-source smart contracts

Multi-token support

  • USDC (default)
  • USDT (optional)
  • Base network (Ethereum L2)
  • Low transaction costs

End-to-end flow

01

Customer clicks pay

Opens checkout popup or redirect.

02

Wallet connects & pays

Customer signs the transaction.

03

SDK verifies on-chain

Automatic blockchain verification.

04

Webhook sent to backend

Your server receives verified data.

05

Order fulfilled

Ship goods, deliver product, send receipt.

FAQ

Frequently asked questions.

What tokens are supported?
Currently USDC on the Base network (Ethereum Layer 2).
What are the fees?
1% platform fee per transaction. No setup costs, no monthly fees. Minimum payment is $1.001. Gas fees are covered by the platform.
How long until I receive funds?
Funds are automatically released after the expiry period (default 7 days) unless the buyer raises a dispute. You can customize the expiry period per payment.
Can buyers get refunds?
Buyers can raise a dispute within the protection period. Our admin team reviews disputes and can release funds to either party based on the evidence.
Do I need a crypto wallet?
Yes, you need a wallet address to receive payments. We recommend MetaMask, Coinbase Wallet, or any Web3-compatible wallet. Buyers can use email + social login (we create embedded wallets for them).
Is this secure?
Yes. Payments are secured by immutable smart contracts on the Base blockchain. Users sign transactions with their own wallets. All transactions are auditable on-chain. HTTPS is required for all integrations.
What about chargebacks?
Cryptocurrency transactions are irreversible — there are no chargebacks like with credit cards. The escrow system provides buyer protection through the dispute mechanism during the protection period.

Ready to integrate?

Try the interactive demo or start integrating directly into your site.