@bash-app/bash-common 30.118.1 → 30.120.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 +64 -48
- 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
|
@@ -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
|
-
|