Files
textbee/web/config/api.ts
isra el 164124d616 feat(billing): update existing Polar subscription on plan change instead of creating a new checkout
A paid subscriber upgrading (pro -> scale) or downgrading (scale -> pro)
previously got a brand-new Polar checkout, ending up with two live Polar
subscriptions and double billing. Now an active paid subscription is
updated in place via Polar's subscription update API.

- store polarSubscriptionId/polarCustomerId/cancelAtPeriodEnd on
  subscriptions (recovered via externalCustomerId for legacy records)
- POST /billing/checkout returns a planChange preview for paid users;
  new POST /billing/change-plan executes it (uncancels a scheduled
  cancellation first, org-default proration, idempotent with webhooks)
- allow monthly<->yearly interval switches; keep ALREADY_ON_PLAN only
  for same plan + same interval; block custom plans (CONTACT_BILLING)
- map Polar 402/403/409 errors to actionable messages; run plan-change
  detection before cached checkout-session reuse
- checkout page shows a confirmation screen before applying the change;
  account page shows a success toast

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 20:09:00 +03:00

53 lines
2.0 KiB
TypeScript

export const ApiEndpoints = {
auth: {
login: () => '/auth/login',
register: () => '/auth/register',
signInWithGoogle: () => '/auth/google-login',
updateProfile: () => '/auth/update-profile',
changePassword: () => '/auth/change-password',
whoAmI: () => '/auth/who-am-i',
updateOnboarding: () => '/auth/onboarding',
sendEmailVerificationEmail: () => '/auth/send-email-verification-email',
verifyEmail: () => '/auth/verify-email',
requestPasswordReset: () => '/auth/request-password-reset',
resetPassword: () => '/auth/reset-password',
generateApiKey: () => '/auth/api-keys',
listApiKeys: (status?: 'active' | 'revoked' | 'all') =>
status
? `/auth/api-keys?status=${encodeURIComponent(status)}`
: '/auth/api-keys',
revokeApiKey: (id: string) => `/auth/api-keys/${id}/revoke`,
renameApiKey: (id: string) => `/auth/api-keys/${id}/rename`,
deleteApiKey: (id: string) => `/auth/api-keys/${id}`,
},
gateway: {
listDevices: () => '/gateway/devices',
deleteDevice: (id: string) => `/gateway/devices/${id}`,
sendSMS: (id: string) => `/gateway/devices/${id}/send-sms`,
sendBulkSMS: (id: string) => `/gateway/devices/${id}/send-bulk-sms`,
getReceivedSMS: (id: string) => `/gateway/devices/${id}/get-received-sms`,
getMessages: (id: string) => `/gateway/devices/${id}/messages`,
getWebhooks: () => '/webhooks',
getWebhookNotifications: () => '/webhooks/notifications',
createWebhook: () => '/webhooks',
updateWebhook: (id: string) => `/webhooks/${id}`,
deleteWebhook: (id: string) => `/webhooks/${id}`,
getStats: () => '/gateway/stats',
},
billing: {
currentSubscription: () => '/billing/current-subscription',
checkout: () => '/billing/checkout',
changePlan: () => '/billing/change-plan',
plans: () => '/billing/plans',
},
support: {
customerSupport: () => '/support/customer-support',
requestAccountDeletion: () => '/support/request-account-deletion',
},
}