Craft your own billing. Any provider. Any platform. 15 minutes.
Self-hosted, provider-agnostic billing library for Kotlin Multiplatform apps. Uses Supabase as the source of truth — your app never talks to a payment provider directly. Open-source SDK + optional hosted dashboard for subscriber management and analytics.
Client App ──→ PayCraft SDK ──→ Supabase (source of truth)
↑
Payment Provider ──webhook──────┘
↑
Dashboard ──────────────────────┘
npx paycraft initThe CLI scaffolds your Supabase schema, Edge Functions, and app configuration interactively.
The fastest way to add PayCraft billing to your KMP app — no cloning, no setup, one prompt.
cd /path/to/your-kmp-app
claudeFetch https://raw.githubusercontent.com/mobilebytelabs/paycraft/main/client-skills/paycraft-adopt.md
Save it to .claude/commands/paycraft-adopt.md in this project, then run /paycraft-adopt.
That's it. Claude handles everything from here:
| Phase | What Claude does | What you do |
|---|---|---|
| Bootstrap | Searches for PayCraft locally, offers to clone it if not found — asks where on your system | Pick a location (or press Enter for ~/paycraft/) |
| Phase 1 — ENV | Creates .env, walks you through each credential |
Answer ~8 questions (Supabase keys, Stripe key, plans) |
| Phase 2 — Supabase | Applies migrations, creates RPCs, deploys webhook, verifies every step | Nothing |
| Phase 3 — Stripe | Creates test product, prices, payment links automatically via Stripe MCP | 2 browser steps: add webhook endpoint + enable portal |
| Phase 4 — Client | Adds dependency, writes PayCraft.configure(), wires Koin + paywall UI |
Nothing |
| Phase 5 — Verify | Writes real DB row, calls is_premium(), confirms true, cleans up |
Nothing |
Result: fully operational billing in test mode. No real charges until you opt in.
After setup, these commands are available in your project:
/paycraft-adopt-env ← re-collect credentials (key rotation)
/paycraft-adopt-supabase ← re-deploy webhook or re-apply migrations
/paycraft-adopt-stripe ← create live products when ready to ship
/paycraft-adopt-client ← re-integrate into a different app
/paycraft-adopt-verify ← re-verify after any config change
/paycraft-adopt-migrate ← migrate to new Supabase/Stripe account or switch providers
After setup completes, your app contains a .paycraft/ directory — a complete record of
how PayCraft is configured in your project:
your-app/
├── .env ← gitignored (auto-added by setup) — all PayCraft keys
└── .paycraft/
├── config.json ← setup answers — safe to commit
├── deployment.json ← resource IDs, no secrets — safe to commit
├── supabase/ ← SQL + Edge Function backup — safe to commit
└── backups/ ← gitignored — timestamped .env copies
Step 5 automatically adds .env and .paycraft/backups/ to your .gitignore.
Everything else in .paycraft/ is safe to commit as deployment documentation.
- Claude Code CLI (
npm install -g @anthropic-ai/claude-code) - KMP app with Koin DI
- Supabase project (free tier works)
- Stripe or Razorpay account (test mode for setup)
Moving to a new Supabase org, rotating Stripe accounts, or switching from Stripe to Razorpay?
/paycraft-adopt-migrate
Claude shows your current deployment, asks what's changing, backs up your .env, collects
only the new credentials, re-deploys only the affected components, and re-verifies end-to-end.
No full re-setup. Subscriber data migration included (optional).
| Migration | Command handles |
|---|---|
| New Supabase project | Re-deploys migrations + webhook, migrates subscriber data |
| New Stripe account | Re-creates products, prices, payment links, webhook |
| New Razorpay account | Re-creates plans, payment links, webhook |
| Stripe → Razorpay | Full provider switch, updates app config |
| Razorpay → Stripe | Full provider switch, updates app config |
- Multi-provider — Stripe, Razorpay, Paddle, PayPal, LemonSqueezy, Flutterwave, Paystack, Midtrans, BTCPay, or bring your own
- No fees — zero per-transaction cuts (beyond your payment provider)
- Self-hosted or Cloud — own your data in Supabase, or use the hosted dashboard
- Multi-tenant — single deployment serves multiple apps with isolated data
- KMP + Compose — works on Android, iOS, macOS, JVM, JS, and Wasm
- Device binding — per-device premium verification with conflict resolution
- Paywall UI included —
PayCraftSheet,PayCraftBanner,PayCraftRestoreout of the box - Plugin system — community-contributed providers as separate Maven artifacts
- Web dashboard — subscriber management, revenue analytics, webhook monitoring
- CLI tooling —
npx paycraft initscaffolds everything in minutes - Koin DI — drop in
PayCraftModule, getBillingManagereverywhere
If you prefer to set up without Claude AI, see docs/QUICK_START.md.
# gradle/libs.versions.toml
[versions]
paycraft = "LATEST_VERSION" # check Maven Central badge above
[libraries]
paycraft = { module = "io.github.mobilebytelabs:paycraft", version.ref = "paycraft" }// shared/build.gradle.kts — commonMain ONLY
commonMain.dependencies {
implementation(libs.paycraft)
}PayCraft.configure {
supabase(
url = BuildConfig.SUPABASE_URL,
anonKey = BuildConfig.SUPABASE_ANON_KEY,
)
provider(
StripeProvider(
paymentLinks = mapOf(
"monthly" to BuildConfig.STRIPE_MONTHLY_LINK,
"quarterly" to BuildConfig.STRIPE_QUARTERLY_LINK,
"yearly" to BuildConfig.STRIPE_YEARLY_LINK,
),
customerPortalUrl = BuildConfig.STRIPE_PORTAL_URL,
)
)
plans(
BillingPlan(id = "monthly", name = "Monthly", price = "₹99", interval = "/month"),
BillingPlan(id = "quarterly", name = "Quarterly", price = "₹249", interval = "/3 months"),
BillingPlan(id = "yearly", name = "Yearly", price = "₹799", interval = "/year", isPopular = true),
)
benefits(
BillingBenefit(icon = Icons.Default.Block, text = "Ad-free experience"),
BillingBenefit(icon = Icons.Default.Download, text = "Unlimited downloads"),
)
supportEmail("support@yourdomain.com")
}startKoin {
modules(yourModules, PayCraftModule)
}var showPaywall by remember { mutableStateOf(false) }
var showRestore by remember { mutableStateOf(false) }
PayCraftBanner(
onClick = { showPaywall = true },
onRestoreClick = { showRestore = true },
)
PayCraftSheet(visible = showPaywall, onDismiss = { showPaywall = false })
PayCraftRestore(visible = showRestore, onDismiss = { showRestore = false })val isPremium by billingManager.isPremium.collectAsState()
if (isPremium) PremiumContent() else FreeContent()| Provider | Status | Checkout | Webhook |
|---|---|---|---|
| Stripe | Stable | Payment Links | stripe-webhook |
| Razorpay | Stable | Payment Links | razorpay-webhook |
| Paddle | Ready | Overlay Checkout | paddle-webhook |
| PayPal | Ready | Smart Buttons | paypal-webhook |
| LemonSqueezy | Ready | Hosted Checkout | lemonsqueezy-webhook |
| Flutterwave | Ready | Standard Checkout | flutterwave-webhook |
| Paystack | Ready | Popup Checkout | paystack-webhook |
| Midtrans | Ready | Snap | midtrans-webhook |
| BTCPay | Ready | Invoice | btcpay-webhook |
| Custom | Stable | Any URL | Implement PaymentProvider |
| Component | Description |
|---|---|
PayCraftSheet |
Conditional bottom-sheet paywall |
PayCraftPaywall |
Full-screen paywall |
PayCraftBanner |
Settings row — upgrade CTA or premium status |
PayCraftRestore |
Email-based restore purchases dialog |
PayCraftPremiumGuard |
Gate any composable behind premium |
| Platform | Targets |
|---|---|
| Android | android |
| iOS | iosX64, iosArm64, iosSimulatorArm64 |
| macOS | macosX64, macosArm64 |
| JVM | jvm |
| JavaScript | js (Browser, Node.js) |
| WebAssembly | wasmJs (Browser) |
The PayCraft web dashboard provides tenant admins with:
- Subscriber management — search, filter, export, manual actions
- Revenue analytics — MRR, churn, cohorts, LTV
- Webhook monitoring — delivery status, payload inspection, manual retry
- Provider configuration — manage keys, test connectivity
- Usage & billing — for multi-tenant cloud deployments
Live: paycraft.mobilebytesensei.com
PayCraft uses a multi-tenant architecture where each app gets isolated data within a shared Supabase deployment:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ App A │ │ App B │ │ App C │
│ (SDK) │ │ (SDK) │ │ (SDK) │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
│ X-PayCraft-API-Key header │
└───────────┬───────┴───────────────────┘
▼
┌────────────────┐
│ Supabase │
│ (RPCs + RLS) │◄──── Webhooks (per-tenant URLs)
└────────┬───────┘
▼
┌────────────────┐
│ Dashboard │
│ (Next.js) │
└────────────────┘
Self-hosted users: omit the API key — RPCs default to single-tenant mode.
- Quick Start — manual 15-minute setup guide
- Architecture — how it works under the hood
- Providers — all 9 providers + custom
- Customization — theme, slot API, custom UI
- Security — webhook verification, key management, RLS
- Claude Skills — all available Claude commands
- Documentation site — full Docusaurus site (per-platform guides, concepts)
See paycraft-sample/ for minimal per-platform projects:
android-compose/— Android Compose + Koinios-swiftui/— SwiftUI wrapperdesktop-jvm/— Desktop Composeweb-wasm/— Browser WasmJs
Create custom provider plugins as separate Maven artifacts:
class MyGatewayPlugin : ProviderPlugin {
override val providerId = "my-gateway"
override fun createProvider(config: PluginConfig): PaymentProvider { ... }
}See plugin-template/ for a starter template.
./gradlew jvmTest # run tests
./gradlew :sample-app:run # desktop sample
./gradlew spotlessApply # format
./gradlew detekt # lint./scripts/release.sh
# or: /lib-release in Claude Code| Service | URL |
|---|---|
| Documentation | mobilebytelabs.github.io/PayCraft |
| Dashboard | paycraft.mobilebytesensei.com |
| CLI | npmjs.com/package/paycraft |
| SDK | Maven Central |
See CONTRIBUTING.md.
Copyright 2026 MobileByteLabs
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.