services/payment/pod-payment/README.md
Provider-agnostic payment service supporting multiple subscription providers (Polar.sh, Stripe, etc.).
Client (UI)
↓ POST /api/v1/subscriptions/:workspace/subscribe
Payment Service → Accounts Service (caller auth)
↓ Create checkout via provider API
Payment Provider (Polar.sh, Stripe, etc.)
↓ Webhook → POST /api/v1/webhooks/:provider
Payment Service → Accounts Service (upsert subscription)
Request:
POST /api/v1/subscriptions/:workspace/subscribe
Authorization: Bearer {token}
{
"type": "tier" | "support",
"plan": "common" | "rare" | "epic" | "legendary",
"customerEmail"?: string,
"customerName"?: string
}
Response:
{
"checkoutId": "checkout_abc123",
"checkoutUrl": "https://checkout.polar.sh/session/abc123"
}
User is redirected to checkoutUrl and completes payment with the provider.
After payment, user is redirected to the success URL with these query parameters:
FRONT_URL/workbench/setting/setting/billing?payment=success&checkout_id={CHECKOUT_ID}
| Endpoint | Method | Purpose |
|---|---|---|
/api/v1/subscriptions/:workspace/subscribe | POST | Create subscription checkout |
/api/v1/subscriptions/:subscriptionId | GET | Get subscription details (admin only) |
/api/v1/subscriptions/:subscriptionId/cancel | POST | Cancel subscription |
/api/v1/webhooks/:provider | POST | Receive webhook events from payment provider |
To run the payment service locally:
rushx run-local
Make sure you have all required environment variables configured (see Environment Configuration below).
| Variable | Description | Example |
|---|---|---|
PORT | Port to listen on | 3040 |
ACCOUNTS_URL | Accounts service URL | http://huly.local:3000 |
FRONT_URL | Frontend URL for redirects | https://huly.local:8087 |
To enable Polar.sh as the payment provider, set:
| Variable | Description | Example |
|---|---|---|
POLAR_ACCESS_TOKEN | Polar.sh API access token | polar_... |
POLAR_WEBHOOK_SECRET | Webhook signature secret | whsec_... |
POLAR_SUBSCRIPTION_PLANS | Plan to product IDs mapping | common@tier:prod_1a,prod_1b;rare@tier:prod_2;epic@tier:prod_3;legendary@tier:prod_4 |
Format: {plan}@{type}:{productIds};... where productIds are comma-separated uuids of products in your Polar.sh account.
Webhook endpoint is registered at /api/v1/webhooks/polar.
To enable Stripe as the payment provider, set:
| Variable | Description | Example |
|---|---|---|
STRIPE_API_KEY | Stripe API secret key | sk_live_... or sk_test_... |
STRIPE_WEBHOOK_SECRET | Webhook signing secret | whsec_... |
STRIPE_SUBSCRIPTION_PLANS | Plan to price ID mapping | common@tier:price_1a;rare@tier:price_2;epic@tier:price_3;legendary@tier:price_4 |
Format: {plan}@{type}:{priceId};... where priceId is the Stripe Price ID (e.g., price_1abc123).
Note: Stripe uses Price IDs (not Product IDs like Polar.sh). You can find Price IDs in your Stripe Dashboard under Products.
Webhook endpoint is registered at /api/v1/webhooks/stripe.
Important: Only one provider can be active at a time. If Polar.sh is configured, Stripe will not be initialized. Remove Polar.sh environment variables to use Stripe instead.
To support additional providers (Lemonsqueezy, etc.):
PaymentProvider interfacePaymentProviderFactory.create()registerWebhookEndpoints()