Files
enkly-ai/api/proto/billing/v1/billing.proto
enkly 79ce8dc4c0
Some checks failed
Go CI Pipeline / build_and_test (push) Failing after 22m0s
Init Project
2026-10-30 18:06:55 +01:00

154 lines
4.9 KiB
Protocol Buffer

// Package billing is the Billing & Subscription bounded context: plans,
// Stripe checkout/portal, webhook ingestion, and the per-plan daily
// message quota. System of record for `subscriptions`, `stripe_events`
// and `usage_daily` (see services/billing/migrations/0001_init.sql).
//
// Mirrors internal/handlers/billing.go + internal/billing/*.go +
// internal/db/subscriptions.go.
syntax = "proto3";
package enkly.billing.v1;
option go_package = "enkly/gen/billing/v1;billingv1";
import "google/protobuf/timestamp.proto";
service BillingService {
// GetOrCreateSubscription replaces db.GetOrCreateSubscription: lazily
// inserts the default free-plan row the first time a user's
// subscription is looked up.
rpc GetOrCreateSubscription(GetOrCreateSubscriptionRequest) returns (Subscription);
// GetPlanCatalog replaces billing.Catalog + billing.PlanPriceIDs for
// rendering /subscription — returns every plan and whether its Stripe
// price is currently configured (so an unconfigured plan renders as
// "Unavailable", same as today).
rpc GetPlanCatalog(GetPlanCatalogRequest) returns (GetPlanCatalogResponse);
// CreateCheckoutSession replaces Handlers.CreateCheckout.
rpc CreateCheckoutSession(CreateCheckoutSessionRequest) returns (CreateCheckoutSessionResponse);
// SyncCheckoutSession replaces Handlers.syncCheckoutSession, the
// fallback path SubscriptionSuccess uses when the webhook hasn't
// landed yet.
rpc SyncCheckoutSession(SyncCheckoutSessionRequest) returns (Subscription);
// CreateBillingPortalSession replaces Handlers.BillingPortal.
rpc CreateBillingPortalSession(CreateBillingPortalSessionRequest) returns (CreateBillingPortalSessionResponse);
// IngestStripeWebhook replaces Handlers.StripeWebhook /
// applyStripeEvent. The gateway still terminates the public
// POST /webhooks/stripe route (so the URL Stripe is configured with
// never changes) but forwards the raw body and signature header here
// untouched — signature verification needs STRIPE_WEBHOOK_SECRET,
// which only billing holds.
rpc IngestStripeWebhook(IngestStripeWebhookRequest) returns (IngestStripeWebhookResponse);
// CheckAndIncrementUsage replaces the daily-cap check + db.
// IncrementDailyUsage call in Handlers.Chat. Called by the agent
// service once per chat turn, after the message is accepted and
// before the agent run starts (same ordering as the monolith).
rpc CheckAndIncrementUsage(CheckAndIncrementUsageRequest) returns (CheckAndIncrementUsageResponse);
// GetDailyUsage replaces db.DailyUsage — a read-only lookup (no
// increment) used by the gateway's /subscription page to show
// today's count without miscounting it as a sent message.
rpc GetDailyUsage(GetDailyUsageRequest) returns (GetDailyUsageResponse);
}
enum Plan {
PLAN_UNSPECIFIED = 0;
PLAN_FREE = 1;
PLAN_BASIC = 2;
PLAN_PRO = 3;
PLAN_PREMIUM = 4;
}
message PlanInfo {
Plan plan = 1;
string name = 2;
int32 price_eur = 3;
int32 daily_message_limit = 4; // 0 = unlimited
bool available = 5; // Stripe price configured (always true for free)
}
message Subscription {
int64 user_id = 1;
Plan plan = 2;
string status = 3; // Stripe's subscription status, or "active" for free
google.protobuf.Timestamp current_period_end = 4;
bool cancel_at_period_end = 5;
// has_billing_account reports whether a Stripe customer exists for
// this user (so the gateway knows whether to show "Manage billing"),
// without exposing the actual customer ID. The raw
// stripe_customer_id / stripe_subscription_id / stripe_price_id are
// deliberately NOT exposed here, mirroring models.Subscription's
// json:"-" — they never need to leave billing.
bool has_billing_account = 6;
}
message GetOrCreateSubscriptionRequest {
int64 user_id = 1;
}
message GetPlanCatalogRequest {}
message GetPlanCatalogResponse {
repeated PlanInfo plans = 1;
string publishable_key = 2; // Stripe publishable key, for Checkout.js
}
message CreateCheckoutSessionRequest {
int64 user_id = 1;
string email = 2;
Plan plan = 3;
string success_url = 4;
string cancel_url = 5;
}
message CreateCheckoutSessionResponse {
string checkout_url = 1;
}
message SyncCheckoutSessionRequest {
int64 user_id = 1;
string stripe_session_id = 2;
}
message CreateBillingPortalSessionRequest {
int64 user_id = 1;
string return_url = 2;
}
message CreateBillingPortalSessionResponse {
string portal_url = 1;
}
message IngestStripeWebhookRequest {
bytes payload = 1;
string signature_header = 2;
}
message IngestStripeWebhookResponse {
bool accepted = 1;
}
message CheckAndIncrementUsageRequest {
int64 user_id = 1;
}
message CheckAndIncrementUsageResponse {
bool allowed = 1;
int32 used = 2;
int32 limit = 3; // 0 = unlimited
string plan_name = 4;
}
message GetDailyUsageRequest {
int64 user_id = 1;
}
message GetDailyUsageResponse {
int32 used = 1;
}