Quickstart
Create an invoice, send the payer to the hosted payment page, and wait for the payment. This takes about five minutes.
Before you start
- Sign in to the dashboard at app.sodapopfi.com
with the wallet that should receive payments, and generate an API key.
Merchant keys start with
dep_. - Note the wallet address you signed in with. It is the
ownerAddressof every request. - Pick a supported chain and token.
You can pass a token address or a symbol (
USDC,USDT,EURC); omittokenAddressto bill in the chain's native token.
Base URL: https://api.sodapopfi.com
.env
SODAPOP_BASE_URL=https://api.sodapopfi.com
SODAPOP_API_KEY=dep_...
OWNER_ADDRESS=0x501BEF961A6f40E063efD6048768b0BC35ab1428
End-to-end example
The script below:
- creates a 12.5 USDC invoice on Base;
- prints the payment page URL for the payer;
- polls the invoice until it is paid, cancelled or expired.
- TypeScript
- Python
- cURL
checkout.ts
import axios from "axios";
const api = axios.create({
baseURL: process.env.SODAPOP_BASE_URL,
headers: {
"Content-Type": "application/json",
"x-depay-api-key": process.env.SODAPOP_API_KEY ?? "",
},
});
const ownerAddress = process.env.OWNER_ADDRESS!;
// 1. Create the invoice — 12.5 USDC on Base.
const { data: invoice } = await api.post("/integration/invoices", {
ownerAddress,
chainId: 8453,
invoiceId: "1000456",
amount: "12.5",
tokenAddress: "USDC",
webhookUrl: "https://merchant.example.com/webhooks/invoice",
metadata: { orderId: "1000456" },
});
// 2. Send the payer to the hosted payment page.
// Store paymentAddress on your order — status lookups are keyed by it.
console.log(`Pay here: ${invoice.url}`);
// 3. Poll until the invoice reaches a final state.
// In production rely on the webhook and use polling for reconciliation.
const PAID = ["paid", "completed"];
const FAILED = ["cancelled", "expired"];
while (true) {
const { data } = await api.get("/integration/invoices/status", {
params: { ownerAddress, paymentAddress: invoice.paymentAddress },
});
const { status, balance } = data.invoice;
console.log(status, balance);
if (PAID.includes(status)) break; // fulfil the order
if (FAILED.includes(status)) break; // release the order
await new Promise((resolve) => setTimeout(resolve, 15_000));
}
Run with npm i axios and Node 20+.
checkout.py
import os
import time
import requests
BASE_URL = os.environ["SODAPOP_BASE_URL"].rstrip("/")
OWNER_ADDRESS = os.environ["OWNER_ADDRESS"]
session = requests.Session()
session.headers.update(
{
"Content-Type": "application/json",
"x-depay-api-key": os.environ["SODAPOP_API_KEY"],
}
)
# 1. Create the invoice — 12.5 USDC on Base.
response = session.post(
f"{BASE_URL}/integration/invoices",
json={
"ownerAddress": OWNER_ADDRESS,
"chainId": 8453,
"invoiceId": "1000456",
"amount": "12.5",
"tokenAddress": "USDC",
"webhookUrl": "https://merchant.example.com/webhooks/invoice",
"metadata": {"orderId": "1000456"},
},
timeout=30,
)
response.raise_for_status()
invoice = response.json()
# 2. Send the payer to the hosted payment page.
# Store paymentAddress on your order — status lookups are keyed by it.
print(f"Pay here: {invoice['url']}")
# 3. Poll until the invoice reaches a final state.
# In production rely on the webhook and use polling for reconciliation.
FINAL = {"paid", "completed", "cancelled", "expired"}
while True:
status = session.get(
f"{BASE_URL}/integration/invoices/status",
params={
"ownerAddress": OWNER_ADDRESS,
"paymentAddress": invoice["paymentAddress"],
},
timeout=30,
)
status.raise_for_status()
current = status.json()["invoice"]
print(current["status"], current.get("balance"))
if current["status"] in FINAL:
break
time.sleep(15)
Run with pip install requests and Python 3.9+.
checkout.sh
set -euo pipefail
BASE_URL="${SODAPOP_BASE_URL:-https://api.sodapopfi.com}"
# 1. Create the invoice — 12.5 USDC on Base.
INVOICE=$(curl -sS -X POST "$BASE_URL/integration/invoices" \
-H 'Content-Type: application/json' \
-H "x-depay-api-key: $SODAPOP_API_KEY" \
-d '{
"ownerAddress": "'"$OWNER_ADDRESS"'",
"chainId": 8453,
"invoiceId": "1000456",
"amount": "12.5",
"tokenAddress": "USDC",
"webhookUrl": "https://merchant.example.com/webhooks/invoice"
}')
# 2. Send the payer to the hosted payment page.
PAYMENT_ADDRESS=$(echo "$INVOICE" | jq -r '.paymentAddress')
echo "Pay here: $(echo "$INVOICE" | jq -r '.url')"
# 3. Poll until the invoice reaches a final state.
while true; do
STATUS=$(curl -sS "$BASE_URL/integration/invoices/status?ownerAddress=$OWNER_ADDRESS&paymentAddress=$PAYMENT_ADDRESS" \
-H "x-depay-api-key: $SODAPOP_API_KEY" | jq -r '.invoice.status')
echo "status: $STATUS"
case "$STATUS" in
paid|completed|cancelled|expired) break ;;
esac
sleep 15
done
What happens next
- As soon as the payment is detected, the invoice becomes
paidand yourwebhookUrlreceives aPOST(Webhooks). - SodaPop then sweeps the funds to your payout address. When the withdrawal
transaction is confirmed the invoice becomes
completed. - If nobody pays before
expiresAt(36 hours by default), the invoice becomesexpired.
paid is the signal to fulfil the order. You do not need to wait for
completed.
Next steps
- Authentication — sign requests with the owner wallet instead of an API key.
- Invoice lifecycle — statuses, expiry and late payments.
- Invoices API reference — all fields, filters and a reusable client.