Skip to main content

PayCraft — Supported Providers

The most feature-complete provider integration.

Setup

  1. Create Stripe account at stripe.com
  2. Create Products in Stripe Dashboard → Products
  3. Create Payment Links for each product
  4. Optionally enable Customer Portal (for subscription management)

Configuration

StripeProvider(
paymentLinks = mapOf(
"monthly" to "https://buy.stripe.com/...",
"quarterly" to "https://buy.stripe.com/...",
"yearly" to "https://buy.stripe.com/...",
),
customerPortalUrl = "https://billing.stripe.com/p/login/...", // optional
)

The customerPortalUrl enables "Manage Subscription" which lets users cancel or update payment.

Webhook Events Handled

EventAction
checkout.session.completedActivate subscription
customer.subscription.updatedUpdate plan/status
customer.subscription.deletedCancel subscription
invoice.payment_failedMark as past_due

Email Prefilling

Stripe payment links support ?prefilled_email=email@example.com — PayCraft automatically appends the user's email to the checkout URL, reducing friction.


Razorpay

Well-supported for Indian markets (INR, UPI, cards, net banking).

Setup

  1. Create Razorpay account at razorpay.com
  2. Create Payment Links for each plan
  3. Configure webhook in Dashboard → Webhooks

Configuration

RazorpayProvider(
paymentLinks = mapOf(
"monthly" to "https://rzp.io/l/...",
"quarterly" to "https://rzp.io/l/...",
"yearly" to "https://rzp.io/l/...",
),
dashboardUrl = "https://dashboard.razorpay.com/app/subscriptions", // optional
)

Webhook Events Handled

EventAction
payment.capturedActivate subscription
subscription.activatedActivate subscription
subscription.chargedRenew subscription
subscription.cancelledCancel subscription

Custom Provider

Bring your own payment provider — any provider with payment links and webhooks works.

CustomProvider(
name = "paddle",
checkoutUrlBuilder = { plan, email ->
"https://checkout.paddle.com/product/${plan.id}"
.let { if (email != null) "$it?email=$email" else it }
},
manageUrlBuilder = { email ->
"https://vendors.paddle.com/subscriptions?email=$email"
},
webhookFunctionName = "paddle-webhook",
)

You must also create the webhook Edge Function at server/functions/paddle-webhook/index.ts. See Adding a New Provider.


Adding a New Provider

Run /add-provider in Claude Code, or follow these steps manually:

1. Implement PaymentProvider

Create cmp-paycraft/src/commonMain/kotlin/.../provider/[Name]Provider.kt:

class [Name]Provider(
private val paymentLinks: Map<String, String>,
private val managementUrl: String? = null,
) : PaymentProvider {
override val name = "[name]"
override val webhookFunctionName = "[name]-webhook"

override fun getCheckoutUrl(plan: BillingPlan, email: String?) =
paymentLinks[plan.id] ?: error("No payment link for '${plan.id}'")

override fun getManageUrl(email: String) = managementUrl
}

2. Create Webhook Edge Function

Create server/functions/[name]-webhook/index.ts:

import { serve } from "https://deno.land/std@0.177.0/http/server.ts";
import { handleSubscriptionEvent } from "../_shared/subscription-handler.ts";

serve(async (req) => {
// 1. Verify webhook signature from provider
const secret = Deno.env.get("[NAME]_WEBHOOK_SECRET")!;
// ... verify HMAC ...

const body = await req.json();

// 2. Map provider event to subscription data
if (body.event === "subscription.active") {
await handleSubscriptionEvent({
email: body.subscriber.email,
provider: "[name]",
customerId: body.subscriber.id,
subscriptionId: body.subscription.id,
plan: body.subscription.plan_id,
status: "active",
periodStart: new Date(body.subscription.start_date),
periodEnd: new Date(body.subscription.end_date),
cancelAtPeriodEnd: false,
});
}

return new Response(JSON.stringify({ received: true }), { status: 200 });
});

3. Update PROVIDERS.md

Document the new provider with setup instructions.


Provider Comparison

FeatureStripeRazorpayCustom
Setup time10 min10 min30 min
Claude skill/setup-stripe/setup-razorpay❌ Manual
Payment link prefill✅ EmailDepends
Customer portal✅ Full portal❌ Dashboard onlyDepends
India UPI/cardsDepends
Global coverageLimitedDepends
Free tierDepends