Skip to main content
Source: Full executable TypeScript in contract/src/examples/03-campaign-payment-treasury/. This page summarizes the flow — read the source for complete per-step code.
PrerequisitesYour platform must be enlisted with the PaymentTreasury (or TimeConstrainedPaymentTreasury) implementation registered and approved — see Platform Enlistment.

The Story

CeloMarket is an online marketplace where independent artisans sell handcrafted goods. Unlike the crowdfunding scenarios, CeloMarket does not run time-bound campaigns with pledges and rewards. Instead, it processes individual e-commerce transactions — a buyer selects a product, pays with cryptocurrency, and the platform fulfills the order. CeloMarket uses the PaymentTreasury model, which works like a traditional payment processor but entirely on-chain. Every payment is broken down into line items (product price, shipping, tax) and follows a two-step flow for the off-chain path: the buyer pays, the treasury is funded, and the platform confirms after verifying the order. Direct on-chain checkout uses processCryptoPayment instead. In this scenario, a buyer named Sam purchases a handcrafted ceramic vase for $120 with $15 shipping. The payment flows through the treasury, gets confirmed by the platform (off-chain path) or settles in one transaction (processCryptoPayment), and the funds become available for withdrawal after fees are disbursed.

Multi-token support

Every payment record includes paymentToken. The treasury only accepts tokens that CampaignInfo.isTokenAccepted allows. Pending, confirmed, fee, and refund accounting is per ERC-20 contract (amounts in that token’s decimals).

PaymentTreasury vs. TimeConstrainedPaymentTreasury

The SDK’s oak.paymentTreasury(address) supports two on-chain variants through the same interface: From your code’s perspective, there is no difference. Time constraints are enforced transparently by the contract.

NFT Handling

All pledge/payment NFTs across every treasury type live on the CampaignInfo contract. No treasury contract is an ERC-721 itself. Before calling any refund function that burns an NFT (claimRefundSelf), the NFT owner must approve the treasury contract to manage the NFT via campaignInfo.approve(treasuryAddress, tokenId).

Role Reference

Steps

Step 1: Create Campaign

Before deploying a PaymentTreasury, CeloMarket needs a CampaignInfo contract, which will hold NFT receipts for crypto payments. E-commerce campaigns typically have goalAmount: 0n.
After the transaction is mined, the deployed CampaignInfo address can be discovered two ways. Both are shown in the source file — prefer Approach 1 when you have the receipt. Approach 1 — Decode CampaignCreated from the receipt (recommended): deterministic and works immediately, regardless of RPC indexing lag.
Approach 2 — Look up via identifierToCampaignInfo (convenience): handy when you only have the identifier and did not keep the receipt. On some RPC providers the mapping may briefly return the zero address right after the transaction, so prefer Approach 1 when the receipt is available.

Step 2: Deploy Treasury

CeloMarket deploys a PaymentTreasury linked to the CampaignInfo from Step 1. Slot 2n is PaymentTreasury; slot 3n is TimeConstrainedPaymentTreasury.

Step 3: Create Payment Record — Flow A (Platform Admin)

For off-chain payments, the Platform Admin creates a payment record on-chain that describes the order: payment ID, line items, external fees, and expiration. This step moves no funds; it records intent. The buyer pays through off-chain rails; the platform later confirms.
Batch variant: createPaymentBatch creates multiple records in a single transaction.

Step 4: Process Crypto Payment — Flow B (Buyer)

processCryptoPayment is an independent entry point: it creates the payment record AND transfers ERC-20 tokens to the treasury in a single transaction. It does not complete a prior createPayment. An NFT is minted as proof of payment.

Step 5: Confirm Payment — Flow A (Platform Admin)

After Sam’s tokens arrive in the treasury (off-chain path), CeloMarket runs fraud detection and validates the shipping address, then confirms on-chain. Until confirmation, funds remain in a pending state. Omit this step if using Flow B.
Batch variant: confirmPaymentBatch(paymentIds, buyerAddresses) for high-volume platforms.

Step 6: Read Payment Data

All payment and treasury data is publicly readable. Useful for order detail pages, customer support dashboards, and audit reports.

Step 7: Handle Refunds

Three distinct paths: A) Cancel an unconfirmed off-chain payment — Platform Admin calls cancelPayment(paymentId). Works only on unconfirmed, non-expired, non-crypto payments. B) Refund a confirmed off-chain payment (no NFT) — Platform Admin calls claimRefund(paymentId, refundAddress). C) Refund a crypto payment (NFT) — Any caller can claimRefundSelf(paymentId). The contract looks up the NFT owner, burns the NFT, and sends the refundable amount to that owner. Crypto payments are auto-confirmed; no prior cancelPayment is needed (and would revert).
Only line items marked as canRefund: true at creation time are included in the refund.

Step 8: Disburse Fees

Transfers accumulated protocol and platform fees to their recipients. Anyone can call it. Can be called multiple times as new payments are confirmed.

Step 9: Withdraw Funds

After fees have been disbursed, the Platform Admin or creator withdraws all remaining confirmed funds to the campaign owner’s wallet.

Step 10: Claim Expired Funds (TimeConstrainedPaymentTreasury only)

After the campaign deadline plus the platform’s claimDelay, the Platform Admin sweeps all remaining balances (confirmed, non-goal, refundable, fees).
If the claim delay has not elapsed, the call reverts with PaymentTreasuryClaimWindowNotReached.

Step 11: Claim Non-Goal Line Items (Platform Admin)

Non-goal line items (shipping fees, handling charges) accumulate separately. Call once per accepted token.

Step 12: Pause and Unpause (Optional)

Step 13: Cancel Treasury (Optional)

Permanent. Once cancelled, no new payments, confirmations, withdrawals, or fee disbursements are possible. Buyers can still claim refunds.