@bash-app/bash-common 30.59.4 → 30.59.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,61 @@
1
1
  // Membership tier definitions and constants shared between frontend and backend
2
- import { MembershipBillingInterval, MembershipTier, MembershipTierInfo } from './extendedSchemas';
2
+ import { MembershipTier } from '@prisma/client';
3
3
 
4
- export { MembershipBillingInterval, MembershipTier };
4
+ export { MembershipTier };
5
+
6
+ export type MembershipBillingInterval = 'Monthly' | 'Yearly';
7
+
8
+ export interface MembershipTierInfo {
9
+ tier: MembershipTier;
10
+ name: string;
11
+ monthlyPrice: number;
12
+ yearlyPrice: number;
13
+ features: string[];
14
+ limits: {
15
+ bashEventsPerMonth?: number;
16
+ featuredEventsPerMonth?: number;
17
+ analyticsAccess: boolean;
18
+ vendorDirectoryAccess: boolean;
19
+ prioritySupport: boolean;
20
+ };
21
+ }
22
+
23
+ // API-related interfaces
24
+ export interface UserMembershipStatus {
25
+ currentTier: MembershipTier;
26
+ expiresAt?: string;
27
+ autoRenew: boolean;
28
+ billingInterval?: MembershipBillingInterval;
29
+ featuredEventsUsed: number;
30
+ stripeSubscriptionId?: string;
31
+ canUpgrade: boolean;
32
+ canDowngrade: boolean;
33
+ features: string[];
34
+ subscriptionStatus: string;
35
+ nextBilling?: Date | null;
36
+ isActive: boolean;
37
+ currentPeriodStart?: number;
38
+ currentPeriodEnd?: number;
39
+ cancelAtPeriodEnd?: boolean;
40
+ }
41
+
42
+ export interface CreateMembershipSubscriptionArgs {
43
+ tierName: string;
44
+ interval?: "monthly" | "yearly";
45
+ paymentMethodId?: string;
46
+ userId?: string; // For API usage
47
+ }
48
+
49
+ export interface MembershipSubscriptionResult {
50
+ subscriptionId: string;
51
+ tier: MembershipTier;
52
+ interval: "monthly" | "yearly";
53
+ status: string;
54
+ currentPeriodStart: number;
55
+ currentPeriodEnd: number;
56
+ cancelAtPeriodEnd?: boolean;
57
+ clientSecret?: string;
58
+ }
5
59
 
6
60
  // Pricing in cents (for Stripe)
