See also: API Reference Examples — executable TypeScript walkthroughs.
The Business
MedConnect is a healthcare platform that connects patients with specialist doctors for consultations, lab work, and follow-up care. Patients pay upfront, but their funds are held in escrow until the doctor confirms the service was delivered. If the service is not delivered within the agreed timeframe, the patient gets a full refund.Why Oak?
MedConnect needs a trustless escrow mechanism that:- Holds patient payments securely until service confirmation
- Allows the platform to confirm delivery on behalf of the provider
- Enables automatic refunds if service is not delivered
- Tracks fees (platform booking fee, protocol fee) transparently
- Works with any accepted ERC-20 on the campaign’s token whitelist (examples below use USDC for readability)
Oak Contract Used
| Contract | Purpose |
|---|---|
| CampaignInfoFactory | Creates the CampaignInfo contract that holds NFT receipts and the accepted token list |
| TreasuryFactory | Deploys the PaymentTreasury clone linked to the CampaignInfo |
| PaymentTreasury | Holds patient funds until the platform confirms service delivery. Supports line items, external fees, and refund flows |
Multi-token support
Payments specifypaymentToken; the contract reverts unless CampaignInfo.isTokenAccepted(paymentToken) is true. The campaign may accept several ERC-20s for one logical currency; pending, confirmed, fee, and refund accounting is per token address in each token’s native decimals. Snippets in this guide use USDC as a stand-in—use any whitelisted token your GlobalParams / campaign configuration allows. Resolve the list with globalParams.getTokensForCurrency(currency) or read the campaign’s cached copy via campaign.getAcceptedTokens() (same addresses the factory stored at creation).
Roles
| Role | Who | On-Chain Functions |
|---|---|---|
| Platform Admin | MedConnect backend | createPayment, createPaymentBatch, confirmPayment, confirmPaymentBatch, cancelPayment, claimRefund(paymentId, address) (non-NFT), claimExpiredFunds, claimNonGoalLineItems, pauseTreasury, unpauseTreasury, cancelTreasury |
| Platform Admin or Campaign Owner | MedConnect or clinic | withdraw, cancelTreasury |
| Patient (Buyer) | Sarah | ERC-20 approve, processCryptoPayment, claimRefundSelf(paymentId) (NFT payments) |
| Protocol Admin | Oak protocol | Receives protocol fees (via disburseFees) |
| Any caller | Anyone | disburseFees, all read functions (getPaymentData, getRaisedAmount, getExpectedAmount, paused, etc.) |
Integration Flow
Step 1: Create a CampaignInfo contract
Role: Any caller — createCampaign is permissionless.
Before deploying a PaymentTreasury, MedConnect needs a CampaignInfo contract. This holds NFT receipts for crypto payments and defines the accepted token list.
Step 2: Deploy the PaymentTreasury
Role: Any caller — deploy on TreasuryFactory is permissionless (the implementation must have been registered and approved during platform onboarding).
MedConnect deploys a PaymentTreasury linked to the CampaignInfo from Step 1.
Step 3: Patient books appointment — two independent payment flows
Sarah books a cardiology consultation with Dr. Rivera. The appointment costs 150 USDC broken down into two line items: consultation (120 USDC) and lab work (30 USDC). MedConnect supports two payment methods — they are not sequential steps:Flow A: Off-chain / fiat payment (createPayment)
Role: Platform Admin — only the platform admin can create payment records.MedConnect creates a payment record on-chain. The
createPayment transaction does not pull ERC-20 from the buyer’s wallet — it only records the obligation and pending accounting. Sarah pays through off-chain rails (credit card, insurance billing, etc.). Before MedConnect can call confirmPayment, the treasury must actually hold enough of the payment token on-chain (for example the platform deposits USDC after fiat settlement). The contract checks the treasury’s ERC-20 balance when confirming; if the tokens are not there, confirmPayment reverts.
Confirm payment (platform admin)
Role: Platform Admin — only the platform admin can callDr. Rivera completes the consultation and marks it as delivered. After off-chain verification and once the treasury holds the required ERC-20 balance, the backend callsconfirmPaymentfor payments created withcreatePayment.
confirmPayment to move accounting from pending to confirmed (and optionally mint an NFT if you pass Sarah’s wallet as buyerAddress).
Flow B: On-chain crypto payment (processCryptoPayment)
Role: Any caller — processCryptoPayment is permissionless, but the buyer must first approve the treasury to transfer their ERC-20 tokens.
This is a standalone operation — it creates the payment record AND transfers ERC-20 tokens in a single transaction. It does not require or complete a prior createPayment call. An NFT is minted to Sarah as proof of payment.
Sarah opens the MedConnect app, sees the $150 charge, and approves the transfer:
Step 4: Read the final treasury state
Role: Any caller — all read functions are public.MedConnect’s dashboard shows the current state of the escrow pool.
Step 5: Disburse fees
Role: Any caller — disburseFees is permissionless. Fees are sent to the Protocol Admin and Platform Admin automatically.
Before the provider can withdraw, accumulated protocol and platform fees are distributed.
Step 6: Withdraw settled funds
Role: Platform Admin or Campaign Owner — either party can trigger withdrawal. Funds are always sent to the campaign owner (Dr. Rivera’s clinic).The settled amount (minus fees) is sent to the campaign owner.
Alternative: Cancellation and refund flows
Three distinct paths exist depending on payment state and type:A) Cancel an unconfirmed off-chain payment (before
confirmPayment):
Role: Platform Admin — cancelPayment works only on unconfirmed, non-expired, non-crypto payments. The transaction removes the pending payment record from contract accounting; it does not automatically return ERC-20 that may already sit in the treasury—handle any token recovery operationally if you deposited before cancelling. Off-chain refunds (card reversal, etc.) are handled by MedConnect outside this call.
Role: Platform Admin —claimRefund(paymentId, refundAddress)refunds a confirmed payment where no NFT was minted. The contract verifies the payment is confirmed and hastokenId == 0.
processCryptoPayment):
Role: Any caller (NFT owner) —Before callingclaimRefundSelf(paymentId)is for crypto payments (auto-confirmed on creation). The contract looks up the NFT owner, burns the NFT, and sends the refundable amount to that owner. No priorcancelPaymentis needed — crypto payments cannot be cancelled viacancelPayment.
claimRefundSelf, the NFT owner must approve the treasury to manage the NFT. All pledge NFTs live on the CampaignInfo contract (not the treasury itself), so approval uses the CampaignInfo SDK entity:
Step 7: Claim non-goal line items
Role: Platform Admin — only the platform admin can claim non-goal line items.If the payment included line items that don’t count toward the campaign goal (e.g., platform commission, processing fees), these accumulate separately. The platform admin can claim them at any time after confirmation.
Step 8: Pause, unpause, or cancel the treasury
Pause the treasury:Role: Platform Admin — only the platform admin can pause and unpause.If MedConnect needs to halt operations for compliance or investigation:
Role: Platform Admin
Role: Platform Admin or Campaign Owner — either party can cancel.Cancellation is irreversible. After cancellation, backers can still claim refunds for confirmed NFT payments, but no new payments, confirmations, or withdrawals can happen.
Batch operations
Role: Platform Admin — batch create and confirm are platform-admin-only.For high-volume platforms, PaymentTreasury supports batch operations to reduce gas costs and prevent nonce conflicts. Batch create payments:
Architecture Diagram
MedConnect Healthcare Escrow FlowKey Takeaways
- ERC-20 approval is required — the buyer must
approvethe treasury contract beforeprocessCryptoPaymentcan transfer tokens createPaymentpath — thecreatePaymenttransaction does not pull ERC-20 from the buyer; fund the treasury beforeconfirmPayment(the contract checks balance on confirm)processCryptoPaymentpath — pulls tokens and confirms in one transaction; usedisburseFees/withdrawafterward—do not callconfirmPaymentfor these payments- Multi-token —
paymentTokenmust be on the campaign’s accepted list; balances and refunds are tracked per ERC-20 (each token’s decimals) - Funds stay in the treasury — held under contract rules until withdrawal, disbursement, or refund flows
- Role-based access —
createPayment/confirmPayment/cancelPaymentare platform-admin-only;processCryptoPaymentanddisburseFeesare permissionless;withdrawrequires admin or owner - Three cancellation/refund paths —
cancelPaymentdeletes unconfirmed off-chain records (no on-chain refund);claimRefund(paymentId, address)refunds confirmed non-NFT payments (platform admin);claimRefundSelf(paymentId)refunds crypto/NFT payments directly (NFT owner, no prior cancel needed; burns pledge NFT — requires prior ERC-721 approval on the CampaignInfo contract) - Line items allow granular tracking (consultation vs. lab work) with configurable goal-counting, fees, and refund rules
- Non-goal line items (e.g., platform commission) can be claimed separately via
claimNonGoalLineItems - Batch operations —
createPaymentBatchandconfirmPaymentBatchfor high-volume platforms - Pause / cancel controls — platform admin can pause; either admin or owner can permanently cancel
- External fees track platform charges transparently on-chain (informational only, no financial impact)
- Simulate before send catches errors before spending gas
multicallbatches multiple reads into a single RPC call for dashboard views