@epztickets/common 1.89.0 → 1.91.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.
@@ -1,5 +1 @@
1
- import { CLASS_CATEGORIES } from "./orientation-scheduled-event";
2
- export type ClassCategory = (typeof CLASS_CATEGORIES)[number]["value"];
3
- export declare const CLASS_CATEGORY_VALUES: string[];
4
- export declare const isClassCategory: (v: unknown) => v is ClassCategory;
5
- export declare function inferClassCategory(title?: string | null): ClassCategory | undefined;
1
+ export * from "../shared/class-categories";
@@ -1,32 +1,19 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
2
16
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isClassCategory = exports.CLASS_CATEGORY_VALUES = void 0;
4
- exports.inferClassCategory = inferClassCategory;
5
- const orientation_scheduled_event_1 = require("./orientation-scheduled-event");
6
- exports.CLASS_CATEGORY_VALUES = orientation_scheduled_event_1.CLASS_CATEGORIES.map((c) => c.value);
7
- const isClassCategory = (v) => typeof v === "string" && exports.CLASS_CATEGORY_VALUES.includes(v);
8
- exports.isClassCategory = isClassCategory;
9
- // Best-effort guess of a category from a free-text class name
10
- // ("Salsa basics" → dance, "BJJ open mat" → martial_arts). Used when a
11
- // booking arrives without a category (AI chat agent, older clients)
12
- // and to estimate categories for bookings made before categories
13
- // existed. Returns undefined when nothing matches.
14
- const KEYWORDS = [
15
- ["martial_arts", /\b(karate|judo|jiu[- ]?jitsu|bjj|taekwondo|tae kwon do|kung fu|boxing|kickbox\w*|mma|muay thai|self[- ]defen[cs]e|aikido|capoeira|krav maga|wrestling)\b/],
16
- ["yoga", /\b(yoga|pilates|meditat\w*|mindful\w*|stretch\w*|wellness|breathwork|sound bath|tai chi)\b/],
17
- ["dance", /\b(danc\w*|salsa|bachata|ballet|hip[- ]?hop|tango|merengue|cumbia|contemporary|choreo\w*|tap|jazz funk|k-?pop|twerk|ballroom|swing|flamenco|folklorico|breakdanc\w*|heels)\b/],
18
- ["fitness", /\b(fitness|hiit|boot ?camp|crossfit|zumba|workout|strength|spin(ning)?|cardio|barre|conditioning|circuit|bootcamp|personal training|training session)\b/],
19
- ["singing", /\b(sing\w*|vocal\w*|voice|choir|chorus|karaoke|a cappella)\b/],
20
- ["instrumental", /\b(guitar|piano|drum\w*|violin|viola|cello|bass|band|ukulele|sax\w*|trumpet|flute|clarinet|keyboard|instrument\w*|orchestra|jam session|dj)\b/],
21
- ["theater", /\b(theat(er|re)|acting|actor\w*|drama|improv|audition\w*|scene study|monologue|musical theat(er|re)|stage combat)\b/],
22
- ["workshop", /\b(workshop|lecture|seminar|talk|course|training|meeting|info session|q&a|panel)\b/],
23
- ];
24
- function inferClassCategory(title) {
25
- if (!title)
26
- return undefined;
27
- const t = title.toLowerCase();
28
- for (const [cat, rx] of KEYWORDS)
29
- if (rx.test(t))
30
- return cat;
31
- return undefined;
32
- }
17
+ // Moved to shared/class-categories.ts (re-exported from the package
18
+ // root and from "@epztickets/common/shared").
19
+ __exportStar(require("../shared/class-categories"), exports);
@@ -1,34 +1,5 @@
1
1
  import { Subjects } from "./subjects";
2
2
  export type OrientationStatus = "scheduled" | "approved" | "no_show" | "declined" | "cancelled";