7
61
  export const MEMBERSHIP_PRICING = {
@@ -1,5 +1,6 @@
1
1
  import { isProduction } from "./apiUtils";
2
- import { BASH_DETAIL_URL } from "../definitions";
2
+ import { BASH_DETAIL_URL, STRIPE_CHECKOUT_TIMEOUT_MS, STRIPE_REDIRECT_TIMEOUT_MS } from "../definitions";
3
+ import { generateBashDetailUrl } from "./slugUtils";
3
4
 
4
5
  const API_HOST = process.env.REACT_APP_API ?? "http://localhost:3500";
5
6
 
@@ -86,3 +87,115 @@ export function urlRemoveQueryParam(url: string, keys: string[]): string {
86
87
 
87
88
  return newUrl;
88
89
  }
90
+
91
+ /**
92
+ * Constructs a full Stripe checkout URL by combining bash event detail URL with checkout path
93
+ * Used by backend to create success/cancel URLs for Stripe checkout sessions
94
+ *
95
+ * @param bashEventId - The bash event ID
96
+ * @param slug - The bash event slug
97
+ * @param checkoutPath - Base checkout path (e.g., CHECKOUT_RETURN_SUCCESS_URL)
98
+ * @param baseUrl - Frontend base URL (e.g., "https://yourdomain.com")
99
+ * @returns Full URL for Stripe checkout session
100
+ *
101
+ * @example
102
+ * buildStripeCheckoutUrl("123", "my-event", "/checkout-success/{CHECKOUT_SESSION_ID}", "https://bash.com")
103
+ * // Returns: "https://bash.com/bash/123-my-event/checkout-success/{CHECKOUT_SESSION_ID}"
104
+ */
105
+ export function buildStripeCheckoutUrl(
106
+ bashEventId: string,
107
+ slug: string,
108
+ checkoutPath: string,
109
+ baseUrl: string
110
+ ): string {
111
+ // Use the existing generateBashDetailUrl function
112
+ const bashDetailPath = generateBashDetailUrl(bashEventId, slug);
113
+ return `${baseUrl}${bashDetailPath}${checkoutPath}`;
114
+ }
115
+
116
+ /**
117
+ * Executes a function with a timeout and retry mechanism
118
+ * Used for Stripe checkout operations that might be slow
119
+ *
120
+ * @param operation - The async operation to execute
121
+ * @param timeoutMs - Timeout in milliseconds
122
+ * @param onTimeout - Callback when timeout occurs
123
+ * @param maxRetries - Maximum number of retries (default: 1)
124
+ * @returns Promise that resolves/rejects based on operation success
125
+ */
126
+ export async function executeWithTimeout<T>(
127
+ operation: () => Promise<T>,
128
+ timeoutMs: number = STRIPE_CHECKOUT_TIMEOUT_MS,
129
+ onTimeout?: (retryCount: number) => void | Promise<void>,
130
+ maxRetries: number = 1
131
+ ): Promise<T> {
132
+ let lastError: Error | undefined;
133
+
134
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
135
+ try {
136
+ const timeoutPromise = new Promise<never>((_, reject) => {
137
+ setTimeout(() => {
138
+ reject(new Error(`Operation timed out after ${timeoutMs}ms`));
139
+ }, timeoutMs);
140
+ });
141
+
142
+ const operationPromise = operation();
143
+
144
+ return await Promise.race([operationPromise, timeoutPromise]);
145
+ } catch (error) {
146
+ lastError = error as Error;
147
+
148
+ if (attempt < maxRetries) {
149
+ console.warn(`Attempt ${attempt + 1} failed, retrying...`, error);
150
+ if (onTimeout) {
151
+ await onTimeout(attempt);
152
+ }
153
+ }
154
+ }
155
+ }
156
+
157
+ throw lastError || new Error('Operation failed after all retries');
158
+ }
159
+
160
+ /**
161
+ * Redirects to Stripe checkout with timeout and fallback
162
+ *
163
+ * @param redirectUrl - The Stripe checkout URL
164
+ * @param timeoutMs - Timeout for redirect
165
+ * @param onTimeout - Callback when redirect times out
166
+ */
167
+ export function redirectToStripeWithTimeout(
168
+ redirectUrl: string,
169
+ timeoutMs: number = STRIPE_REDIRECT_TIMEOUT_MS,
170
+ onTimeout?: () => void
171
+ ): void {
172
+ let redirectCompleted = false;
173
+
174
+ // Set up timeout
175
+ const timeoutId = setTimeout(() => {
176
+ if (!redirectCompleted) {
177
+ console.warn('Stripe redirect timed out, forcing refresh...');
178
+ if (onTimeout) {
179
+ onTimeout();
180
+ } else {
181
+ // Default behavior: refresh the page
182
+ window.location.reload();
183
+ }
184
+ }
185
+ }, timeoutMs);
186
+
187
+ try {
188
+ // Attempt redirect
189
+ window.location.href = redirectUrl;
190
+ redirectCompleted = true;
191
+ clearTimeout(timeoutId);
192
+ } catch (error) {
193
+ console.error('Failed to redirect to Stripe:', error);
194
+ clearTimeout(timeoutId);
195
+ if (onTimeout) {
196
+ onTimeout();
197
+ } else {
198
+ window.location.reload();
199
+ }
200
+ }
201
+ }