feat: Stripe subscriptions, tier-based permissions, dark mode, and UX improvements
- Fix Stripe SDK v14 compatibility (bracket notation for subscription items) - Fix subscription period dates from subscription items instead of subscription object - Add tier-based permissions (can_accept_payments, etc.) on tenant signup - Add stripe_customer_id field to Tenant model - Add clickable logo on login page (navigates to /) - Add database setup message during signup wizard - Add dark mode support for payment settings and Connect onboarding - Add subscription management endpoints (cancel, reactivate) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -48,6 +48,8 @@ export interface ConnectAccountInfo {
|
||||
export interface PaymentConfig {
|
||||
payment_mode: PaymentMode;
|
||||
tier: string;
|
||||
tier_allows_payments: boolean;
|
||||
stripe_configured: boolean;
|
||||
can_accept_payments: boolean;
|
||||
api_keys: ApiKeysInfo | null;
|
||||
connect_account: ConnectAccountInfo | null;
|
||||
@@ -431,3 +433,114 @@ export const getTransactionDetail = (id: number) =>
|
||||
*/
|
||||
export const refundTransaction = (transactionId: number, request?: RefundRequest) =>
|
||||
apiClient.post<RefundResponse>(`/payments/transactions/${transactionId}/refund/`, request || {});
|
||||
|
||||
// ============================================================================
|
||||
// Subscription Plans & Add-ons
|
||||
// ============================================================================
|
||||
|
||||
export interface SubscriptionPlan {
|
||||
id: number;
|
||||
name: string;
|
||||
description: string;
|
||||
plan_type: 'base' | 'addon';
|
||||
business_tier: string;
|
||||
price_monthly: number | null;
|
||||
price_yearly: number | null;
|
||||
features: string[];
|
||||
permissions: Record<string, boolean>;
|
||||
limits: Record<string, number>;
|
||||
transaction_fee_percent: number;
|
||||
transaction_fee_fixed: number;
|
||||
is_most_popular: boolean;
|
||||
show_price: boolean;
|
||||
stripe_price_id: string;
|
||||
}
|
||||
|
||||
export interface SubscriptionPlansResponse {
|
||||
current_tier: string;
|
||||
current_plan: SubscriptionPlan | null;
|
||||
plans: SubscriptionPlan[];
|
||||
addons: SubscriptionPlan[];
|
||||
}
|
||||
|
||||
export interface CheckoutResponse {
|
||||
checkout_url: string;
|
||||
session_id: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available subscription plans and add-ons.
|
||||
*/
|
||||
export const getSubscriptionPlans = () =>
|
||||
apiClient.get<SubscriptionPlansResponse>('/payments/plans/');
|
||||
|
||||
/**
|
||||
* Create a checkout session for upgrading or purchasing add-ons.
|
||||
*/
|
||||
export const createCheckoutSession = (planId: number, billingPeriod: 'monthly' | 'yearly' = 'monthly') =>
|
||||
apiClient.post<CheckoutResponse>('/payments/checkout/', {
|
||||
plan_id: planId,
|
||||
billing_period: billingPeriod,
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// Active Subscriptions
|
||||
// ============================================================================
|
||||
|
||||
export interface ActiveSubscription {
|
||||
id: string;
|
||||
plan_name: string;
|
||||
plan_type: 'base' | 'addon';
|
||||
status: 'active' | 'past_due' | 'canceled' | 'incomplete' | 'trialing';
|
||||
current_period_start: string;
|
||||
current_period_end: string;
|
||||
cancel_at_period_end: boolean;
|
||||
cancel_at: string | null;
|
||||
canceled_at: string | null;
|
||||
amount: number;
|
||||
amount_display: string;
|
||||
interval: 'month' | 'year';
|
||||
stripe_subscription_id: string;
|
||||
}
|
||||
|
||||
export interface SubscriptionsResponse {
|
||||
subscriptions: ActiveSubscription[];
|
||||
has_active_subscription: boolean;
|
||||
}
|
||||
|
||||
export interface CancelSubscriptionResponse {
|
||||
success: boolean;
|
||||
message: string;
|
||||
cancel_at_period_end: boolean;
|
||||
current_period_end: string;
|
||||
}
|
||||
|
||||
export interface ReactivateSubscriptionResponse {
|
||||
success: boolean;
|
||||
message: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get active subscriptions for the current tenant.
|
||||
*/
|
||||
export const getSubscriptions = () =>
|
||||
apiClient.get<SubscriptionsResponse>('/payments/subscriptions/');
|
||||
|
||||
/**
|
||||
* Cancel a subscription.
|
||||
* @param subscriptionId - Stripe subscription ID
|
||||
* @param immediate - If true, cancel immediately. If false, cancel at period end.
|
||||
*/
|
||||
export const cancelSubscription = (subscriptionId: string, immediate: boolean = false) =>
|
||||
apiClient.post<CancelSubscriptionResponse>('/payments/subscriptions/cancel/', {
|
||||
subscription_id: subscriptionId,
|
||||
immediate,
|
||||
});
|
||||
|
||||
/**
|
||||
* Reactivate a subscription that was set to cancel at period end.
|
||||
*/
|
||||
export const reactivateSubscription = (subscriptionId: string) =>
|
||||
apiClient.post<ReactivateSubscriptionResponse>('/payments/subscriptions/reactivate/', {
|
||||
subscription_id: subscriptionId,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user