3
- export declare const CLASS_CATEGORIES: readonly [{
4
- readonly value: "singing";
5
- readonly label: "Singing / vocal";
6
- }, {
7
- readonly value: "instrumental";
8
- readonly label: "Instrumental";
9
- }, {
10
- readonly value: "dance";
11
- readonly label: "Dance";
12
- }, {
13
- readonly value: "fitness";
14
- readonly label: "Fitness";
15
- }, {
16
- readonly value: "theater";
17
- readonly label: "Theater / acting";
18
- }, {
19
- readonly value: "yoga";
20
- readonly label: "Yoga / Pilates / wellness";
21
- }, {
22
- readonly value: "martial_arts";
23
- readonly label: "Martial arts";
24
- }, {
25
- readonly value: "workshop";
26
- readonly label: "Workshop / lecture";
27
- }, {
28
- readonly value: "other";
29
- readonly label: "Other";
30
- }];
31
- export declare const classCategoryLabel: (v?: string) => string | undefined;
32
3
  export interface OrientationSnapshot {
33
4
  id: string;
34
5
  ownerUserId: string;
@@ -1,19 +1,2 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.classCategoryLabel = exports.CLASS_CATEGORIES = void 0;
4
- // "Category of class" the renter picks on the orientation form.
5
- // Shared by rooms (validation), notifications (email labels); the
6
- // frontend keeps a matching copy in lib/orientations.ts.
7
- exports.CLASS_CATEGORIES = [
8
- { value: "singing", label: "Singing / vocal" },
9
- { value: "instrumental", label: "Instrumental" },
10
- { value: "dance", label: "Dance" },
11
- { value: "fitness", label: "Fitness" },
12
- { value: "theater", label: "Theater / acting" },
13
- { value: "yoga", label: "Yoga / Pilates / wellness" },
14
- { value: "martial_arts", label: "Martial arts" },
15
- { value: "workshop", label: "Workshop / lecture" },
16
- { value: "other", label: "Other" },
17
- ];
18
- const classCategoryLabel = (v) => { var _a, _b; return (_b = (_a = exports.CLASS_CATEGORIES.find((c) => c.value === v)) === null || _a === void 0 ? void 0 : _a.label) !== null && _b !== void 0 ? _b : v; };
19
- exports.classCategoryLabel = classCategoryLabel;
@@ -8,5 +8,7 @@ export interface OrientationUpdatedEvent {
8
8
  changedBy: "renter" | "owner" | "admin";
9
9
  previousStartTime?: string;
10
10
  previousEndTime?: string;
11
+ accountDeleted?: "renter" | "owner";
12
+ ownerBanned?: boolean;
11
13
  };
12
14
  }
@@ -9,6 +9,7 @@ export interface RenterApprovalChangedEvent {
9
9
  orientationId?: string;
10
10
  roomId?: string;
11
11
  roomTitle?: string;
12
+ banned?: boolean;
12
13
  changedAt: string;
13
14
  };
14
15
  }
package/build/index.d.ts CHANGED
@@ -37,7 +37,6 @@ export * from "./events/orientation-settings-updated-event";
37
37
  export * from "./events/renter-approval-changed-event";
38
38
  export * from "./events/orientation-scheduled-event";
39
39
  export * from "./events/orientation-updated-event";
40
- export * from "./events/class-category";
41
40
  export * from "./events/owner-plan-updated-event";
42
41
  export * from "./events/subscription-payment-events";
43
42
  export * from "./events/order-cancelled-event";
@@ -74,3 +73,4 @@ export * from "./events/user-self-booking-payment-reminder-requested-event";
74
73
  export * from "./events/connect-disconnect-requested-event";
75
74
  export * from "./events/connect-disconnect-approved-event";
76
75
  export * from "./events/connect-disconnect-rejected-event";
76
+ export * from "./shared";
package/build/index.js CHANGED
@@ -57,7 +57,7 @@ __exportStar(require("./events/orientation-settings-updated-event"), exports);
57
57
  __exportStar(require("./events/renter-approval-changed-event"), exports);
58
58
  __exportStar(require("./events/orientation-scheduled-event"), exports);
59
59
  __exportStar(require("./events/orientation-updated-event"), exports);
60
- __exportStar(require("./events/class-category"), exports);
60
+ // class categories now live in shared/ (re-exported below)
61
61
  __exportStar(require("./events/owner-plan-updated-event"), exports);
62
62
  __exportStar(require("./events/subscription-payment-events"), exports);
63
63
  __exportStar(require("./events/order-cancelled-event"), exports);
