Payments Overview
Understand YeetCode's two payment systems and when to use each.
YeetCode ships with two payment integrations that cover different business models. You can use either or both depending on your market and pricing strategy.
Two Systems, Two Models
| Polar.sh | Yoco | |
|---|---|---|
| Model | Recurring subscriptions | One-time access purchases |
| Currency | USD (international) | ZAR (South Africa) |
| Billing | Monthly/yearly, auto-renews | Pay once, access expires after a set duration |
| Customer management | Polar customer portal (upgrade, cancel, billing info) | No portal — buy again when access expires |
| Best for | SaaS with ongoing value (features, storage, seats) | Courses, digital products, time-limited content |
| Market | Global | South African businesses only |
Polar.sh — Recurring Subscriptions
Polar handles subscription lifecycle management: checkout, billing cycles, upgrades, downgrades, cancellations, and customer portals. Think Stripe, but with a simpler API.
How it works:
- You define subscription tiers in
config.yamlunderpricing - Customer clicks a plan on your marketing site
- Polar hosts the checkout page — handles card details, billing
- Polar sends webhooks to your Convex backend on subscription events
- Your backend tracks subscription status in the
subscriptionstable - Access is ongoing while the subscription is active
What you get out of the box:
- Checkout session creation
- Webhook handlers for
checkout.updated,order.created,subscription.created,subscription.updated,subscription.revoked - Subscription status tracking
- Customer portal (manage billing, cancel, update payment method)
- Discount code workflow (auto-generates 100% off codes for specific products)
- Confirmation and cancellation emails via Resend
Setup guide: Connect Polar to Convex In-depth guide: Polar Pricing & Subscriptions
Yoco — One-Time Access Purchases
Yoco is a South African payment gateway. Instead of subscriptions, customers buy time-limited access packages — 3 months, 6 months, or lifetime. When access expires, they buy again.
How it works:
- You define access packages in
config.yamlunderaccessPackages - Customer picks a package on your marketing site
- Yoco hosts the checkout page — handles card and EFT payments
- Yoco sends a webhook when payment succeeds
- Your backend grants access for the package's duration (stored in
accessGrantstable) - Cron job sends reminder emails as expiry approaches (14 days, 7 days, 1 day, at expiry)
What you get out of the box:
- Checkout session creation via Yoco SDK
- Webhook handler with HMAC-SHA256 signature verification
- Access granting with duration stacking (buying again extends from current expiry, not from now)
- Access query (
hasAccess) for frontend gating - Expiry reminder emails at 14/7/1/0 days
- Support for card and instant EFT payments
Setup guide: Connect Yoco to Convex In-depth guide: Yoco Pricing & Access Packages
Which Should I Use?
Use Polar if:
- Your customers are international
- You want recurring revenue with monthly/yearly billing
- You need a customer portal for self-service billing management
- Your product delivers ongoing value (features, storage, API calls)
Use Yoco if:
- Your customers are in South Africa
- You sell time-limited access (courses, content, tools with expiry)
- You want to accept ZAR payments with local card and EFT support
- You prefer a simpler "buy once, use for X days" model
Use both if:
- You have an international audience AND a South African audience
- You offer both subscription and one-time-purchase options
Both systems are fully independent — they use separate database tables, separate webhook endpoints, and separate config sections. You can enable one and ignore the other, or run both side by side.
Configuration
Both systems pull their product data from config.yaml at the repo root:
# Polar subscriptions (international, USD, recurring)
pricing:
- id: "free"
name: "Free"
price: 0
currency: "$"
priceLabel: "/month"
polarProductId: "your-polar-product-id"
# ... features, cta, etc.
# Yoco access packages (South Africa, ZAR, one-time)
accessPackages:
- id: "3-month"
name: "3 Month Access"
durationDays: 90
priceInCents: 29900
currency: "ZAR"Edit these sections to define your products. The marketing site reads from this config to render pricing cards, and the backend uses it to create checkout sessions and grant access.
Architecture
Both payment flows follow the same pattern:
Marketing UI → Convex action (create checkout) → Hosted checkout page
↓
Customer pays
↓
Convex HTTP endpoint ← Webhook ← Payment provider
↓
Webhook handler (verify signature, deduplicate, persist)
↓
Database trigger (grant access / track subscription)
↓
Email notification (via Resend)Webhook events are stored in the webhooks table for audit logging. Duplicate webhooks are detected by event ID and silently ignored.