Payment Gateways
Architecture
Section titled “Architecture”All payment operations go through a standard adapter interface. Each gateway implements the same contract, and the rest of saas-core interacts only with the interface.
Commerce API │ ├── Adapter Interface │ ├── createCheckout() │ ├── verifyWebhook() │ ├── cancelSubscription() │ └── getSubscription() │ ├── LemonSqueezyAdapter (Phase 3) └── AppleIAPAdapter (Phase 4)LemonSqueezy (Phase 3)
Section titled “LemonSqueezy (Phase 3)”Checkout Flow
Section titled “Checkout Flow”1. User clicks "Upgrade" in Acme → Acme backend calls saas-core: POST /commerce/checkout → saas-core creates a checkout URL with custom_data → Returns checkout URL to Acme
2. User completes payment on LemonSqueezy
3. LS sends webhook to saas-core: POST /api/webhooks/lemonsqueezy → saas-core verifies webhook signature → Updates subscription status → Creates outbox event
4. saas-core outbox worker sends event to Acme webhookCustom Data
Section titled “Custom Data”Checkout requests include custom_data that saas-core uses to correlate the webhook back to the correct record:
{ "app_id": "tobby", "user_id": "u_xxx", "sku": "tobby.pro.monthly", "subscription_id": "sub_xxx"}Apple IAP (Phase 4)
Section titled “Apple IAP (Phase 4)”Verification Flow
Section titled “Verification Flow”1. User purchases in iOS StoreKit → iOS App gets transaction_id
2. iOS App sends transaction_id to its backend → Backend calls saas-core: POST /commerce/iap/verify
3. saas-core verifies with Apple App Store Server API → Upserts subscription record → Returns standardized Subscription object
4. Apple sends renewal/refund notifications → saas-core updates subscription statusProduct Mapping
Section titled “Product Mapping”Apple IAP products are mapped to saas-core prices via the gateway_product_id
field in the price table. When an IAP verification comes in, saas-core
looks up the gateway_product_id to find the corresponding SKU and
application.
Webhook Processing
Section titled “Webhook Processing”All gateway webhooks follow the same processing pipeline:
- Signature verification — reject forged webhooks
- Idempotency check — deduplicate by gateway event ID
- State transition — update subscription with optimistic locking
- Outbox event — create transaction outbox record for delivery