@@ -94,3 +94,5 @@ __exportStar(require("./events/user-self-booking-payment-reminder-requested-even
94
94
  __exportStar(require("./events/connect-disconnect-requested-event"), exports);
95
95
  __exportStar(require("./events/connect-disconnect-approved-event"), exports);
96
96
  __exportStar(require("./events/connect-disconnect-rejected-event"), exports);
97
+ // Browser-safe helpers (also available as "@epztickets/common/shared")
98
+ __exportStar(require("./shared"), exports);
@@ -0,0 +1,26 @@
1
+ export interface DayHours {
2
+ closed: boolean;
3
+ open?: string;
4
+ close?: string;
5
+ }
6
+ export interface BusinessHours {
7
+ mon: DayHours;
8
+ tue: DayHours;
9
+ wed: DayHours;
10
+ thu: DayHours;
11
+ fri: DayHours;
12
+ sat: DayHours;
13
+ sun: DayHours;
14
+ }
15
+ export interface HoursCheckResult {
16
+ ok: boolean;
17
+ /** Human-readable reason. Only meaningful when ok=false. */
18
+ reason?: string;
19
+ }
20
+ /**
21
+ * Public check. Returns { ok: true } when the booking is allowed
22
+ * (either because there's no hours restriction, OR the window
23
+ * fits inside the day's open/close). Returns { ok: false, reason }
24
+ * with a message safe to surface to the caller when it doesn't.
25
+ */
26
+ export declare function checkBusinessHours(hours: BusinessHours | undefined, startTime: Date, endTime: Date, timezone: string | undefined): HoursCheckResult;
@@ -0,0 +1,118 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkBusinessHours = checkBusinessHours;
4
+ // Server-side counterpart to reservation-frontend/lib/business-
5
+ // hours.ts. Given a Room replica's businessHours + the requested
6
+ // booking window, decides whether the window falls entirely
7
+ // within the room's open hours for the relevant day-of-week.
8
+ //
9
+ // Design decisions:
10
+ // - Backward compat: when businessHours is undefined the guard
11
+ // ALLOWS the booking. Matches the client's "no gate when
12
+ // hours aren't configured" behavior — an owner who hasn't
13
+ // set hours yet shouldn't accidentally lock renters out.
14
+ // - Timezone: businessHours are stored in the room's local
15
+ // time (e.g. "09:00" = 9am). At validation time we need the
16
+ // day-of-week and HH:MM of the requested start/end in that
17
+ // same local timezone. We use the ORDER'S buyer timezone
18
+ // as a proxy — in almost every case the buyer and the room
19
+ // are in the same physical location, so their timezones
20
+ // match. If they differ, worst-case the guard evaluates in
21
+ // the wrong timezone. That's a rare edge case we can revisit
22
+ // once we have real cross-timezone data.
23
+ // - Multi-day ranges: rejected. V1 doesn't support overnight
24
+ // bookings that cross a business-hour boundary. If an owner
25
+ // eventually wants "open Fri 8pm to Sat 2am," we'll add
26
+ // dedicated support then.
27
+ const JS_DAY_TO_KEY = [
28
+ "sun", "mon", "tue", "wed", "thu", "fri", "sat",
29
+ ];
30
+ /**
31
+ * Extract { dayOfWeek, hh:mm } for a Date in a given IANA
32
+ * timezone, using Intl.DateTimeFormat. Falls back to UTC when
33
+ * the timezone is invalid or missing.
34
+ */
35
+ function partsIn(date, timezone) {
36
+ var _a, _b, _c, _d;
37
+ const opts = Object.assign({ weekday: "short", hour: "2-digit", minute: "2-digit", hour12: false }, (timezone ? { timeZone: timezone } : { timeZone: "UTC" }));
38
+ let formatted;
39
+ try {
40
+ formatted = new Intl.DateTimeFormat("en-US", opts).formatToParts(date);
41
+ }
42
+ catch (_e) {
43
+ formatted = new Intl.DateTimeFormat("en-US", Object.assign(Object.assign({}, opts), { timeZone: "UTC" })).formatToParts(date);
44
+ }
45
+ const map = {};
46
+ for (const p of formatted)
47
+ map[p.type] = p.value;
48
+ const weekdayShort = (_a = map.weekday) !== null && _a !== void 0 ? _a : "Sun";
49
+ // Intl gives "Sun" ... "Sat" — map to our internal keys.
50
+ const dayKey = (_b = {
51
+ Sun: "sun", Mon: "mon", Tue: "tue", Wed: "wed",
52
+ Thu: "thu", Fri: "fri", Sat: "sat",
53
+ }[weekdayShort]) !== null && _b !== void 0 ? _b : "sun";
54
+ const hh = map.hour === "24" ? "00" : ((_c = map.hour) !== null && _c !== void 0 ? _c : "00");
55
+ const mm = (_d = map.minute) !== null && _d !== void 0 ? _d : "00";
56
+ return { dayKey: dayKey, hhmm: `${hh}:${mm}` };
57
+ }
58
+ /**
59
+ * Public check. Returns { ok: true } when the booking is allowed
60
+ * (either because there's no hours restriction, OR the window
61
+ * fits inside the day's open/close). Returns { ok: false, reason }
62
+ * with a message safe to surface to the caller when it doesn't.
63
+ */
64
+ function checkBusinessHours(hours, startTime, endTime, timezone) {
65
+ // No hours configured — no gate. Same behavior as the
66
+ // frontend picker when businessHours is undefined.
67
+ if (!hours)
68
+ return { ok: true };
69
+ // Multi-day guard. This is intentionally strict — an overnight
70
+ // booking that would legitimately span two days (e.g. 8pm-2am)
71
+ // is out of scope for the current business-hours model.
72
+ const startParts = partsIn(startTime, timezone);
73
+ const endParts = partsIn(endTime, timezone);
74
+ if (startParts.dayKey !== endParts.dayKey) {
75
+ return {
76
+ ok: false,
77
+ reason: "This room does not accept bookings that cross midnight. Please book within a single day.",
78
+ };
79
+ }
80
+ const day = hours[startParts.dayKey];
81
+ if (!day)
82
+ return { ok: true };
83
+ if (day.closed) {
84
+ return {
85
+ ok: false,
86
+ reason: `This room is closed on ${dayLabel(startParts.dayKey)}. Please pick a day when it's open.`,
87
+ };
88
+ }
89
+ if (!day.open || !day.close) {
90
+ // Malformed row — treat as no gate rather than reject a
91
+ // legitimate booking on bad server-side data.
92
+ return { ok: true };
93
+ }
94
+ // String comparison works for zero-padded 24-hour HH:MM.
95
+ if (startParts.hhmm < day.open || endParts.hhmm > day.close) {
96
+ return {
97
+ ok: false,
98
+ reason: `This room is open ${to12h(day.open)} to ${to12h(day.close)} on ${dayLabel(startParts.dayKey)}. Please pick a time within the open window.`,
99
+ };
100
+ }
101
+ return { ok: true };
102
+ }
103
+ // Small formatting helpers used only in the error message. Kept
104
+ // local so the guard has no external formatting dependency.
105
+ function dayLabel(key) {
106
+ return {
107
+ mon: "Monday", tue: "Tuesday", wed: "Wednesday", thu: "Thursday",
108
+ fri: "Friday", sat: "Saturday", sun: "Sunday",
109
+ }[key];
110
+ }
111
+ function to12h(hhmm) {
112
+ const [h, m] = hhmm.split(":").map(Number);
113
+ if (Number.isNaN(h) || Number.isNaN(m))
114
+ return hhmm;
115
+ const period = h < 12 ? "AM" : "PM";
116
+ const displayH = h === 0 ? 12 : h > 12 ? h - 12 : h;
117
+ return `${displayH}:${String(m).padStart(2, "0")} ${period}`;
118
+ }
@@ -0,0 +1,33 @@
1
+ export declare const CLASS_CATEGORIES: readonly [{
2
+ readonly value: "singing";
3
+ readonly label: "Singing / vocal";
4
+ }, {
5
+ readonly value: "instrumental";
6
+ readonly label: "Instrumental";
7
+ }, {
8
+ readonly value: "dance";
9
+ readonly label: "Dance";
10
+ }, {
11
+ readonly value: "fitness";
12
+ readonly label: "Fitness";
13
+ }, {
14
+ readonly value: "theater";
15
+ readonly label: "Theater / acting";
16
+ }, {
17
+ readonly value: "yoga";
18
+ readonly label: "Yoga / Pilates / wellness";
19
+ }, {
20
+ readonly value: "martial_arts";
21
+ readonly label: "Martial arts";
22
+ }, {
23
+ readonly value: "workshop";
24
+ readonly label: "Workshop / lecture";
25
+ }, {
26
+ readonly value: "other";
27
+ readonly label: "Other";
28
+ }];
29
+ export declare const classCategoryLabel: (v?: string) => string | undefined;
30
+ export type ClassCategory = (typeof CLASS_CATEGORIES)[number]["value"];
31
+ export declare const CLASS_CATEGORY_VALUES: string[];
32
+ export declare const isClassCategory: (v: unknown) => v is ClassCategory;
33
+ export declare function inferClassCategory(title?: string | null): ClassCategory | undefined;
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ // "Category of class" — single source for services AND the frontend
3
+ // (via @epztickets/common/shared). Used by the orientation form, the
4
+ // Reserve modal, orders validation, and metrics trends.
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.isClassCategory = exports.CLASS_CATEGORY_VALUES = exports.classCategoryLabel = exports.CLASS_CATEGORIES = void 0;
7
+ exports.inferClassCategory = inferClassCategory;
8
+ exports.CLASS_CATEGORIES = [
9
+ { value: "singing", label: "Singing / vocal" },
10
+ { value: "instrumental", label: "Instrumental" },
11
+ { value: "dance", label: "Dance" },
12
+ { value: "fitness", label: "Fitness" },
13
+ { value: "theater", label: "Theater / acting" },
14
+ { value: "yoga", label: "Yoga / Pilates / wellness" },
15
+ { value: "martial_arts", label: "Martial arts" },
16
+ { value: "workshop", label: "Workshop / lecture" },
17
+ { value: "other", label: "Other" },
18
+ ];
19
+ const classCategoryLabel = (v) => { var _a, _b; return (_b = (_a = exports.CLASS_CATEGORIES.find((c) => c.value === v)) === null || _a === void 0 ? void 0 : _a.label) !== null && _b !== void 0 ? _b : v; };
20
+ exports.classCategoryLabel = classCategoryLabel;
21
+ exports.CLASS_CATEGORY_VALUES = exports.CLASS_CATEGORIES.map((c) => c.value);
22
+ const isClassCategory = (v) => typeof v === "string" && exports.CLASS_CATEGORY_VALUES.includes(v);
23
+ exports.isClassCategory = isClassCategory;
24
+ // Best-effort guess of a category from a free-text class name
25
+ // ("Salsa basics" → dance, "BJJ open mat" → martial_arts). Used when a
26
+ // booking arrives without a category (AI chat agent, older clients)
27
+ // and to estimate categories for bookings made before categories
28
+ // existed. Returns undefined when nothing matches.
29
+ const KEYWORDS = [
30
+ ["martial_arts", /\b(karate|judo|jiu[- ]?jitsu|bjj|taekwondo|tae kwon do|kung fu|boxing|kickbox\w*|mma|muay thai|self[- ]defen[cs]e|aikido|capoeira|krav maga|wrestling)\b/],
31
+ ["yoga", /\b(yoga|pilates|meditat\w*|mindful\w*|stretch\w*|wellness|breathwork|sound bath|tai chi)\b/],
32
+ ["dance", /\b(danc\w*|salsa|bachata|ballet|hip[- ]?hop|tango|merengue|cumbia|contemporary|choreo\w*|tap|jazz funk|k-?pop|twerk|ballroom|swing|flamenco|folklorico|breakdanc\w*|heels)\b/],
33
+ ["fitness", /\b(fitness|hiit|boot ?camp|crossfit|zumba|workout|strength|spin(ning)?|cardio|barre|conditioning|circuit|bootcamp|personal training|training session)\b/],
34
+ ["singing", /\b(sing\w*|vocal\w*|voice|choir|chorus|karaoke|a cappella)\b/],
35
+ ["instrumental", /\b(guitar|piano|drum\w*|violin|viola|cello|bass|band|ukulele|sax\w*|trumpet|flute|clarinet|keyboard|instrument\w*|orchestra|jam session|dj)\b/],
36
+ ["theater", /\b(theat(er|re)|acting|actor\w*|drama|improv|audition\w*|scene study|monologue|musical theat(er|re)|stage combat)\b/],
37
+ ["workshop", /\b(workshop|lecture|seminar|talk|course|training|meeting|info session|q&a|panel)\b/],
38
+ ];
39
+ function inferClassCategory(title) {
40
+ if (!title)
41
+ return undefined;
42
+ const t = title.toLowerCase();
43
+ for (const [cat, rx] of KEYWORDS)
44
+ if (rx.test(t))
45
+ return cat;
46
+ return undefined;
47
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./stripe-fee";
2
+ export * from "./business-hours";
3
+ export * from "./class-categories";
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ // Browser-safe, dependency-free helpers shared by the services AND
18
+ // the Next.js frontend. Import from "@epztickets/common/shared" in the
19
+ // frontend (the package root pulls in server-only code: express,
20
+ // nats, pino…). Services can keep importing from the root, which
21
+ // re-exports everything here.
22
+ __exportStar(require("./stripe-fee"), exports);
23
+ __exportStar(require("./business-hours"), exports);
24
+ __exportStar(require("./class-categories"), exports);
@@ -0,0 +1,8 @@
1
+ export declare const STRIPE_FEE_PCT = 0.029;
2
+ export declare const STRIPE_FEE_FIXED_CENTS = 30;
3
+ /** Total to charge (cents) for a base price (cents). 0 stays 0. */
4
+ export declare function totalWithStripeFeeCents(baseCents: number): number;
5
+ /** Dollars in → dollars out, rounded to cents. */
6
+ export declare function totalWithStripeFee(baseDollars: number): number;
7
+ /** The processing-fee portion (dollars) of a base price (dollars). */
8
+ export declare function stripeFeeFor(baseDollars: number): number;
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ // Stripe processing-fee pass-through — THE single source of truth for
3
+ // what a renter pays at checkout. Used by:
4
+ // - payments (new-stripe-session): the amount actually charged
5
+ // - frontend ReserveModal: the price shown before booking
6
+ // - frontend order page: fallback total when Stripe's real charged
7
+ // amount isn't recorded yet
8
+ // If these ever disagreed, renters would see one price and pay another.
9
+ //
10
+ // Stripe takes 2.9% + 30¢ of the TOTAL charge. We gross up the room
11
+ // price so that after Stripe's cut the platform still receives the
12
+ // full base amount: total = ceil((base + 30) / (1 - 0.029)).
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.STRIPE_FEE_FIXED_CENTS = exports.STRIPE_FEE_PCT = void 0;
15
+ exports.totalWithStripeFeeCents = totalWithStripeFeeCents;
16
+ exports.totalWithStripeFee = totalWithStripeFee;
17
+ exports.stripeFeeFor = stripeFeeFor;
18
+ exports.STRIPE_FEE_PCT = 0.029;
19
+ exports.STRIPE_FEE_FIXED_CENTS = 30;
20
+ /** Total to charge (cents) for a base price (cents). 0 stays 0. */
21
+ function totalWithStripeFeeCents(baseCents) {
22
+ if (baseCents <= 0)
23
+ return 0;
24
+ return Math.ceil((baseCents + exports.STRIPE_FEE_FIXED_CENTS) / (1 - exports.STRIPE_FEE_PCT));
25
+ }
26
+ /** Dollars in → dollars out, rounded to cents. */
27
+ function totalWithStripeFee(baseDollars) {
28
+ return totalWithStripeFeeCents(Math.round(baseDollars * 100)) / 100;
29
+ }
30
+ /** The processing-fee portion (dollars) of a base price (dollars). */
31
+ function stripeFeeFor(baseDollars) {
32
+ return Math.round((totalWithStripeFee(baseDollars) - baseDollars) * 100) / 100;
33
+ }
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "@epztickets/common",
3
- "version": "1.89.0",
3
+ "version": "1.91.0",
4
4
  "main": "./build/index.js",
5
5
  "types": "./build/index.d.ts",
6
6
  "files": [
7
- "build/**/*"
7
+ "build/**/*",
8
+ "shared/package.json"
8
9
  ],
9
10
  "scripts": {
10
11
  "clean": "rimraf build",
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "@epztickets/common/shared",
3
+ "private": true,
4
+ "main": "../build/shared/index.js",
5
+ "types": "../build/shared/index.d.ts"
6
+ }