@bash-app/bash-common 30.118.0 → 30.119.0
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.
- package/dist/definitions.d.ts +3 -0
- package/dist/definitions.d.ts.map +1 -1
- package/dist/definitions.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/membershipDefinitions.d.ts +9 -5
- package/dist/membershipDefinitions.d.ts.map +1 -1
- package/dist/membershipDefinitions.js +28 -24
- package/dist/membershipDefinitions.js.map +1 -1
- package/dist/utils/bashPointsPaymentUtils.d.ts +55 -0
- package/dist/utils/bashPointsPaymentUtils.d.ts.map +1 -0
- package/dist/utils/bashPointsPaymentUtils.js +79 -0
- package/dist/utils/bashPointsPaymentUtils.js.map +1 -0
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/ticketListUtils.d.ts +2 -1
- package/dist/utils/ticketListUtils.d.ts.map +1 -1
- package/dist/utils/ticketListUtils.js +61 -11
- package/dist/utils/ticketListUtils.js.map +1 -1
- package/package.json +1 -1
- package/prisma/migrations/add_bashcash_pricing_to_ticket_tier.sql +15 -0
- package/prisma/migrations/add_bashpoints_purchase_tracking.sql +33 -0
- package/prisma/migrations/add_pricing_type_enum.sql +88 -0
- package/prisma/migrations/diagnostic_bashcash_columns.sql +157 -0
- package/prisma/migrations/fix_bashcash_referral_code_schema_mismatch.sql +81 -0
- package/prisma/migrations/rename_bashcash_to_bashpoints.sql +183 -0
- package/prisma/migrations/rename_bashcredits_to_bashpoints.sql +96 -0
- package/prisma/schema.prisma +432 -317
- package/src/definitions.ts +3 -0
- package/src/index.ts +1 -1
- package/src/membershipDefinitions.ts +29 -25
- package/src/utils/bashPointsPaymentUtils.ts +107 -0
- package/src/utils/index.ts +1 -1
- package/src/utils/ticketListUtils.ts +64 -10
- package/src/utils/bashCashPaymentUtils.ts +0 -146
package/src/definitions.ts
CHANGED
|
@@ -354,6 +354,7 @@ export type FilterFields = {
|
|
|
354
354
|
specialOffers: string[];
|
|
355
355
|
eventFormat: string[];
|
|
356
356
|
hasOpenTasks: boolean; // Boolean filter for events with open volunteer tasks
|
|
357
|
+
acceptsBashPoints: boolean; // Boolean filter for events with BashPoints-priced tickets
|
|
357
358
|
};
|
|
358
359
|
|
|
359
360
|
export type ApiEntityApproval = {
|
|
@@ -362,6 +363,8 @@ export type ApiEntityApproval = {
|
|
|
362
363
|
|
|
363
364
|
export type TicketTierWherePriceIsAString = Omit<TicketTier, "price"> & {
|
|
364
365
|
price: string;
|
|
366
|
+
pricingType?: string; // 'USD' or 'BASHCASH'
|
|
367
|
+
priceBashPoints?: string; // Price in BashPoints credits (string for form input)
|
|
365
368
|
} & { cannotDelete: boolean };
|
|
366
369
|
|
|
367
370
|
export type ApiServiceAddonParams = {
|
package/src/index.ts
CHANGED
|
@@ -9,7 +9,7 @@ export * from "./utils/addressUtils";
|
|
|
9
9
|
export * from "./utils/apiUtils";
|
|
10
10
|
export * from "./utils/arrayUtils";
|
|
11
11
|
export * from "./utils/awsS3Utils";
|
|
12
|
-
export * from "./utils/
|
|
12
|
+
export * from "./utils/bashPointsPaymentUtils";
|
|
13
13
|
export * from "./utils/contentFilterUtils";
|
|
14
14
|
export * from "./utils/dateTimeUtils";
|
|
15
15
|
export * from "./utils/discountEngine/bestPriceResolver";
|
|
@@ -90,7 +90,7 @@ export const MEMBERSHIP_PRICING = {
|
|
|
90
90
|
Legend: { monthly: 99900, yearly: 1000000 }, // $999/month, $10,000/year
|
|
91
91
|
};
|
|
92
92
|
|
|
93
|
-
// Helper: Get monthly price in cents for a tier (for
|
|
93
|
+
// Helper: Get monthly price in cents for a tier (for BashPoints calculations)
|
|
94
94
|
export const MEMBERSHIP_MONTHLY_PRICE_CENTS: Record<MembershipTier, number> = {
|
|
95
95
|
Basic: MEMBERSHIP_PRICING.Basic.monthly,
|
|
96
96
|
Creator: MEMBERSHIP_PRICING.Creator.monthly,
|
|
@@ -104,59 +104,63 @@ export const MEMBERSHIP_TIER_HIERARCHY: MembershipTier[] = [
|
|
|
104
104
|
];
|
|
105
105
|
|
|
106
106
|
// ============================================================================
|
|
107
|
-
//
|
|
107
|
+
// BASHPOINTS REWARDS SYSTEM
|
|
108
108
|
// ============================================================================
|
|
109
109
|
|
|
110
110
|
/**
|
|
111
|
-
*
|
|
112
|
-
*
|
|
111
|
+
* BashPoints rewards per membership tier
|
|
112
|
+
* Points are loyalty rewards with floating market value (like airline miles)
|
|
113
113
|
*/
|
|
114
|
-
export const
|
|
115
|
-
monthlyCredits: number; //
|
|
114
|
+
export const BASHPOINTS_REWARDS: Record<MembershipTier, {
|
|
115
|
+
monthlyCredits: number; // Points earned per month
|
|
116
116
|
ticketCashbackRate: number; // Cashback % on ticket purchases
|
|
117
117
|
serviceCashbackRate: number; // Cashback % on service bookings
|
|
118
118
|
}> = {
|
|
119
119
|
Basic: {
|
|
120
120
|
monthlyCredits: 0,
|
|
121
|
-
ticketCashbackRate: 0,
|
|
122
|
-
serviceCashbackRate: 0,
|
|
121
|
+
ticketCashbackRate: 0.01, // 1% cashback
|
|
122
|
+
serviceCashbackRate: 0.01, // 1% cashback
|
|
123
123
|
},
|
|
124
124
|
Creator: {
|
|
125
|
-
monthlyCredits: 10000, // 10,000
|
|
126
|
-
ticketCashbackRate: 0.
|
|
127
|
-
serviceCashbackRate: 0.
|
|
125
|
+
monthlyCredits: 10000, // 10,000 BashPoints/month
|
|
126
|
+
ticketCashbackRate: 0.03, // 3% cashback
|
|
127
|
+
serviceCashbackRate: 0.02, // 2% cashback
|
|
128
128
|
},
|
|
129
129
|
Pro: {
|
|
130
|
-
monthlyCredits: 40000, // 40,000
|
|
130
|
+
monthlyCredits: 40000, // 40,000 BashPoints/month
|
|
131
131
|
ticketCashbackRate: 0.05, // 5% cashback
|
|
132
132
|
serviceCashbackRate: 0.03, // 3% cashback
|
|
133
133
|
},
|
|
134
134
|
Elite: {
|
|
135
|
-
monthlyCredits: 120000, // 120,000
|
|
136
|
-
ticketCashbackRate: 0.
|
|
137
|
-
serviceCashbackRate: 0.
|
|
135
|
+
monthlyCredits: 120000, // 120,000 BashPoints/month
|
|
136
|
+
ticketCashbackRate: 0.07, // 7% cashback
|
|
137
|
+
serviceCashbackRate: 0.04, // 4% cashback
|
|
138
138
|
},
|
|
139
139
|
Legend: {
|
|
140
|
-
monthlyCredits: 250000, // 250,000
|
|
141
|
-
ticketCashbackRate: 0.
|
|
142
|
-
serviceCashbackRate: 0.
|
|
140
|
+
monthlyCredits: 250000, // 250,000 BashPoints/month
|
|
141
|
+
ticketCashbackRate: 0.10, // 10% cashback
|
|
142
|
+
serviceCashbackRate: 0.05, // 5% cashback
|
|
143
143
|
},
|
|
144
144
|
} as const;
|
|
145
145
|
|
|
146
146
|
/**
|
|
147
|
-
*
|
|
147
|
+
* BashPoints system rules and limits
|
|
148
|
+
*
|
|
149
|
+
* IMPORTANT: BashPoints is a LOYALTY POINTS SYSTEM with floating market value.
|
|
150
|
+
* Credits do NOT have a fixed dollar conversion rate.
|
|
151
|
+
* Hosts/providers set their own BashPoints prices for tickets and services.
|
|
148
152
|
*/
|
|
149
|
-
export const
|
|
153
|
+
export const BASHPOINTS_CONFIG = {
|
|
150
154
|
// Referral bonuses
|
|
151
|
-
referralBonusRate: 0.10, // 10% of referee's first purchase
|
|
152
|
-
maxReferralCredits: 10000, //
|
|
155
|
+
referralBonusRate: 0.10, // 10% of referee's first USD purchase (in credits)
|
|
156
|
+
maxReferralCredits: 10000, // 10,000 credit cap on referral bonuses
|
|
153
157
|
|
|
154
158
|
// Credit lifecycle
|
|
155
159
|
creditExpirationMonths: 12, // Credits expire after 12 months
|
|
156
160
|
|
|
157
|
-
// Redemption rules
|
|
158
|
-
minimumRedemption: 100, //
|
|
159
|
-
membershipRedemptionCap: 0.50, //
|
|
161
|
+
// Redemption rules (deprecated for floating value system)
|
|
162
|
+
minimumRedemption: 100, // Legacy: was used for fixed-value redemptions
|
|
163
|
+
membershipRedemptionCap: 0.50, // NOT USED: Can't pay memberships with BashPoints (no fixed value)
|
|
160
164
|
} as const;
|
|
161
165
|
|
|
162
166
|
// ============================================================================
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BashPoints Utilities
|
|
3
|
+
*
|
|
4
|
+
* BashPoints is a loyalty rewards program where:
|
|
5
|
+
* 1. Users EARN BashPoints as cashback on USD purchases (tickets & services)
|
|
6
|
+
* 2. Users SPEND BashPoints ONLY on BashPoints-priced tickets (floating market value set by hosts)
|
|
7
|
+
*
|
|
8
|
+
* BashPoints have NO fixed dollar value - hosts set prices freely (e.g., "500 points for entry")
|
|
9
|
+
* This is a peer-to-peer points exchange system, NOT a stored value currency.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Format BashPoints for display
|
|
14
|
+
* @param points BashPoints amount
|
|
15
|
+
* @returns Formatted string (e.g., "28,750 points")
|
|
16
|
+
*/
|
|
17
|
+
export function formatBashPoints(points: number): string {
|
|
18
|
+
return `${points.toLocaleString('en-US')} point${points === 1 ? '' : 's'}`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Calculate BashPoints cashback earned on a USD purchase
|
|
23
|
+
* Uses tiered rates based on membership level (handled by membership tier definitions)
|
|
24
|
+
*
|
|
25
|
+
* @param amountCents Amount paid in USD cents
|
|
26
|
+
* @param cashbackRate Cashback rate (e.g., 0.05 for 5%)
|
|
27
|
+
* @returns BashPoints earned (1 cent = 1 point for earning calculation)
|
|
28
|
+
*/
|
|
29
|
+
export function calculateBashPointsCashback(
|
|
30
|
+
amountCents: number,
|
|
31
|
+
cashbackRate: number
|
|
32
|
+
): number {
|
|
33
|
+
// Award 1 point per cent spent, multiplied by cashback rate
|
|
34
|
+
// Example: $10.00 (1000 cents) at 5% = 50 points
|
|
35
|
+
return Math.floor(amountCents * cashbackRate);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// =====================================================================
|
|
39
|
+
// DEPRECATED FUNCTIONS - Service Booking Payments Removed
|
|
40
|
+
// BashPoints can ONLY be spent on BashPoints-priced tickets, not service bookings
|
|
41
|
+
// =====================================================================
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @deprecated BashPoints cannot be used for service bookings
|
|
45
|
+
* Only for BashPoints-priced tickets with floating market value
|
|
46
|
+
*/
|
|
47
|
+
export function centsToBashPoints(cents: number): number {
|
|
48
|
+
console.warn('DEPRECATED: centsToBashPoints - BashPoints have no fixed dollar value');
|
|
49
|
+
return cents; // Keep for backward compatibility but log warning
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @deprecated BashPoints cannot be used for service bookings
|
|
54
|
+
* Only for BashPoints-priced tickets with floating market value
|
|
55
|
+
*/
|
|
56
|
+
export function bashPointsToCents(credits: number): number {
|
|
57
|
+
console.warn('DEPRECATED: bashPointsToCents - BashPoints have no fixed dollar value');
|
|
58
|
+
return credits; // Keep for backward compatibility but log warning
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* @deprecated BashPoints cannot be used for service bookings
|
|
63
|
+
*/
|
|
64
|
+
export function calculateMaxBashPointsForBooking(
|
|
65
|
+
totalCents: number,
|
|
66
|
+
userBalance: number
|
|
67
|
+
): number {
|
|
68
|
+
console.warn('DEPRECATED: calculateMaxBashPointsForBooking - BashPoints not supported for service bookings');
|
|
69
|
+
return 0;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @deprecated BashPoints cannot be used for service bookings
|
|
74
|
+
*/
|
|
75
|
+
export function calculateSplitPayment(
|
|
76
|
+
totalCents: number,
|
|
77
|
+
bashPointsToUse: number
|
|
78
|
+
): {
|
|
79
|
+
bashPointsCents: number;
|
|
80
|
+
stripeCents: number;
|
|
81
|
+
bashPointsCredits: number;
|
|
82
|
+
} {
|
|
83
|
+
console.warn('DEPRECATED: calculateSplitPayment - BashPoints not supported for service bookings');
|
|
84
|
+
return {
|
|
85
|
+
bashPointsCents: 0,
|
|
86
|
+
stripeCents: totalCents,
|
|
87
|
+
bashPointsCredits: 0,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* @deprecated BashPoints cannot be used for service bookings
|
|
93
|
+
*/
|
|
94
|
+
export function validateBashPointsPayment(
|
|
95
|
+
bashPointsToUse: number,
|
|
96
|
+
userBalance: number,
|
|
97
|
+
totalCents: number
|
|
98
|
+
): {
|
|
99
|
+
valid: boolean;
|
|
100
|
+
errorMessage?: string;
|
|
101
|
+
} {
|
|
102
|
+
return {
|
|
103
|
+
valid: false,
|
|
104
|
+
errorMessage: "BashPoints can only be used for BashPoints-priced tickets, not service bookings. Please pay with a credit card.",
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
package/src/utils/index.ts
CHANGED
|
@@ -3,7 +3,7 @@ export * from './apiUtils';
|
|
|
3
3
|
export * from './arrayUtils';
|
|
4
4
|
export * from './awsS3Utils';
|
|
5
5
|
export * from './badgeUtils';
|
|
6
|
-
export * from './
|
|
6
|
+
export * from './bashPointsPaymentUtils';
|
|
7
7
|
export * from './blog/blogDbUtils';
|
|
8
8
|
export * from './blogUtils';
|
|
9
9
|
export * from './contentFilterUtils';
|
|
@@ -13,8 +13,50 @@ import {
|
|
|
13
13
|
LUXON_DATETIME_FORMAT_ISO_LIKE,
|
|
14
14
|
} from "./luxonUtils";
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Encodes a string to Base64 URL-safe format
|
|
18
|
+
* Replaces + with -, / with _, and removes = padding
|
|
19
|
+
*/
|
|
20
|
+
function encodeBase64UrlSafe(str: string): string {
|
|
21
|
+
if (typeof window !== "undefined") {
|
|
22
|
+
// Browser environment
|
|
23
|
+
return btoa(str)
|
|
24
|
+
.replace(/\+/g, "-")
|
|
25
|
+
.replace(/\//g, "_")
|
|
26
|
+
.replace(/=/g, "");
|
|
27
|
+
} else {
|
|
28
|
+
// Node environment
|
|
29
|
+
return Buffer.from(str)
|
|
30
|
+
.toString("base64")
|
|
31
|
+
.replace(/\+/g, "-")
|
|
32
|
+
.replace(/\//g, "_")
|
|
33
|
+
.replace(/=/g, "");
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Decodes a Base64 URL-safe string
|
|
39
|
+
* Replaces - with +, _ with /, and adds back = padding
|
|
40
|
+
*/
|
|
41
|
+
function decodeBase64UrlSafe(str: string): string {
|
|
42
|
+
// Add back padding
|
|
43
|
+
let base64 = str.replace(/-/g, "+").replace(/_/g, "/");
|
|
44
|
+
while (base64.length % 4) {
|
|
45
|
+
base64 += "=";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (typeof window !== "undefined") {
|
|
49
|
+
// Browser environment
|
|
50
|
+
return atob(base64);
|
|
51
|
+
} else {
|
|
52
|
+
// Node environment
|
|
53
|
+
return Buffer.from(base64, "base64").toString();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
16
57
|
/**
|
|
17
58
|
* Returns A string structured like: [ticketTierId]__[numberOfTickets];;[date]~~[numberOfTickets];;[date]~~,...
|
|
59
|
+
* The returned string is Base64 URL-safe encoded to prevent routing issues
|
|
18
60
|
* @param ticketList A map where the key is the ticketTierId
|
|
19
61
|
*/
|
|
20
62
|
export function ticketListToString(
|
|
@@ -23,51 +65,63 @@ export function ticketListToString(
|
|
|
23
65
|
const ticketListArr: string[] = [];
|
|
24
66
|
|
|
25
67
|
for (const [ticketTierId, ticketListArgs] of ticketList.entries()) {
|
|
26
|
-
let ticketsExist = true;
|
|
27
68
|
const ticketArgs: string[] = [
|
|
28
69
|
`${ticketTierId}${URL_PARAMS_TICKET_TIER_ID_NUMBER_OF_TICKETS_DATE_DELIM}`,
|
|
29
70
|
];
|
|
71
|
+
let hasAnyTickets = false; // Track if we added ANY valid tickets for this tier
|
|
72
|
+
|
|
30
73
|
for (const ticketNumAndDates of ticketListArgs) {
|
|
31
74
|
if (ticketNumAndDates.numberOfTickets > 0) {
|
|
32
|
-
|
|
75
|
+
hasAnyTickets = true;
|
|
33
76
|
|
|
34
|
-
// Ensure ticketDateTime is in ISO format
|
|
77
|
+
// Ensure ticketDateTime is in ISO format (URL-safe)
|
|
35
78
|
let dateTimeStr = ticketNumAndDates.ticketDateTime;
|
|
36
79
|
if (dateTimeStr) {
|
|
37
80
|
// If it's already a string, try to parse it as DateTime and convert to ISO
|
|
38
81
|
const dateTime = dateTimeFromDate(dateTimeStr);
|
|
39
82
|
if (dateTime.isValid) {
|
|
40
|
-
dateTimeStr =
|
|
83
|
+
dateTimeStr = dateTime.toISO() || dateTimeStr;
|
|
41
84
|
}
|
|
42
85
|
}
|
|
43
86
|
|
|
44
87
|
ticketArgs.push(
|
|
45
88
|
`${ticketNumAndDates.numberOfTickets}${URL_PARAMS_TICKETS_DATE_DELIM}${dateTimeStr}`
|
|
46
89
|
);
|
|
47
|
-
} else {
|
|
48
|
-
ticketsExist = false;
|
|
49
90
|
}
|
|
50
91
|
}
|
|
51
|
-
|
|
92
|
+
|
|
93
|
+
// Only include this tier if we found at least one ticket > 0
|
|
94
|
+
if (hasAnyTickets) {
|
|
52
95
|
ticketListArr.push(
|
|
53
96
|
ticketArgs.join(URL_PARAMS_NUMBER_OF_TICKETS_TICKETS_DATE_DELIM)
|
|
54
97
|
);
|
|
55
98
|
}
|
|
56
99
|
}
|
|
57
|
-
|
|
100
|
+
const plainString = ticketListArr.join(URL_PARAMS_TICKET_LIST_DELIM);
|
|
101
|
+
return encodeBase64UrlSafe(plainString);
|
|
58
102
|
}
|
|
59
103
|
|
|
60
104
|
/**
|
|
61
105
|
* Returns map where the key is the ticketTierId
|
|
62
|
-
* @param ticketListStr A string
|
|
106
|
+
* @param ticketListStr A Base64 URL-safe encoded string that decodes to: [ticketTierId]__[numberOfTickets];;[date]~~[numberOfTickets];;[date]~~...,...
|
|
63
107
|
*/
|
|
64
108
|
export function ticketListStrToTicketList(
|
|
65
109
|
ticketListStr: string
|
|
66
110
|
): Map<string, NumberOfTicketsForDate[]> {
|
|
67
111
|
const ticketList: Map<string, NumberOfTicketsForDate[]> = new Map();
|
|
68
112
|
|
|
113
|
+
// Decode the Base64 URL-safe string first
|
|
114
|
+
let decodedStr: string;
|
|
115
|
+
try {
|
|
116
|
+
decodedStr = decodeBase64UrlSafe(ticketListStr);
|
|
117
|
+
} catch (error) {
|
|
118
|
+
console.error("Failed to decode ticketListStr:", error);
|
|
119
|
+
// If decoding fails, try using the string as-is (for backward compatibility)
|
|
120
|
+
decodedStr = ticketListStr;
|
|
121
|
+
}
|
|
122
|
+
|
|
69
123
|
// [ticketTierId]__~~[numberOfTickets];;[date]~~[numberOfTickets];;[date]~~...,...
|
|
70
|
-
const ticketListArgs =
|
|
124
|
+
const ticketListArgs = decodedStr.split(URL_PARAMS_TICKET_LIST_DELIM);
|
|
71
125
|
|
|
72
126
|
ticketListArgs.forEach((tierIdAndTicketNumbersAndDates: string): void => {
|
|
73
127
|
const ticketNumAndDatesArr: NumberOfTicketsForDate[] = [];
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* BashCash Payment Utilities
|
|
3
|
-
* Handles calculations and conversions for BashCash payment system
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
// 1 BashCash credit = 1 cent = $0.01
|
|
7
|
-
export const BASHCASH_TO_CENTS_RATE = 1;
|
|
8
|
-
export const CENTS_TO_BASHCASH_RATE = 1;
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Convert USD cents to BashCash credits
|
|
12
|
-
* @param cents Amount in cents (e.g., 28750 = $287.50)
|
|
13
|
-
* @returns BashCash credits (e.g., 28750 credits)
|
|
14
|
-
*/
|
|
15
|
-
export function centsToBashCash(cents: number): number {
|
|
16
|
-
return Math.floor(cents * CENTS_TO_BASHCASH_RATE);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Convert BashCash credits to USD cents
|
|
21
|
-
* @param credits BashCash credits (e.g., 28750 credits)
|
|
22
|
-
* @returns Amount in cents (e.g., 28750 = $287.50)
|
|
23
|
-
*/
|
|
24
|
-
export function bashCashToCents(credits: number): number {
|
|
25
|
-
return Math.floor(credits * BASHCASH_TO_CENTS_RATE);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Format BashCash credits for display
|
|
30
|
-
* @param credits BashCash credits
|
|
31
|
-
* @returns Formatted string (e.g., "28,750 credits")
|
|
32
|
-
*/
|
|
33
|
-
export function formatBashCash(credits: number): string {
|
|
34
|
-
return `${credits.toLocaleString('en-US')} credit${credits === 1 ? '' : 's'}`;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Format BashCash credits with USD equivalent
|
|
39
|
-
* @param credits BashCash credits
|
|
40
|
-
* @returns Formatted string (e.g., "28,750 credits ($287.50)")
|
|
41
|
-
*/
|
|
42
|
-
export function formatBashCashWithUSD(credits: number): string {
|
|
43
|
-
const cents = bashCashToCents(credits);
|
|
44
|
-
const dollars = (cents / 100).toFixed(2);
|
|
45
|
-
return `${formatBashCash(credits)} ($${dollars})`;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Calculate maximum BashCash that can be used for a booking
|
|
50
|
-
* @param totalCents Total booking cost in cents
|
|
51
|
-
* @param userBalance User's BashCash balance in credits
|
|
52
|
-
* @returns Maximum BashCash credits that can be applied
|
|
53
|
-
*/
|
|
54
|
-
export function calculateMaxBashCashForBooking(
|
|
55
|
-
totalCents: number,
|
|
56
|
-
userBalance: number
|
|
57
|
-
): number {
|
|
58
|
-
// No cap on BashCash usage for service bookings (can pay 100% if they have enough)
|
|
59
|
-
const maxPossible = centsToBashCash(totalCents);
|
|
60
|
-
return Math.min(maxPossible, userBalance);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Calculate split payment amounts
|
|
65
|
-
* @param totalCents Total booking cost in cents
|
|
66
|
-
* @param bashCashToUse BashCash credits user wants to apply
|
|
67
|
-
* @returns Object with bashCashCents and stripeCents
|
|
68
|
-
*/
|
|
69
|
-
export function calculateSplitPayment(
|
|
70
|
-
totalCents: number,
|
|
71
|
-
bashCashToUse: number
|
|
72
|
-
): {
|
|
73
|
-
bashCashCents: number;
|
|
74
|
-
stripeCents: number;
|
|
75
|
-
bashCashCredits: number;
|
|
76
|
-
} {
|
|
77
|
-
const bashCashCents = bashCashToCents(bashCashToUse);
|
|
78
|
-
const stripeCents = Math.max(0, totalCents - bashCashCents);
|
|
79
|
-
|
|
80
|
-
return {
|
|
81
|
-
bashCashCents,
|
|
82
|
-
stripeCents,
|
|
83
|
-
bashCashCredits: bashCashToUse,
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Validate BashCash payment
|
|
89
|
-
* @param bashCashToUse BashCash credits to use
|
|
90
|
-
* @param userBalance User's current balance
|
|
91
|
-
* @param totalCents Total booking cost
|
|
92
|
-
* @returns Validation result
|
|
93
|
-
*/
|
|
94
|
-
export function validateBashCashPayment(
|
|
95
|
-
bashCashToUse: number,
|
|
96
|
-
userBalance: number,
|
|
97
|
-
totalCents: number
|
|
98
|
-
): {
|
|
99
|
-
valid: boolean;
|
|
100
|
-
errorMessage?: string;
|
|
101
|
-
} {
|
|
102
|
-
if (bashCashToUse < 0) {
|
|
103
|
-
return {
|
|
104
|
-
valid: false,
|
|
105
|
-
errorMessage: "BashCash amount cannot be negative",
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
if (bashCashToUse > userBalance) {
|
|
110
|
-
return {
|
|
111
|
-
valid: false,
|
|
112
|
-
errorMessage: `Insufficient BashCash balance. You have ${formatBashCash(userBalance)}, but tried to use ${formatBashCash(bashCashToUse)}.`,
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const bashCashCents = bashCashToCents(bashCashToUse);
|
|
117
|
-
if (bashCashCents > totalCents) {
|
|
118
|
-
return {
|
|
119
|
-
valid: false,
|
|
120
|
-
errorMessage: `BashCash amount ($${(bashCashCents / 100).toFixed(2)}) exceeds booking total ($${(totalCents / 100).toFixed(2)}).`,
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return { valid: true };
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Calculate BashCash cashback for a service booking (3%)
|
|
129
|
-
* @param amountCents Amount paid in cents
|
|
130
|
-
* @returns BashCash credits earned as cashback
|
|
131
|
-
*/
|
|
132
|
-
export function calculateServiceBookingCashback(amountCents: number): number {
|
|
133
|
-
const CASHBACK_RATE = 0.03; // 3% for service bookings
|
|
134
|
-
return Math.floor((amountCents * CASHBACK_RATE));
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Calculate BashCash cashback for a ticket purchase (5%)
|
|
139
|
-
* @param amountCents Amount paid in cents
|
|
140
|
-
* @returns BashCash credits earned as cashback
|
|
141
|
-
*/
|
|
142
|
-
export function calculateTicketCashback(amountCents: number): number {
|
|
143
|
-
const CASHBACK_RATE = 0.05; // 5% for ticket purchases
|
|
144
|
-
return Math.floor((amountCents * CASHBACK_RATE));
|
|
145
|
-
}
|
|
146
|
-
|