@burdenoff/microfe-billing 2026.910.7 → 2026.910.8
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.
|
@@ -5,54 +5,54 @@ import { useCallback as o, useEffect as s, useMemo as c, useState as l } from "r
|
|
|
5
5
|
//#region src/billing/modules/checkout/hooks/useCheckout.ts
|
|
6
6
|
var ee = 100;
|
|
7
7
|
function u(u = {}) {
|
|
8
|
-
let { currentUser: d } = e(), { defaultCurrency: f = "USD", defaultSaveCard: p = !1, upgradeFromSubscriptionId: m, downgradeFromSubscriptionId: h, defaultSubscriptionId: g } = u, [_, v] = l([]), [y, te] = l(null), [b,
|
|
8
|
+
let { currentUser: d } = e(), { defaultCurrency: f = "USD", defaultSaveCard: p = !1, upgradeFromSubscriptionId: m, downgradeFromSubscriptionId: h, defaultSubscriptionId: g } = u, [_, v] = l([]), [y, te] = l(null), [b, x] = l("AUTO"), [S, C] = l(f), [w, T] = l(""), [E, D] = l(0), [ne, O] = l("PERCENTAGE"), [k, A] = l(!1), [j, M] = l(0), [N, P] = l("idle"), [re, F] = l(null), [I, L] = l(0), [ie, R] = l(0), [ae, z] = l("NONE"), [B, V] = l(null), [H, oe] = l(p), [U, se] = l(!1), [W, ce] = l(null), [G, le] = l(g ?? null), [ue] = n(), [de] = a(), [fe] = r(), [K, { data: pe, loading: me }] = t(), [he] = i(), q = c(() => _[0]?.currency ?? "USD", [_]), J = c(() => _.reduce((e, t) => e + t.price * t.quantity, 0), [_]), Y = c(() => _.reduce((e, t) => e + (t.priceUSD ?? t.price) * t.quantity, 0), [_]), X = c(() => E ? Math.min(E, J) : 0, [J, E]), Z = c(() => {
|
|
9
9
|
let e = J - X;
|
|
10
|
-
return (
|
|
10
|
+
return (k ? Math.max(0, e - j) : e) + I;
|
|
11
11
|
}, [
|
|
12
12
|
J,
|
|
13
13
|
X,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
]), Q = y?.creditAmount ?? 0, ge = c(() => Q >= Y * ee, [Q, Y]), _e = c(() => !_.some((e) => e.type === "CREDITS") && Q > 0, [_, Q]), ve = pe?.convertCurrency?.amount ?? null, ye = pe?.convertCurrency?.exchangeRate ?? 1, be = ve ?? Z, xe =
|
|
14
|
+
k,
|
|
15
|
+
j,
|
|
16
|
+
I
|
|
17
|
+
]), Q = y?.creditAmount ?? 0, ge = c(() => Q >= Y * ee, [Q, Y]), _e = c(() => !_.some((e) => e.type === "CREDITS") && Q > 0, [_, Q]), ve = pe?.convertCurrency?.amount ?? null, ye = pe?.convertCurrency?.exchangeRate ?? 1, be = ve ?? Z, xe = N === "loading" || me, Se = N === "processing";
|
|
18
18
|
s(() => {
|
|
19
|
-
if (b !== "CREDITS" && _.length !== 0 && !_.every((e) => e.currency ===
|
|
20
|
-
if (
|
|
19
|
+
if (b !== "CREDITS" && _.length !== 0 && !_.every((e) => e.currency === S || e.type === "CREDITS") && Z > 0 && q !== S) {
|
|
20
|
+
if (S === "INR" && I === 0 && J > 0) return;
|
|
21
21
|
K({ variables: {
|
|
22
22
|
amount: Z,
|
|
23
|
-
toCurrency:
|
|
23
|
+
toCurrency: S,
|
|
24
24
|
fromCurrency: q
|
|
25
25
|
} });
|
|
26
26
|
}
|
|
27
27
|
}, [
|
|
28
28
|
Z,
|
|
29
|
-
|
|
29
|
+
S,
|
|
30
30
|
b,
|
|
31
31
|
K,
|
|
32
|
-
|
|
32
|
+
I,
|
|
33
33
|
J,
|
|
34
34
|
_,
|
|
35
35
|
q
|
|
36
36
|
]), s(() => {
|
|
37
37
|
if (!y || J <= 0) {
|
|
38
|
-
|
|
38
|
+
L(0), R(0), z("NONE");
|
|
39
39
|
return;
|
|
40
40
|
}
|
|
41
41
|
if (b === "CREDITS") {
|
|
42
|
-
|
|
42
|
+
L(0), R(0), z("NONE");
|
|
43
43
|
return;
|
|
44
44
|
}
|
|
45
|
-
if (
|
|
46
|
-
|
|
45
|
+
if (S !== "INR") {
|
|
46
|
+
L(0), R(0), z("NONE");
|
|
47
47
|
return;
|
|
48
48
|
}
|
|
49
49
|
let e = J - X, t = .18;
|
|
50
|
-
|
|
50
|
+
L(e * t), R(t), z("GST");
|
|
51
51
|
}, [
|
|
52
52
|
y,
|
|
53
53
|
J,
|
|
54
54
|
X,
|
|
55
|
-
|
|
55
|
+
S,
|
|
56
56
|
b
|
|
57
57
|
]);
|
|
58
58
|
let Ce = o((e) => {
|
|
@@ -92,9 +92,9 @@ function u(u = {}) {
|
|
|
92
92
|
}, []), Ee = o(() => {
|
|
93
93
|
v([]);
|
|
94
94
|
}, []), De = o((e) => {
|
|
95
|
-
te(e),
|
|
95
|
+
te(e), A(!1), M(0), x((e) => e === "CREDITS" ? "AUTO" : e);
|
|
96
96
|
}, []), Oe = o((e) => {
|
|
97
|
-
|
|
97
|
+
C(e);
|
|
98
98
|
let t = _.every((t) => t.currency === e || t.type === "CREDITS");
|
|
99
99
|
Z > 0 && q !== e && !t && K({ variables: {
|
|
100
100
|
amount: Z,
|
|
@@ -111,25 +111,25 @@ function u(u = {}) {
|
|
|
111
111
|
items: _,
|
|
112
112
|
billingAccount: y,
|
|
113
113
|
selectedProvider: b,
|
|
114
|
-
selectedCurrency:
|
|
115
|
-
promoCode:
|
|
116
|
-
promoDiscount:
|
|
117
|
-
promoDiscountType:
|
|
118
|
-
useCredits:
|
|
119
|
-
creditsToUse:
|
|
120
|
-
checkoutState:
|
|
121
|
-
error:
|
|
122
|
-
selectedPaymentMethodId:
|
|
123
|
-
saveCard:
|
|
124
|
-
autoRenew:
|
|
125
|
-
autoRenewPaymentMethodId:
|
|
114
|
+
selectedCurrency: S,
|
|
115
|
+
promoCode: w,
|
|
116
|
+
promoDiscount: E,
|
|
117
|
+
promoDiscountType: ne,
|
|
118
|
+
useCredits: k,
|
|
119
|
+
creditsToUse: j,
|
|
120
|
+
checkoutState: N,
|
|
121
|
+
error: re,
|
|
122
|
+
selectedPaymentMethodId: B,
|
|
123
|
+
saveCard: H,
|
|
124
|
+
autoRenew: U,
|
|
125
|
+
autoRenewPaymentMethodId: W,
|
|
126
126
|
selectedSubscriptionId: G,
|
|
127
127
|
subtotal: J,
|
|
128
128
|
subtotalUSD: Y,
|
|
129
129
|
discountAmount: X,
|
|
130
|
-
taxAmount:
|
|
131
|
-
taxRate:
|
|
132
|
-
taxType:
|
|
130
|
+
taxAmount: I,
|
|
131
|
+
taxRate: ie,
|
|
132
|
+
taxType: ae,
|
|
133
133
|
total: Z,
|
|
134
134
|
totalInSelectedCurrency: be,
|
|
135
135
|
availableCredits: Q,
|
|
@@ -147,12 +147,12 @@ function u(u = {}) {
|
|
|
147
147
|
clearItems: Ee,
|
|
148
148
|
setBillingAccount: De,
|
|
149
149
|
setPaymentProvider: o((e) => {
|
|
150
|
-
|
|
151
|
-
}, [
|
|
150
|
+
x(e), e === "RAZORPAY" && S !== "INR" && C("INR");
|
|
151
|
+
}, [S]),
|
|
152
152
|
setCurrency: Oe,
|
|
153
|
-
setPromoCode:
|
|
153
|
+
setPromoCode: T,
|
|
154
154
|
validatePromoCode: o(async () => {
|
|
155
|
-
if (!
|
|
155
|
+
if (!w.trim()) return {
|
|
156
156
|
valid: !1,
|
|
157
157
|
code: "",
|
|
158
158
|
discountType: "PERCENTAGE",
|
|
@@ -160,48 +160,48 @@ function u(u = {}) {
|
|
|
160
160
|
error: "Please enter a promo code"
|
|
161
161
|
};
|
|
162
162
|
try {
|
|
163
|
-
|
|
163
|
+
P("loading");
|
|
164
164
|
let { data: e, error: t } = await he({ variables: { input: {
|
|
165
|
-
code:
|
|
165
|
+
code: w.trim().toUpperCase(),
|
|
166
166
|
userId: d?.id ?? "",
|
|
167
167
|
userEmail: d?.email,
|
|
168
168
|
amount: J,
|
|
169
|
-
currency:
|
|
169
|
+
currency: S
|
|
170
170
|
} } });
|
|
171
|
-
if (t || !e?.validateCoupon) return
|
|
171
|
+
if (t || !e?.validateCoupon) return D(0), P("idle"), {
|
|
172
172
|
valid: !1,
|
|
173
|
-
code:
|
|
173
|
+
code: w,
|
|
174
174
|
discountType: "PERCENTAGE",
|
|
175
175
|
discountValue: 0,
|
|
176
176
|
error: "Failed to validate coupon"
|
|
177
177
|
};
|
|
178
178
|
let n = e.validateCoupon;
|
|
179
|
-
if (!n.valid) return
|
|
179
|
+
if (!n.valid) return D(0), P("idle"), {
|
|
180
180
|
valid: !1,
|
|
181
|
-
code:
|
|
181
|
+
code: w,
|
|
182
182
|
discountType: "PERCENTAGE",
|
|
183
183
|
discountValue: 0,
|
|
184
184
|
error: n.error || "Invalid coupon code"
|
|
185
185
|
};
|
|
186
186
|
let r = n.coupon;
|
|
187
|
-
if (!r) return
|
|
187
|
+
if (!r) return D(0), P("idle"), {
|
|
188
188
|
valid: !1,
|
|
189
|
-
code:
|
|
189
|
+
code: w,
|
|
190
190
|
discountType: "PERCENTAGE",
|
|
191
191
|
discountValue: 0,
|
|
192
192
|
error: "Coupon data not found"
|
|
193
193
|
};
|
|
194
194
|
let i = n.discountAmount || 0, a = r.type === "FIXED_AMOUNT" ? "FIXED" : "PERCENTAGE";
|
|
195
|
-
return
|
|
195
|
+
return D(i), O(a), P("idle"), {
|
|
196
196
|
valid: !0,
|
|
197
197
|
code: r.code,
|
|
198
198
|
discountType: a,
|
|
199
199
|
discountValue: i
|
|
200
200
|
};
|
|
201
201
|
} catch {
|
|
202
|
-
return
|
|
202
|
+
return D(0), P("idle"), {
|
|
203
203
|
valid: !1,
|
|
204
|
-
code:
|
|
204
|
+
code: w,
|
|
205
205
|
discountType: "PERCENTAGE",
|
|
206
206
|
discountValue: 0,
|
|
207
207
|
error: "Failed to validate coupon"
|
|
@@ -210,27 +210,27 @@ function u(u = {}) {
|
|
|
210
210
|
}, [
|
|
211
211
|
d?.email,
|
|
212
212
|
d?.id,
|
|
213
|
-
|
|
214
|
-
|
|
213
|
+
w,
|
|
214
|
+
S,
|
|
215
215
|
J,
|
|
216
216
|
he
|
|
217
217
|
]),
|
|
218
218
|
clearPromoCode: o(() => {
|
|
219
|
-
|
|
219
|
+
T(""), D(0), O("PERCENTAGE");
|
|
220
220
|
}, []),
|
|
221
|
-
setUseCredits:
|
|
221
|
+
setUseCredits: A,
|
|
222
222
|
setCreditsToUse: o((e) => {
|
|
223
223
|
let t = Math.min(e, Q, J - X);
|
|
224
|
-
|
|
224
|
+
M(Math.max(0, t));
|
|
225
225
|
}, [
|
|
226
226
|
Q,
|
|
227
227
|
J,
|
|
228
228
|
X
|
|
229
229
|
]),
|
|
230
|
-
setSelectedPaymentMethodId:
|
|
231
|
-
setSaveCard:
|
|
232
|
-
setAutoRenew:
|
|
233
|
-
setAutoRenewPaymentMethodId:
|
|
230
|
+
setSelectedPaymentMethodId: V,
|
|
231
|
+
setSaveCard: oe,
|
|
232
|
+
setAutoRenew: se,
|
|
233
|
+
setAutoRenewPaymentMethodId: ce,
|
|
234
234
|
setSelectedSubscriptionId: le,
|
|
235
235
|
initiatePayment: o(async () => {
|
|
236
236
|
if (!y) return {
|
|
@@ -242,7 +242,7 @@ function u(u = {}) {
|
|
|
242
242
|
total: 0,
|
|
243
243
|
taxType: "NONE",
|
|
244
244
|
jurisdiction: "UNKNOWN",
|
|
245
|
-
currency:
|
|
245
|
+
currency: S,
|
|
246
246
|
error: "Please select a billing account"
|
|
247
247
|
};
|
|
248
248
|
if (_.length === 0) return {
|
|
@@ -254,24 +254,24 @@ function u(u = {}) {
|
|
|
254
254
|
total: 0,
|
|
255
255
|
taxType: "NONE",
|
|
256
256
|
jurisdiction: "UNKNOWN",
|
|
257
|
-
currency:
|
|
257
|
+
currency: S,
|
|
258
258
|
error: "No items in checkout"
|
|
259
259
|
};
|
|
260
260
|
try {
|
|
261
|
-
|
|
261
|
+
P("processing"), F(null);
|
|
262
262
|
let e = _[0], t = e.type, n = _.find((e) => e.metadata?.storeOrderId)?.metadata?.storeOrderId, r = _.find((e) => e.metadata?.billingOrderId)?.metadata?.billingOrderId || void 0;
|
|
263
263
|
n && (t = "ORDER");
|
|
264
264
|
let i = J - X, a = b;
|
|
265
|
-
if (a === "AUTO" && (a =
|
|
265
|
+
if (a === "AUTO" && (a = S === "INR" ? "RAZORPAY" : "STRIPE"), t === "ORDER" && !r && n && y) {
|
|
266
266
|
let { data: e } = await de({ variables: {
|
|
267
267
|
storeOrderId: n,
|
|
268
268
|
billingAccountId: y.id,
|
|
269
|
-
currency:
|
|
269
|
+
currency: S
|
|
270
270
|
} }), t = e?.ensureStoreOrderBillingOrder;
|
|
271
271
|
if (t?.success && t.billingOrderId) r = t.billingOrderId;
|
|
272
272
|
else {
|
|
273
273
|
let e = t?.error ?? "Failed to create billing order";
|
|
274
|
-
return
|
|
274
|
+
return P("error"), F(e), {
|
|
275
275
|
success: !1,
|
|
276
276
|
provider: a,
|
|
277
277
|
subtotal: i,
|
|
@@ -280,7 +280,7 @@ function u(u = {}) {
|
|
|
280
280
|
total: i,
|
|
281
281
|
taxType: "NONE",
|
|
282
282
|
jurisdiction: "UNKNOWN",
|
|
283
|
-
currency:
|
|
283
|
+
currency: S,
|
|
284
284
|
error: e
|
|
285
285
|
};
|
|
286
286
|
}
|
|
@@ -299,7 +299,7 @@ function u(u = {}) {
|
|
|
299
299
|
subscriptionId: e.length > 0 ? G : void 0,
|
|
300
300
|
seatCount: t?.metadata?.seatCount
|
|
301
301
|
} } }), r = n?.spendCredits;
|
|
302
|
-
return r?.creditTransactionID ? (
|
|
302
|
+
return r?.creditTransactionID ? (P("success"), {
|
|
303
303
|
success: !0,
|
|
304
304
|
provider: "CREDITS",
|
|
305
305
|
subtotal: i,
|
|
@@ -308,9 +308,9 @@ function u(u = {}) {
|
|
|
308
308
|
total: i,
|
|
309
309
|
taxType: "NONE",
|
|
310
310
|
jurisdiction: "UNKNOWN",
|
|
311
|
-
currency:
|
|
311
|
+
currency: S,
|
|
312
312
|
orderId: r.creditTransactionID
|
|
313
|
-
}) : (
|
|
313
|
+
}) : (P("error"), F("Failed to process credit payment"), {
|
|
314
314
|
success: !1,
|
|
315
315
|
provider: "CREDITS",
|
|
316
316
|
subtotal: i,
|
|
@@ -319,14 +319,14 @@ function u(u = {}) {
|
|
|
319
319
|
total: i,
|
|
320
320
|
taxType: "NONE",
|
|
321
321
|
jurisdiction: "UNKNOWN",
|
|
322
|
-
currency:
|
|
322
|
+
currency: S,
|
|
323
323
|
error: "Failed to process credit payment"
|
|
324
324
|
});
|
|
325
325
|
}
|
|
326
|
-
let o = _.filter((e) => e.type === "ADDON"), s = o.length > 0 ? o.map((e) => e.metadata?.addonId || e.id) : void 0, c = o.length > 0 ? o.map((e) => e.quantity) : void 0, l = _.find((e) => e.type === "PLAN")?.metadata?.duration === "yearly", ee = t === "PLAN" &&
|
|
326
|
+
let o = _.filter((e) => e.type === "ADDON"), s = o.length > 0 ? o.map((e) => e.metadata?.addonId || e.id) : void 0, c = o.length > 0 ? o.map((e) => e.quantity) : void 0, l = _.find((e) => e.type === "PLAN")?.metadata?.duration === "yearly", ee = t === "PLAN" && U && (W || B) || void 0, { data: u } = await ue({ variables: { input: {
|
|
327
327
|
billingAccountId: y.id,
|
|
328
328
|
amount: i,
|
|
329
|
-
currency:
|
|
329
|
+
currency: S,
|
|
330
330
|
purchaseType: t,
|
|
331
331
|
provider: a,
|
|
332
332
|
orderId: r || void 0,
|
|
@@ -337,16 +337,16 @@ function u(u = {}) {
|
|
|
337
337
|
addonIds: t === "ADDON" && o.length > 1 && !r ? s : void 0,
|
|
338
338
|
quantities: t === "ADDON" && o.length > 1 && !r ? c : void 0,
|
|
339
339
|
subscriptionId: t === "ADDON" && !r ? G : void 0,
|
|
340
|
-
paymentMethodId:
|
|
341
|
-
saveCard:
|
|
340
|
+
paymentMethodId: B || void 0,
|
|
341
|
+
saveCard: B ? void 0 : H || U,
|
|
342
342
|
upgradeFromSubscriptionId: m || void 0,
|
|
343
343
|
downgradeFromSubscriptionId: h || void 0,
|
|
344
344
|
isAnnual: l || void 0,
|
|
345
|
-
promoCode:
|
|
346
|
-
autoRenew: t === "PLAN" &&
|
|
345
|
+
promoCode: w || void 0,
|
|
346
|
+
autoRenew: t === "PLAN" && U ? !0 : void 0,
|
|
347
347
|
autoRenewPaymentMethodId: ee
|
|
348
348
|
} } }), d = u?.initiatePayment;
|
|
349
|
-
return d?.success ? (
|
|
349
|
+
return d?.success ? (P("success"), d) : (P("error"), F(d?.error ?? "Payment initiation failed"), {
|
|
350
350
|
success: !1,
|
|
351
351
|
provider: a,
|
|
352
352
|
subtotal: i,
|
|
@@ -355,12 +355,12 @@ function u(u = {}) {
|
|
|
355
355
|
total: i,
|
|
356
356
|
taxType: "NONE",
|
|
357
357
|
jurisdiction: "UNKNOWN",
|
|
358
|
-
currency:
|
|
358
|
+
currency: S,
|
|
359
359
|
error: d?.error ?? "Payment initiation failed"
|
|
360
360
|
});
|
|
361
361
|
} catch (e) {
|
|
362
362
|
let t = e instanceof Error ? e.message : "An error occurred";
|
|
363
|
-
return
|
|
363
|
+
return P("error"), F(t), {
|
|
364
364
|
success: !1,
|
|
365
365
|
provider: "AUTO",
|
|
366
366
|
subtotal: 0,
|
|
@@ -369,22 +369,22 @@ function u(u = {}) {
|
|
|
369
369
|
total: 0,
|
|
370
370
|
taxType: "NONE",
|
|
371
371
|
jurisdiction: "UNKNOWN",
|
|
372
|
-
currency:
|
|
372
|
+
currency: S,
|
|
373
373
|
error: t
|
|
374
374
|
};
|
|
375
375
|
}
|
|
376
376
|
}, [
|
|
377
377
|
y,
|
|
378
378
|
_,
|
|
379
|
-
|
|
379
|
+
S,
|
|
380
380
|
b,
|
|
381
381
|
J,
|
|
382
382
|
X,
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
V,
|
|
383
|
+
w,
|
|
384
|
+
B,
|
|
386
385
|
H,
|
|
387
386
|
U,
|
|
387
|
+
W,
|
|
388
388
|
G,
|
|
389
389
|
m,
|
|
390
390
|
h,
|
|
@@ -393,7 +393,7 @@ function u(u = {}) {
|
|
|
393
393
|
fe
|
|
394
394
|
]),
|
|
395
395
|
reset: o(() => {
|
|
396
|
-
v([]), te(null),
|
|
396
|
+
v([]), te(null), x("AUTO"), C(f), T(""), D(0), O("PERCENTAGE"), A(!1), M(0), P("idle"), F(null), L(0), R(0), z("NONE"), V(null), oe(p), se(!1), ce(null), le(g ?? null);
|
|
397
397
|
}, [
|
|
398
398
|
f,
|
|
399
399
|
p,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCheckout.js","names":[],"sources":["../../../../../src/billing/modules/checkout/hooks/useCheckout.ts"],"sourcesContent":["/**\n * Main checkout hook for managing checkout state and payment processing\n */\n\nimport { useState, useCallback, useMemo, useEffect } from 'react';\nimport {\n useInitiatePaymentCheckoutMutation,\n useSpendCreditsCheckoutMutation,\n useConvertCurrencyLazyQuery,\n useValidateCouponLazyQuery,\n} from '../../../../generated/global-operations';\nimport { useBilling } from '../../../providers/BillingProvider';\nimport { useEnsureStoreOrderBillingOrder } from './useStoreShipping';\nimport type {\n CheckoutItem,\n CheckoutBillingAccount,\n PaymentProvider,\n Currency,\n CheckoutState,\n PaymentInitResponse,\n PromoCodeResult,\n PurchaseType,\n} from '../types';\n\n/**\n * Credit conversion rate: How many credits equal 1 USD\n * This should match the backend creditRate.rateUSD (default 0.01 means 100 credits = $1)\n */\nconst CREDITS_PER_USD = 100;\n\ninterface UseCheckoutOptions {\n billingAccountId?: string;\n defaultCurrency?: Currency;\n defaultSaveCard?: boolean;\n upgradeFromSubscriptionId?: string;\n /** For plan downgrades — old sub auto-renewal cancelled, runs to natural expiry */\n downgradeFromSubscriptionId?: string;\n /** For addon purchases - pre-selected subscription ID */\n defaultSubscriptionId?: string;\n}\n\ninterface UseCheckoutResult {\n // State\n items: CheckoutItem[];\n billingAccount: CheckoutBillingAccount | null;\n selectedProvider: PaymentProvider;\n selectedCurrency: Currency;\n promoCode: string;\n promoDiscount: number;\n promoDiscountType: 'PERCENTAGE' | 'FIXED';\n useCredits: boolean;\n creditsToUse: number;\n checkoutState: CheckoutState;\n error: string | null;\n\n // Saved card state\n selectedPaymentMethodId: string | null;\n saveCard: boolean;\n\n // Auto-pay state (for subscription purchases)\n autoRenew: boolean;\n autoRenewPaymentMethodId: string | null;\n\n // Subscription state (for addon purchases)\n selectedSubscriptionId: string | null;\n\n // Computed values\n subtotal: number;\n subtotalUSD: number;\n discountAmount: number;\n taxAmount: number;\n taxRate: number;\n taxType: string;\n total: number;\n totalInSelectedCurrency: number;\n availableCredits: number;\n hasEnoughCredits: boolean;\n canPayWithCredits: boolean;\n isLoading: boolean;\n isProcessing: boolean;\n isCalculatingTax: boolean;\n\n // Currency conversion\n conversionRate: number;\n convertedAmount: number | null;\n\n // Actions\n addItem: (item: CheckoutItem) => void;\n removeItem: (itemId: string) => void;\n updateItemQuantity: (itemId: string, quantity: number) => void;\n updateItemPrice: (itemId: string, price: number, currency?: Currency) => void;\n clearItems: () => void;\n setBillingAccount: (account: CheckoutBillingAccount | null) => void;\n setPaymentProvider: (provider: PaymentProvider) => void;\n setCurrency: (currency: Currency) => void;\n setPromoCode: (code: string) => void;\n validatePromoCode: () => Promise<PromoCodeResult>;\n clearPromoCode: () => void;\n setUseCredits: (use: boolean) => void;\n setCreditsToUse: (amount: number) => void;\n setSelectedPaymentMethodId: (id: string | null) => void;\n setSaveCard: (save: boolean) => void;\n setAutoRenew: (enabled: boolean) => void;\n setAutoRenewPaymentMethodId: (id: string | null) => void;\n setSelectedSubscriptionId: (id: string | null) => void;\n initiatePayment: () => Promise<PaymentInitResponse>;\n reset: () => void;\n}\n\nexport function useCheckout(options: UseCheckoutOptions = {}): UseCheckoutResult {\n const { currentUser } = useBilling();\n const {\n defaultCurrency = 'USD',\n defaultSaveCard = false, // CRITICAL: Never save cards without explicit user consent\n upgradeFromSubscriptionId,\n downgradeFromSubscriptionId,\n defaultSubscriptionId,\n } = options;\n\n // Core state\n const [items, setItems] = useState<CheckoutItem[]>([]);\n const [billingAccount, setBillingAccount] = useState<CheckoutBillingAccount | null>(null);\n const [selectedProvider, setSelectedProvider] = useState<PaymentProvider>('AUTO');\n const [selectedCurrency, setSelectedCurrency] = useState<Currency>(defaultCurrency);\n const [promoCode, setPromoCode] = useState('');\n const [promoDiscount, setPromoDiscount] = useState(0);\n const [promoDiscountType, setPromoDiscountType] = useState<'PERCENTAGE' | 'FIXED'>('PERCENTAGE');\n const [useCredits, setUseCredits] = useState(false);\n const [creditsToUse, setCreditsToUse] = useState(0);\n const [checkoutState, setCheckoutState] = useState<CheckoutState>('idle');\n const [error, setError] = useState<string | null>(null);\n const [taxAmount, setTaxAmount] = useState(0);\n const [taxRate, setTaxRate] = useState(0);\n const [taxType, setTaxType] = useState<string>('NONE');\n\n // Saved card state\n const [selectedPaymentMethodId, setSelectedPaymentMethodId] = useState<string | null>(null);\n const [saveCard, setSaveCard] = useState(defaultSaveCard);\n\n // Auto-pay state (for subscription purchases)\n const [autoRenew, setAutoRenew] = useState(false);\n const [autoRenewPaymentMethodId, setAutoRenewPaymentMethodId] = useState<string | null>(null);\n\n // Subscription state (for addon purchases)\n const [selectedSubscriptionId, setSelectedSubscriptionId] = useState<string | null>(\n defaultSubscriptionId ?? null\n );\n\n // GraphQL mutations and queries\n const [initiatePaymentMutation] = useInitiatePaymentCheckoutMutation();\n const [ensureStoreOrderBillingOrderMutation] = useEnsureStoreOrderBillingOrder();\n const [spendCreditsMutation] = useSpendCreditsCheckoutMutation();\n const [convertCurrency, { data: currencyData, loading: currencyLoading }] =\n useConvertCurrencyLazyQuery();\n const [validateCoupon] = useValidateCouponLazyQuery();\n\n // Computed values\n\n // The currency `total`/`subtotal` are actually denominated in. Plans,\n // addons, and credits are priced in USD; store orders are priced in the\n // order's own currency (item.currency), which may not be USD. Converting\n // `total` to the selected currency must pivot FROM this currency, not\n // always assume USD — see BOFF-7127 (a ₹50 item was shown as $4,748.50\n // because the conversion always requested a USD→X rate and multiplied,\n // which is only correct when the source amount actually is in USD).\n const nativeCurrency = useMemo<Currency>(() => items[0]?.currency ?? 'USD', [items]);\n\n const subtotal = useMemo(() => {\n return items.reduce((sum, item) => sum + item.price * item.quantity, 0);\n }, [items]);\n\n // USD subtotal — always use priceUSD (base price) for credit calculations.\n // item.price may be in a local currency (INR, EUR, etc.) when currencyPrices is set.\n const subtotalUSD = useMemo(() => {\n return items.reduce((sum, item) => sum + (item.priceUSD ?? item.price) * item.quantity, 0);\n }, [items]);\n\n const discountAmount = useMemo(() => {\n if (!promoDiscount) return 0;\n // promoDiscount now contains the actual discount amount from backend validation\n // (not the percentage value), so use it directly\n return Math.min(promoDiscount, subtotal);\n }, [subtotal, promoDiscount]);\n\n // Total includes tax (calculated via calculateTax query)\n const total = useMemo(() => {\n const afterDiscount = subtotal - discountAmount;\n const withCredits = useCredits ? Math.max(0, afterDiscount - creditsToUse) : afterDiscount;\n return withCredits + taxAmount;\n }, [subtotal, discountAmount, useCredits, creditsToUse, taxAmount]);\n\n const availableCredits = billingAccount?.creditAmount ?? 0;\n\n const hasEnoughCredits = useMemo(() => {\n // Use subtotalUSD so this works correctly for any display currency\n const creditsRequired = subtotalUSD * CREDITS_PER_USD;\n return availableCredits >= creditsRequired;\n }, [availableCredits, subtotalUSD]);\n\n // Can only pay with credits if not buying credits\n const canPayWithCredits = useMemo(() => {\n const hasCreditsPurchase = items.some((item) => item.type === 'CREDITS');\n return !hasCreditsPurchase && availableCredits > 0;\n }, [items, availableCredits]);\n\n // Currency conversion result\n const convertedAmount = currencyData?.convertCurrency?.amount ?? null;\n const conversionRate = currencyData?.convertCurrency?.exchangeRate ?? 1;\n const totalInSelectedCurrency = convertedAmount ?? total;\n\n const isLoading = checkoutState === 'loading' || currencyLoading;\n const isProcessing = checkoutState === 'processing';\n\n // Effect to trigger currency conversion when total or currency changes\n // This ensures the converted amount is always up-to-date\n useEffect(() => {\n // Skip conversion for credit payments (no currency conversion needed)\n if (selectedProvider === 'CREDITS') {\n return;\n }\n\n // Skip if no items yet (cart is still loading)\n if (items.length === 0) {\n return;\n }\n\n // Skip conversion if items already have prices in selected currency\n // (Plans/Addons with multi-currency pricing have currencyPrices stored)\n const allItemsHaveLocalizedPrices = items.every(\n (item) => item.currency === selectedCurrency || item.type === 'CREDITS'\n );\n\n if (allItemsHaveLocalizedPrices) {\n // Items are already priced in the selected currency (from currencyPrices)\n // No conversion needed - subtotal and total are already correct!\n return;\n }\n\n // Only fetch if we have a valid total and it needs converting at all.\n if (total > 0 && nativeCurrency !== selectedCurrency) {\n // For INR, wait until tax has been calculated (taxAmount > 0 or taxRate is set)\n // This prevents double API calls - one before tax, one after\n if (selectedCurrency === 'INR' && taxAmount === 0 && subtotal > 0) {\n // Tax hasn't been calculated yet, skip this conversion\n // The effect will run again when taxAmount changes\n return;\n }\n\n convertCurrency({\n variables: {\n amount: total,\n toCurrency: selectedCurrency,\n fromCurrency: nativeCurrency,\n },\n });\n }\n }, [\n total,\n selectedCurrency,\n selectedProvider,\n convertCurrency,\n taxAmount,\n subtotal,\n items,\n nativeCurrency,\n ]);\n\n // Effect to calculate tax when billing account, subtotal, or currency changes\n // Tax is only applied for INR payments (18% GST)\n useEffect(() => {\n // Only calculate tax if we have a billing account and items\n if (!billingAccount || subtotal <= 0) {\n // Reset tax if no billing account or empty cart\n setTaxAmount(0);\n setTaxRate(0);\n setTaxType('NONE');\n return;\n }\n\n // Skip if paying with credits (no tax on credit payments)\n if (selectedProvider === 'CREDITS') {\n setTaxAmount(0);\n setTaxRate(0);\n setTaxType('NONE');\n return;\n }\n\n // Only apply tax for INR payments\n if (selectedCurrency !== 'INR') {\n setTaxAmount(0);\n setTaxRate(0);\n setTaxType('NONE');\n return;\n }\n\n // For INR: Calculate 18% GST on the USD amount, then we'll convert for display\n const afterDiscount = subtotal - discountAmount;\n const GST_RATE = 0.18;\n const taxInUSD = afterDiscount * GST_RATE;\n\n setTaxAmount(taxInUSD);\n setTaxRate(GST_RATE);\n setTaxType('GST');\n }, [billingAccount, subtotal, discountAmount, selectedCurrency, selectedProvider]);\n\n // Actions\n const addItem = useCallback((item: CheckoutItem) => {\n setItems((prev) => {\n // Plans can only be purchased once - quantity is always 1\n if (item.type === 'PLAN') {\n // Check if any plan already exists in cart\n const existingPlan = prev.find((i) => i.type === 'PLAN');\n if (existingPlan) {\n // Replace existing plan with new one (can only have 1 plan)\n return prev.map((i) => (i.type === 'PLAN' ? { ...item, quantity: 1 } : i));\n }\n // Add new plan with quantity forced to 1\n return [...prev, { ...item, quantity: 1 }];\n }\n\n // For addons and credits, allow quantity increment\n const existing = prev.find((i) => i.id === item.id);\n if (existing) {\n return prev.map((i) =>\n i.id === item.id ? { ...i, quantity: i.quantity + item.quantity } : i\n );\n }\n return [...prev, item];\n });\n }, []);\n\n const removeItem = useCallback((itemId: string) => {\n setItems((prev) => prev.filter((i) => i.id !== itemId));\n }, []);\n\n const updateItemQuantity = useCallback(\n (itemId: string, quantity: number) => {\n if (quantity <= 0) {\n removeItem(itemId);\n return;\n }\n setItems((prev) =>\n prev.map((i) => {\n if (i.id !== itemId) return i;\n // Plans can only have quantity 1\n if (i.type === 'PLAN') {\n return { ...i, quantity: 1 };\n }\n return { ...i, quantity };\n })\n );\n },\n [removeItem]\n );\n\n const updateItemPrice = useCallback((itemId: string, price: number, currency?: Currency) => {\n setItems((prev) =>\n prev.map((i) => {\n if (i.id !== itemId) return i;\n // Update price and optionally currency\n const updates: Partial<CheckoutItem> = { price };\n if (currency) {\n updates.currency = currency;\n }\n return { ...i, ...updates };\n })\n );\n }, []);\n\n const clearItems = useCallback(() => {\n setItems([]);\n }, []);\n\n const handleSetBillingAccount = useCallback((account: CheckoutBillingAccount | null) => {\n setBillingAccount(account);\n // Reset credits usage when billing account changes\n setUseCredits(false);\n setCreditsToUse(0);\n }, []);\n\n const handleSetCurrency = useCallback(\n (currency: Currency) => {\n setSelectedCurrency(currency);\n // Skip conversion if items already have localized prices\n // (Plans/Addons with multi-currency pricing don't need conversion)\n const allItemsHaveLocalizedPrices = items.every(\n (item) => item.currency === currency || item.type === 'CREDITS'\n );\n\n // Only fetch conversion rate if items don't have localized prices\n if (total > 0 && nativeCurrency !== currency && !allItemsHaveLocalizedPrices) {\n convertCurrency({\n variables: {\n amount: total,\n toCurrency: currency,\n fromCurrency: nativeCurrency,\n },\n });\n }\n },\n [total, convertCurrency, items, nativeCurrency]\n );\n\n const handleSetPaymentProvider = useCallback(\n (provider: PaymentProvider) => {\n setSelectedProvider(provider);\n // Razorpay only supports INR\n if (provider === 'RAZORPAY' && selectedCurrency !== 'INR') {\n setSelectedCurrency('INR');\n }\n },\n [selectedCurrency]\n );\n\n const validatePromoCode = useCallback(async (): Promise<PromoCodeResult> => {\n if (!promoCode.trim()) {\n return {\n valid: false,\n code: '',\n discountType: 'PERCENTAGE',\n discountValue: 0,\n error: 'Please enter a promo code',\n };\n }\n\n try {\n setCheckoutState('loading');\n\n // Build validation input\n const validationInput = {\n code: promoCode.trim().toUpperCase(),\n userId: currentUser?.id ?? '',\n userEmail: currentUser?.email,\n amount: subtotal,\n currency: selectedCurrency,\n };\n\n const { data, error: queryError } = await validateCoupon({\n variables: {\n input: validationInput,\n },\n });\n\n if (queryError || !data?.validateCoupon) {\n setPromoDiscount(0);\n setCheckoutState('idle');\n return {\n valid: false,\n code: promoCode,\n discountType: 'PERCENTAGE',\n discountValue: 0,\n error: 'Failed to validate coupon',\n };\n }\n\n const response = data.validateCoupon;\n\n if (!response.valid) {\n setPromoDiscount(0);\n setCheckoutState('idle');\n return {\n valid: false,\n code: promoCode,\n discountType: 'PERCENTAGE',\n discountValue: 0,\n error: response.error || 'Invalid coupon code',\n };\n }\n\n // Extract discount details from coupon\n const coupon = response.coupon;\n if (!coupon) {\n setPromoDiscount(0);\n setCheckoutState('idle');\n return {\n valid: false,\n code: promoCode,\n discountType: 'PERCENTAGE',\n discountValue: 0,\n error: 'Coupon data not found',\n };\n }\n\n // Use the ACTUAL discount amount from validation (includes caps and currency conversion)\n const actualDiscountAmount = response.discountAmount || 0;\n\n const discountType = coupon.type === 'FIXED_AMOUNT' ? 'FIXED' : 'PERCENTAGE';\n setPromoDiscount(actualDiscountAmount); // Use actual discount, not coupon.value\n setPromoDiscountType(discountType);\n setCheckoutState('idle');\n\n return {\n valid: true,\n code: coupon.code,\n discountType,\n discountValue: actualDiscountAmount, // Use actual discount, not coupon.value\n };\n } catch {\n setPromoDiscount(0);\n setCheckoutState('idle');\n return {\n valid: false,\n code: promoCode,\n discountType: 'PERCENTAGE',\n discountValue: 0,\n error: 'Failed to validate coupon',\n };\n }\n }, [currentUser?.email, currentUser?.id, promoCode, selectedCurrency, subtotal, validateCoupon]);\n\n const clearPromoCode = useCallback(() => {\n setPromoCode('');\n setPromoDiscount(0);\n setPromoDiscountType('PERCENTAGE');\n }, []);\n\n const handleSetCreditsToUse = useCallback(\n (amount: number) => {\n const maxCredits = Math.min(amount, availableCredits, subtotal - discountAmount);\n setCreditsToUse(Math.max(0, maxCredits));\n },\n [availableCredits, subtotal, discountAmount]\n );\n\n const initiatePayment = useCallback(\n async (): Promise<PaymentInitResponse> => {\n if (!billingAccount) {\n return {\n success: false,\n provider: 'AUTO',\n subtotal: 0,\n taxAmount: 0,\n taxRate: 0,\n total: 0,\n taxType: 'NONE',\n jurisdiction: 'UNKNOWN',\n currency: selectedCurrency,\n error: 'Please select a billing account',\n };\n }\n\n if (items.length === 0) {\n return {\n success: false,\n provider: 'AUTO',\n subtotal: 0,\n taxAmount: 0,\n taxRate: 0,\n total: 0,\n taxType: 'NONE',\n jurisdiction: 'UNKNOWN',\n currency: selectedCurrency,\n error: 'No items in checkout',\n };\n }\n\n try {\n setCheckoutState('processing');\n setError(null);\n\n // Determine purchase type from first item\n const firstItem = items[0];\n let purchaseType: PurchaseType = firstItem.type;\n\n // Check if this is a store order checkout (detect early)\n const storeOrderItem = items.find((item) => item.metadata?.storeOrderId);\n const storeOrderId = storeOrderItem?.metadata?.storeOrderId as string | undefined;\n let billingOrderId =\n (items.find((item) => item.metadata?.billingOrderId)?.metadata?.billingOrderId as\n string | undefined) || undefined;\n\n // For store orders, use 'ORDER' purchase type\n if (storeOrderId) {\n purchaseType = 'ORDER' as PurchaseType;\n }\n\n // Calculate amount to charge\n const chargeAmount = subtotal - discountAmount;\n\n // Determine provider\n let provider = selectedProvider;\n if (provider === 'AUTO') {\n provider = selectedCurrency === 'INR' ? 'RAZORPAY' : 'STRIPE';\n }\n\n // If this is a store order without a billing order yet, let the store service\n // create it from its authoritative order snapshot. This persists the billing\n // order ID and registers the signed callback on the same service that owns\n // the order; browser line-item prices and shipping values are never sent to\n // billing as the order snapshot.\n if (purchaseType === 'ORDER' && !billingOrderId && storeOrderId && billingAccount) {\n const { data: billingOrderResult } = await ensureStoreOrderBillingOrderMutation({\n variables: {\n storeOrderId,\n billingAccountId: billingAccount.id,\n currency: selectedCurrency,\n },\n });\n const ensureResult = billingOrderResult?.ensureStoreOrderBillingOrder;\n\n if (ensureResult?.success && ensureResult.billingOrderId) {\n billingOrderId = ensureResult.billingOrderId;\n } else {\n const errMsg = ensureResult?.error ?? 'Failed to create billing order';\n setCheckoutState('error');\n setError(errMsg);\n return {\n success: false,\n provider,\n subtotal: chargeAmount,\n taxAmount: 0,\n taxRate: 0,\n total: chargeAmount,\n taxType: 'NONE',\n jurisdiction: 'UNKNOWN',\n currency: selectedCurrency,\n error: errMsg,\n };\n }\n }\n\n // ========================================\n // CREDITS PAYMENT - use spendCredits mutation\n // Supports multiple addons\n // ========================================\n if (provider === 'CREDITS') {\n const addonItems = items.filter((item) => item.type === 'ADDON');\n const planItem = items.find((item) => item.type === 'PLAN');\n\n const { data } = await spendCreditsMutation({\n variables: {\n input: {\n billingAccountId: billingAccount.id,\n planId: planItem ? (planItem.metadata?.planId as string) || planItem.id : undefined,\n // Multiple addons supported via addOnIds array\n addOnIds:\n addonItems.length > 0\n ? addonItems.map((item) => ({\n id: (item.metadata?.addonId as string) || item.id,\n quantity: item.quantity,\n }))\n : undefined,\n isAnnual: planItem?.metadata?.duration === 'yearly',\n // Include upgrade context if changing plans\n upgradeFromSubscriptionId: upgradeFromSubscriptionId || undefined,\n // Include downgrade context if downgrading\n downgradeFromSubscriptionId: downgradeFromSubscriptionId || undefined,\n // For addon purchases - subscription to attach to\n subscriptionId: addonItems.length > 0 ? selectedSubscriptionId : undefined,\n // For PER_SEAT plans - pass seat count for credit calculation + subscription storage\n seatCount: planItem?.metadata?.seatCount as number | undefined,\n },\n },\n });\n\n const response = data?.spendCredits;\n\n if (!response?.creditTransactionID) {\n setCheckoutState('error');\n setError('Failed to process credit payment');\n return {\n success: false,\n provider: 'CREDITS',\n subtotal: chargeAmount,\n taxAmount: 0,\n taxRate: 0,\n total: chargeAmount,\n taxType: 'NONE',\n jurisdiction: 'UNKNOWN',\n currency: selectedCurrency,\n error: 'Failed to process credit payment',\n };\n }\n\n setCheckoutState('success');\n return {\n success: true,\n provider: 'CREDITS',\n subtotal: chargeAmount,\n taxAmount: 0,\n taxRate: 0,\n total: chargeAmount,\n taxType: 'NONE',\n jurisdiction: 'UNKNOWN',\n currency: selectedCurrency,\n // Return transaction ID for success handling\n orderId: response.creditTransactionID,\n };\n }\n\n // ========================================\n // CARD PAYMENT (Stripe/Razorpay) - use initiatePayment\n // Supports single or multiple addons\n // Supports store orders (via orderId parameter)\n // ========================================\n const addonItems = items.filter((item) => item.type === 'ADDON');\n\n // Build addon IDs and quantities for the mutation\n const addonIds =\n addonItems.length > 0\n ? addonItems.map((item) => (item.metadata?.addonId as string) || item.id)\n : undefined;\n const quantities =\n addonItems.length > 0 ? addonItems.map((item) => item.quantity) : undefined;\n\n // Determine if this is an annual plan purchase\n const planItem = items.find((item) => item.type === 'PLAN');\n const isAnnualPlan = planItem?.metadata?.duration === 'yearly';\n\n // Determine auto-renew payment method\n const autoRenewPMId =\n purchaseType === 'PLAN' && autoRenew\n ? autoRenewPaymentMethodId || selectedPaymentMethodId || undefined\n : undefined;\n\n const { data } = await initiatePaymentMutation({\n variables: {\n input: {\n billingAccountId: billingAccount.id,\n amount: chargeAmount, // Note: Backend IGNORES this and calculates from DB\n currency: selectedCurrency,\n purchaseType,\n provider: provider,\n // Store order - use orderId parameter (billing order ID)\n orderId: billingOrderId || undefined,\n creditAmount: purchaseType === 'CREDITS' ? firstItem.quantity : undefined,\n planId:\n purchaseType === 'PLAN'\n ? (firstItem.metadata?.planId as string) || firstItem.id\n : undefined,\n // Single addon (backwards compatible) - skip if store order\n addonId:\n purchaseType === 'ADDON' && addonItems.length === 1 && !billingOrderId\n ? (addonItems[0].metadata?.addonId as string) || addonItems[0].id\n : undefined,\n quantity:\n purchaseType === 'ADDON' && addonItems.length === 1 && !billingOrderId\n ? addonItems[0].quantity\n : undefined,\n // Multiple addons (cart checkout) - skip if store order\n addonIds:\n purchaseType === 'ADDON' && addonItems.length > 1 && !billingOrderId\n ? addonIds\n : undefined,\n quantities:\n purchaseType === 'ADDON' && addonItems.length > 1 && !billingOrderId\n ? quantities\n : undefined,\n // For addon purchases - subscription to attach to (skip for store orders)\n subscriptionId:\n purchaseType === 'ADDON' && !billingOrderId ? selectedSubscriptionId : undefined,\n // Saved card support\n paymentMethodId: selectedPaymentMethodId || undefined,\n // Save card if: user checked save card OR auto-renewal is enabled (which requires a saved card)\n // Only save if not using existing card (selectedPaymentMethodId is null)\n saveCard: !selectedPaymentMethodId ? saveCard || autoRenew : undefined,\n // Include upgrade context if changing plans\n upgradeFromSubscriptionId: upgradeFromSubscriptionId || undefined,\n // Include downgrade context if downgrading\n downgradeFromSubscriptionId: downgradeFromSubscriptionId || undefined,\n // Annual plan flag (for pricing calculation)\n isAnnual: isAnnualPlan || undefined,\n // Promo code (validated and applied on backend)\n promoCode: promoCode || undefined,\n // Auto-renewal (for plan purchases only)\n autoRenew: purchaseType === 'PLAN' && autoRenew ? true : undefined,\n autoRenewPaymentMethodId: autoRenewPMId,\n },\n },\n });\n\n const response = data?.initiatePayment;\n\n if (!response?.success) {\n setCheckoutState('error');\n setError(response?.error ?? 'Payment initiation failed');\n return {\n success: false,\n provider: provider,\n subtotal: chargeAmount,\n taxAmount: 0,\n taxRate: 0,\n total: chargeAmount,\n taxType: 'NONE',\n jurisdiction: 'UNKNOWN',\n currency: selectedCurrency,\n error: response?.error ?? 'Payment initiation failed',\n };\n }\n\n // Note: Tax is already calculated upfront in the effect, don't overwrite\n // The response tax is in converted currency which would mess up display\n setCheckoutState('success');\n\n return response;\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : 'An error occurred';\n setCheckoutState('error');\n setError(errorMessage);\n return {\n success: false,\n provider: 'AUTO',\n subtotal: 0,\n taxAmount: 0,\n taxRate: 0,\n total: 0,\n taxType: 'NONE',\n jurisdiction: 'UNKNOWN',\n currency: selectedCurrency,\n error: errorMessage,\n };\n }\n },\n [\n billingAccount,\n items,\n selectedCurrency,\n selectedProvider,\n subtotal,\n discountAmount,\n promoCode,\n selectedPaymentMethodId,\n saveCard,\n autoRenew,\n autoRenewPaymentMethodId,\n selectedSubscriptionId,\n upgradeFromSubscriptionId,\n downgradeFromSubscriptionId,\n initiatePaymentMutation,\n ensureStoreOrderBillingOrderMutation,\n spendCreditsMutation,\n ]\n );\n\n const reset = useCallback(() => {\n setItems([]);\n setBillingAccount(null);\n setSelectedProvider('AUTO');\n setSelectedCurrency(defaultCurrency);\n setPromoCode('');\n setPromoDiscount(0);\n setPromoDiscountType('PERCENTAGE');\n setUseCredits(false);\n setCreditsToUse(0);\n setCheckoutState('idle');\n setError(null);\n setTaxAmount(0);\n setTaxRate(0);\n setTaxType('NONE');\n setSelectedPaymentMethodId(null);\n setSaveCard(defaultSaveCard);\n setAutoRenew(false);\n setAutoRenewPaymentMethodId(null);\n setSelectedSubscriptionId(defaultSubscriptionId ?? null);\n }, [defaultCurrency, defaultSaveCard, defaultSubscriptionId]);\n\n return {\n // State\n items,\n billingAccount,\n selectedProvider,\n selectedCurrency,\n promoCode,\n promoDiscount,\n promoDiscountType,\n useCredits,\n creditsToUse,\n checkoutState,\n error,\n\n // Saved card state\n selectedPaymentMethodId,\n saveCard,\n\n // Auto-pay state\n autoRenew,\n autoRenewPaymentMethodId,\n\n // Subscription state (for addon purchases)\n selectedSubscriptionId,\n\n // Computed\n subtotal,\n subtotalUSD,\n discountAmount,\n taxAmount,\n taxRate,\n taxType,\n total,\n totalInSelectedCurrency,\n availableCredits,\n hasEnoughCredits,\n canPayWithCredits,\n isLoading,\n isProcessing,\n isCalculatingTax: false, // Tax is calculated synchronously now\n conversionRate,\n convertedAmount,\n\n // Actions\n addItem,\n removeItem,\n updateItemQuantity,\n updateItemPrice,\n clearItems,\n setBillingAccount: handleSetBillingAccount,\n setPaymentProvider: handleSetPaymentProvider,\n setCurrency: handleSetCurrency,\n setPromoCode,\n validatePromoCode,\n clearPromoCode,\n setUseCredits,\n setCreditsToUse: handleSetCreditsToUse,\n setSelectedPaymentMethodId,\n setSaveCard,\n setAutoRenew,\n setAutoRenewPaymentMethodId,\n setSelectedSubscriptionId,\n initiatePayment,\n reset,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useCheckout.js","names":[],"sources":["../../../../../src/billing/modules/checkout/hooks/useCheckout.ts"],"sourcesContent":["/**\n * Main checkout hook for managing checkout state and payment processing\n */\n\nimport { useState, useCallback, useMemo, useEffect } from 'react';\nimport {\n useInitiatePaymentCheckoutMutation,\n useSpendCreditsCheckoutMutation,\n useConvertCurrencyLazyQuery,\n useValidateCouponLazyQuery,\n} from '../../../../generated/global-operations';\nimport { useBilling } from '../../../providers/BillingProvider';\nimport { useEnsureStoreOrderBillingOrder } from './useStoreShipping';\nimport type {\n CheckoutItem,\n CheckoutBillingAccount,\n PaymentProvider,\n Currency,\n CheckoutState,\n PaymentInitResponse,\n PromoCodeResult,\n PurchaseType,\n} from '../types';\n\n/**\n * Credit conversion rate: How many credits equal 1 USD\n * This should match the backend creditRate.rateUSD (default 0.01 means 100 credits = $1)\n */\nconst CREDITS_PER_USD = 100;\n\ninterface UseCheckoutOptions {\n billingAccountId?: string;\n defaultCurrency?: Currency;\n defaultSaveCard?: boolean;\n upgradeFromSubscriptionId?: string;\n /** For plan downgrades — old sub auto-renewal cancelled, runs to natural expiry */\n downgradeFromSubscriptionId?: string;\n /** For addon purchases - pre-selected subscription ID */\n defaultSubscriptionId?: string;\n}\n\ninterface UseCheckoutResult {\n // State\n items: CheckoutItem[];\n billingAccount: CheckoutBillingAccount | null;\n selectedProvider: PaymentProvider;\n selectedCurrency: Currency;\n promoCode: string;\n promoDiscount: number;\n promoDiscountType: 'PERCENTAGE' | 'FIXED';\n useCredits: boolean;\n creditsToUse: number;\n checkoutState: CheckoutState;\n error: string | null;\n\n // Saved card state\n selectedPaymentMethodId: string | null;\n saveCard: boolean;\n\n // Auto-pay state (for subscription purchases)\n autoRenew: boolean;\n autoRenewPaymentMethodId: string | null;\n\n // Subscription state (for addon purchases)\n selectedSubscriptionId: string | null;\n\n // Computed values\n subtotal: number;\n subtotalUSD: number;\n discountAmount: number;\n taxAmount: number;\n taxRate: number;\n taxType: string;\n total: number;\n totalInSelectedCurrency: number;\n availableCredits: number;\n hasEnoughCredits: boolean;\n canPayWithCredits: boolean;\n isLoading: boolean;\n isProcessing: boolean;\n isCalculatingTax: boolean;\n\n // Currency conversion\n conversionRate: number;\n convertedAmount: number | null;\n\n // Actions\n addItem: (item: CheckoutItem) => void;\n removeItem: (itemId: string) => void;\n updateItemQuantity: (itemId: string, quantity: number) => void;\n updateItemPrice: (itemId: string, price: number, currency?: Currency) => void;\n clearItems: () => void;\n setBillingAccount: (account: CheckoutBillingAccount | null) => void;\n setPaymentProvider: (provider: PaymentProvider) => void;\n setCurrency: (currency: Currency) => void;\n setPromoCode: (code: string) => void;\n validatePromoCode: () => Promise<PromoCodeResult>;\n clearPromoCode: () => void;\n setUseCredits: (use: boolean) => void;\n setCreditsToUse: (amount: number) => void;\n setSelectedPaymentMethodId: (id: string | null) => void;\n setSaveCard: (save: boolean) => void;\n setAutoRenew: (enabled: boolean) => void;\n setAutoRenewPaymentMethodId: (id: string | null) => void;\n setSelectedSubscriptionId: (id: string | null) => void;\n initiatePayment: () => Promise<PaymentInitResponse>;\n reset: () => void;\n}\n\nexport function useCheckout(options: UseCheckoutOptions = {}): UseCheckoutResult {\n const { currentUser } = useBilling();\n const {\n defaultCurrency = 'USD',\n defaultSaveCard = false, // CRITICAL: Never save cards without explicit user consent\n upgradeFromSubscriptionId,\n downgradeFromSubscriptionId,\n defaultSubscriptionId,\n } = options;\n\n // Core state\n const [items, setItems] = useState<CheckoutItem[]>([]);\n const [billingAccount, setBillingAccount] = useState<CheckoutBillingAccount | null>(null);\n const [selectedProvider, setSelectedProvider] = useState<PaymentProvider>('AUTO');\n const [selectedCurrency, setSelectedCurrency] = useState<Currency>(defaultCurrency);\n const [promoCode, setPromoCode] = useState('');\n const [promoDiscount, setPromoDiscount] = useState(0);\n const [promoDiscountType, setPromoDiscountType] = useState<'PERCENTAGE' | 'FIXED'>('PERCENTAGE');\n const [useCredits, setUseCredits] = useState(false);\n const [creditsToUse, setCreditsToUse] = useState(0);\n const [checkoutState, setCheckoutState] = useState<CheckoutState>('idle');\n const [error, setError] = useState<string | null>(null);\n const [taxAmount, setTaxAmount] = useState(0);\n const [taxRate, setTaxRate] = useState(0);\n const [taxType, setTaxType] = useState<string>('NONE');\n\n // Saved card state\n const [selectedPaymentMethodId, setSelectedPaymentMethodId] = useState<string | null>(null);\n const [saveCard, setSaveCard] = useState(defaultSaveCard);\n\n // Auto-pay state (for subscription purchases)\n const [autoRenew, setAutoRenew] = useState(false);\n const [autoRenewPaymentMethodId, setAutoRenewPaymentMethodId] = useState<string | null>(null);\n\n // Subscription state (for addon purchases)\n const [selectedSubscriptionId, setSelectedSubscriptionId] = useState<string | null>(\n defaultSubscriptionId ?? null\n );\n\n // GraphQL mutations and queries\n const [initiatePaymentMutation] = useInitiatePaymentCheckoutMutation();\n const [ensureStoreOrderBillingOrderMutation] = useEnsureStoreOrderBillingOrder();\n const [spendCreditsMutation] = useSpendCreditsCheckoutMutation();\n const [convertCurrency, { data: currencyData, loading: currencyLoading }] =\n useConvertCurrencyLazyQuery();\n const [validateCoupon] = useValidateCouponLazyQuery();\n\n // Computed values\n\n // The currency `total`/`subtotal` are actually denominated in. Plans,\n // addons, and credits are priced in USD; store orders are priced in the\n // order's own currency (item.currency), which may not be USD. Converting\n // `total` to the selected currency must pivot FROM this currency, not\n // always assume USD — see BOFF-7127 (a ₹50 item was shown as $4,748.50\n // because the conversion always requested a USD→X rate and multiplied,\n // which is only correct when the source amount actually is in USD).\n const nativeCurrency = useMemo<Currency>(() => items[0]?.currency ?? 'USD', [items]);\n\n const subtotal = useMemo(() => {\n return items.reduce((sum, item) => sum + item.price * item.quantity, 0);\n }, [items]);\n\n // USD subtotal — always use priceUSD (base price) for credit calculations.\n // item.price may be in a local currency (INR, EUR, etc.) when currencyPrices is set.\n const subtotalUSD = useMemo(() => {\n return items.reduce((sum, item) => sum + (item.priceUSD ?? item.price) * item.quantity, 0);\n }, [items]);\n\n const discountAmount = useMemo(() => {\n if (!promoDiscount) return 0;\n // promoDiscount now contains the actual discount amount from backend validation\n // (not the percentage value), so use it directly\n return Math.min(promoDiscount, subtotal);\n }, [subtotal, promoDiscount]);\n\n // Total includes tax (calculated via calculateTax query)\n const total = useMemo(() => {\n const afterDiscount = subtotal - discountAmount;\n const withCredits = useCredits ? Math.max(0, afterDiscount - creditsToUse) : afterDiscount;\n return withCredits + taxAmount;\n }, [subtotal, discountAmount, useCredits, creditsToUse, taxAmount]);\n\n const availableCredits = billingAccount?.creditAmount ?? 0;\n\n const hasEnoughCredits = useMemo(() => {\n // Use subtotalUSD so this works correctly for any display currency\n const creditsRequired = subtotalUSD * CREDITS_PER_USD;\n return availableCredits >= creditsRequired;\n }, [availableCredits, subtotalUSD]);\n\n // Can only pay with credits if not buying credits\n const canPayWithCredits = useMemo(() => {\n const hasCreditsPurchase = items.some((item) => item.type === 'CREDITS');\n return !hasCreditsPurchase && availableCredits > 0;\n }, [items, availableCredits]);\n\n // Currency conversion result\n const convertedAmount = currencyData?.convertCurrency?.amount ?? null;\n const conversionRate = currencyData?.convertCurrency?.exchangeRate ?? 1;\n const totalInSelectedCurrency = convertedAmount ?? total;\n\n const isLoading = checkoutState === 'loading' || currencyLoading;\n const isProcessing = checkoutState === 'processing';\n\n // Effect to trigger currency conversion when total or currency changes\n // This ensures the converted amount is always up-to-date\n useEffect(() => {\n // Skip conversion for credit payments (no currency conversion needed)\n if (selectedProvider === 'CREDITS') {\n return;\n }\n\n // Skip if no items yet (cart is still loading)\n if (items.length === 0) {\n return;\n }\n\n // Skip conversion if items already have prices in selected currency\n // (Plans/Addons with multi-currency pricing have currencyPrices stored)\n const allItemsHaveLocalizedPrices = items.every(\n (item) => item.currency === selectedCurrency || item.type === 'CREDITS'\n );\n\n if (allItemsHaveLocalizedPrices) {\n // Items are already priced in the selected currency (from currencyPrices)\n // No conversion needed - subtotal and total are already correct!\n return;\n }\n\n // Only fetch if we have a valid total and it needs converting at all.\n if (total > 0 && nativeCurrency !== selectedCurrency) {\n // For INR, wait until tax has been calculated (taxAmount > 0 or taxRate is set)\n // This prevents double API calls - one before tax, one after\n if (selectedCurrency === 'INR' && taxAmount === 0 && subtotal > 0) {\n // Tax hasn't been calculated yet, skip this conversion\n // The effect will run again when taxAmount changes\n return;\n }\n\n convertCurrency({\n variables: {\n amount: total,\n toCurrency: selectedCurrency,\n fromCurrency: nativeCurrency,\n },\n });\n }\n }, [\n total,\n selectedCurrency,\n selectedProvider,\n convertCurrency,\n taxAmount,\n subtotal,\n items,\n nativeCurrency,\n ]);\n\n // Effect to calculate tax when billing account, subtotal, or currency changes\n // Tax is only applied for INR payments (18% GST)\n useEffect(() => {\n // Only calculate tax if we have a billing account and items\n if (!billingAccount || subtotal <= 0) {\n // Reset tax if no billing account or empty cart\n setTaxAmount(0);\n setTaxRate(0);\n setTaxType('NONE');\n return;\n }\n\n // Skip if paying with credits (no tax on credit payments)\n if (selectedProvider === 'CREDITS') {\n setTaxAmount(0);\n setTaxRate(0);\n setTaxType('NONE');\n return;\n }\n\n // Only apply tax for INR payments\n if (selectedCurrency !== 'INR') {\n setTaxAmount(0);\n setTaxRate(0);\n setTaxType('NONE');\n return;\n }\n\n // For INR: Calculate 18% GST on the USD amount, then we'll convert for display\n const afterDiscount = subtotal - discountAmount;\n const GST_RATE = 0.18;\n const taxInUSD = afterDiscount * GST_RATE;\n\n setTaxAmount(taxInUSD);\n setTaxRate(GST_RATE);\n setTaxType('GST');\n }, [billingAccount, subtotal, discountAmount, selectedCurrency, selectedProvider]);\n\n // Actions\n const addItem = useCallback((item: CheckoutItem) => {\n setItems((prev) => {\n // Plans can only be purchased once - quantity is always 1\n if (item.type === 'PLAN') {\n // Check if any plan already exists in cart\n const existingPlan = prev.find((i) => i.type === 'PLAN');\n if (existingPlan) {\n // Replace existing plan with new one (can only have 1 plan)\n return prev.map((i) => (i.type === 'PLAN' ? { ...item, quantity: 1 } : i));\n }\n // Add new plan with quantity forced to 1\n return [...prev, { ...item, quantity: 1 }];\n }\n\n // For addons and credits, allow quantity increment\n const existing = prev.find((i) => i.id === item.id);\n if (existing) {\n return prev.map((i) =>\n i.id === item.id ? { ...i, quantity: i.quantity + item.quantity } : i\n );\n }\n return [...prev, item];\n });\n }, []);\n\n const removeItem = useCallback((itemId: string) => {\n setItems((prev) => prev.filter((i) => i.id !== itemId));\n }, []);\n\n const updateItemQuantity = useCallback(\n (itemId: string, quantity: number) => {\n if (quantity <= 0) {\n removeItem(itemId);\n return;\n }\n setItems((prev) =>\n prev.map((i) => {\n if (i.id !== itemId) return i;\n // Plans can only have quantity 1\n if (i.type === 'PLAN') {\n return { ...i, quantity: 1 };\n }\n return { ...i, quantity };\n })\n );\n },\n [removeItem]\n );\n\n const updateItemPrice = useCallback((itemId: string, price: number, currency?: Currency) => {\n setItems((prev) =>\n prev.map((i) => {\n if (i.id !== itemId) return i;\n // Update price and optionally currency\n const updates: Partial<CheckoutItem> = { price };\n if (currency) {\n updates.currency = currency;\n }\n return { ...i, ...updates };\n })\n );\n }, []);\n\n const clearItems = useCallback(() => {\n setItems([]);\n }, []);\n\n const handleSetBillingAccount = useCallback((account: CheckoutBillingAccount | null) => {\n setBillingAccount(account);\n // Reset credits usage when billing account changes — different accounts\n // carry different credit balances, so a CREDITS selection made against\n // the previous account may no longer be valid (or even offered) for\n // this one. Without also resetting the provider, the order summary kept\n // displaying amounts in Credits (and checkout could still submit with\n // provider: 'CREDITS') even after the Credits option had disappeared\n // from the payment method list for the new account.\n setUseCredits(false);\n setCreditsToUse(0);\n setSelectedProvider((prev) => (prev === 'CREDITS' ? 'AUTO' : prev));\n }, []);\n\n const handleSetCurrency = useCallback(\n (currency: Currency) => {\n setSelectedCurrency(currency);\n // Skip conversion if items already have localized prices\n // (Plans/Addons with multi-currency pricing don't need conversion)\n const allItemsHaveLocalizedPrices = items.every(\n (item) => item.currency === currency || item.type === 'CREDITS'\n );\n\n // Only fetch conversion rate if items don't have localized prices\n if (total > 0 && nativeCurrency !== currency && !allItemsHaveLocalizedPrices) {\n convertCurrency({\n variables: {\n amount: total,\n toCurrency: currency,\n fromCurrency: nativeCurrency,\n },\n });\n }\n },\n [total, convertCurrency, items, nativeCurrency]\n );\n\n const handleSetPaymentProvider = useCallback(\n (provider: PaymentProvider) => {\n setSelectedProvider(provider);\n // Razorpay only supports INR\n if (provider === 'RAZORPAY' && selectedCurrency !== 'INR') {\n setSelectedCurrency('INR');\n }\n },\n [selectedCurrency]\n );\n\n const validatePromoCode = useCallback(async (): Promise<PromoCodeResult> => {\n if (!promoCode.trim()) {\n return {\n valid: false,\n code: '',\n discountType: 'PERCENTAGE',\n discountValue: 0,\n error: 'Please enter a promo code',\n };\n }\n\n try {\n setCheckoutState('loading');\n\n // Build validation input\n const validationInput = {\n code: promoCode.trim().toUpperCase(),\n userId: currentUser?.id ?? '',\n userEmail: currentUser?.email,\n amount: subtotal,\n currency: selectedCurrency,\n };\n\n const { data, error: queryError } = await validateCoupon({\n variables: {\n input: validationInput,\n },\n });\n\n if (queryError || !data?.validateCoupon) {\n setPromoDiscount(0);\n setCheckoutState('idle');\n return {\n valid: false,\n code: promoCode,\n discountType: 'PERCENTAGE',\n discountValue: 0,\n error: 'Failed to validate coupon',\n };\n }\n\n const response = data.validateCoupon;\n\n if (!response.valid) {\n setPromoDiscount(0);\n setCheckoutState('idle');\n return {\n valid: false,\n code: promoCode,\n discountType: 'PERCENTAGE',\n discountValue: 0,\n error: response.error || 'Invalid coupon code',\n };\n }\n\n // Extract discount details from coupon\n const coupon = response.coupon;\n if (!coupon) {\n setPromoDiscount(0);\n setCheckoutState('idle');\n return {\n valid: false,\n code: promoCode,\n discountType: 'PERCENTAGE',\n discountValue: 0,\n error: 'Coupon data not found',\n };\n }\n\n // Use the ACTUAL discount amount from validation (includes caps and currency conversion)\n const actualDiscountAmount = response.discountAmount || 0;\n\n const discountType = coupon.type === 'FIXED_AMOUNT' ? 'FIXED' : 'PERCENTAGE';\n setPromoDiscount(actualDiscountAmount); // Use actual discount, not coupon.value\n setPromoDiscountType(discountType);\n setCheckoutState('idle');\n\n return {\n valid: true,\n code: coupon.code,\n discountType,\n discountValue: actualDiscountAmount, // Use actual discount, not coupon.value\n };\n } catch {\n setPromoDiscount(0);\n setCheckoutState('idle');\n return {\n valid: false,\n code: promoCode,\n discountType: 'PERCENTAGE',\n discountValue: 0,\n error: 'Failed to validate coupon',\n };\n }\n }, [currentUser?.email, currentUser?.id, promoCode, selectedCurrency, subtotal, validateCoupon]);\n\n const clearPromoCode = useCallback(() => {\n setPromoCode('');\n setPromoDiscount(0);\n setPromoDiscountType('PERCENTAGE');\n }, []);\n\n const handleSetCreditsToUse = useCallback(\n (amount: number) => {\n const maxCredits = Math.min(amount, availableCredits, subtotal - discountAmount);\n setCreditsToUse(Math.max(0, maxCredits));\n },\n [availableCredits, subtotal, discountAmount]\n );\n\n const initiatePayment = useCallback(\n async (): Promise<PaymentInitResponse> => {\n if (!billingAccount) {\n return {\n success: false,\n provider: 'AUTO',\n subtotal: 0,\n taxAmount: 0,\n taxRate: 0,\n total: 0,\n taxType: 'NONE',\n jurisdiction: 'UNKNOWN',\n currency: selectedCurrency,\n error: 'Please select a billing account',\n };\n }\n\n if (items.length === 0) {\n return {\n success: false,\n provider: 'AUTO',\n subtotal: 0,\n taxAmount: 0,\n taxRate: 0,\n total: 0,\n taxType: 'NONE',\n jurisdiction: 'UNKNOWN',\n currency: selectedCurrency,\n error: 'No items in checkout',\n };\n }\n\n try {\n setCheckoutState('processing');\n setError(null);\n\n // Determine purchase type from first item\n const firstItem = items[0];\n let purchaseType: PurchaseType = firstItem.type;\n\n // Check if this is a store order checkout (detect early)\n const storeOrderItem = items.find((item) => item.metadata?.storeOrderId);\n const storeOrderId = storeOrderItem?.metadata?.storeOrderId as string | undefined;\n let billingOrderId =\n (items.find((item) => item.metadata?.billingOrderId)?.metadata?.billingOrderId as\n string | undefined) || undefined;\n\n // For store orders, use 'ORDER' purchase type\n if (storeOrderId) {\n purchaseType = 'ORDER' as PurchaseType;\n }\n\n // Calculate amount to charge\n const chargeAmount = subtotal - discountAmount;\n\n // Determine provider\n let provider = selectedProvider;\n if (provider === 'AUTO') {\n provider = selectedCurrency === 'INR' ? 'RAZORPAY' : 'STRIPE';\n }\n\n // If this is a store order without a billing order yet, let the store service\n // create it from its authoritative order snapshot. This persists the billing\n // order ID and registers the signed callback on the same service that owns\n // the order; browser line-item prices and shipping values are never sent to\n // billing as the order snapshot.\n if (purchaseType === 'ORDER' && !billingOrderId && storeOrderId && billingAccount) {\n const { data: billingOrderResult } = await ensureStoreOrderBillingOrderMutation({\n variables: {\n storeOrderId,\n billingAccountId: billingAccount.id,\n currency: selectedCurrency,\n },\n });\n const ensureResult = billingOrderResult?.ensureStoreOrderBillingOrder;\n\n if (ensureResult?.success && ensureResult.billingOrderId) {\n billingOrderId = ensureResult.billingOrderId;\n } else {\n const errMsg = ensureResult?.error ?? 'Failed to create billing order';\n setCheckoutState('error');\n setError(errMsg);\n return {\n success: false,\n provider,\n subtotal: chargeAmount,\n taxAmount: 0,\n taxRate: 0,\n total: chargeAmount,\n taxType: 'NONE',\n jurisdiction: 'UNKNOWN',\n currency: selectedCurrency,\n error: errMsg,\n };\n }\n }\n\n // ========================================\n // CREDITS PAYMENT - use spendCredits mutation\n // Supports multiple addons\n // ========================================\n if (provider === 'CREDITS') {\n const addonItems = items.filter((item) => item.type === 'ADDON');\n const planItem = items.find((item) => item.type === 'PLAN');\n\n const { data } = await spendCreditsMutation({\n variables: {\n input: {\n billingAccountId: billingAccount.id,\n planId: planItem ? (planItem.metadata?.planId as string) || planItem.id : undefined,\n // Multiple addons supported via addOnIds array\n addOnIds:\n addonItems.length > 0\n ? addonItems.map((item) => ({\n id: (item.metadata?.addonId as string) || item.id,\n quantity: item.quantity,\n }))\n : undefined,\n isAnnual: planItem?.metadata?.duration === 'yearly',\n // Include upgrade context if changing plans\n upgradeFromSubscriptionId: upgradeFromSubscriptionId || undefined,\n // Include downgrade context if downgrading\n downgradeFromSubscriptionId: downgradeFromSubscriptionId || undefined,\n // For addon purchases - subscription to attach to\n subscriptionId: addonItems.length > 0 ? selectedSubscriptionId : undefined,\n // For PER_SEAT plans - pass seat count for credit calculation + subscription storage\n seatCount: planItem?.metadata?.seatCount as number | undefined,\n },\n },\n });\n\n const response = data?.spendCredits;\n\n if (!response?.creditTransactionID) {\n setCheckoutState('error');\n setError('Failed to process credit payment');\n return {\n success: false,\n provider: 'CREDITS',\n subtotal: chargeAmount,\n taxAmount: 0,\n taxRate: 0,\n total: chargeAmount,\n taxType: 'NONE',\n jurisdiction: 'UNKNOWN',\n currency: selectedCurrency,\n error: 'Failed to process credit payment',\n };\n }\n\n setCheckoutState('success');\n return {\n success: true,\n provider: 'CREDITS',\n subtotal: chargeAmount,\n taxAmount: 0,\n taxRate: 0,\n total: chargeAmount,\n taxType: 'NONE',\n jurisdiction: 'UNKNOWN',\n currency: selectedCurrency,\n // Return transaction ID for success handling\n orderId: response.creditTransactionID,\n };\n }\n\n // ========================================\n // CARD PAYMENT (Stripe/Razorpay) - use initiatePayment\n // Supports single or multiple addons\n // Supports store orders (via orderId parameter)\n // ========================================\n const addonItems = items.filter((item) => item.type === 'ADDON');\n\n // Build addon IDs and quantities for the mutation\n const addonIds =\n addonItems.length > 0\n ? addonItems.map((item) => (item.metadata?.addonId as string) || item.id)\n : undefined;\n const quantities =\n addonItems.length > 0 ? addonItems.map((item) => item.quantity) : undefined;\n\n // Determine if this is an annual plan purchase\n const planItem = items.find((item) => item.type === 'PLAN');\n const isAnnualPlan = planItem?.metadata?.duration === 'yearly';\n\n // Determine auto-renew payment method\n const autoRenewPMId =\n purchaseType === 'PLAN' && autoRenew\n ? autoRenewPaymentMethodId || selectedPaymentMethodId || undefined\n : undefined;\n\n const { data } = await initiatePaymentMutation({\n variables: {\n input: {\n billingAccountId: billingAccount.id,\n amount: chargeAmount, // Note: Backend IGNORES this and calculates from DB\n currency: selectedCurrency,\n purchaseType,\n provider: provider,\n // Store order - use orderId parameter (billing order ID)\n orderId: billingOrderId || undefined,\n creditAmount: purchaseType === 'CREDITS' ? firstItem.quantity : undefined,\n planId:\n purchaseType === 'PLAN'\n ? (firstItem.metadata?.planId as string) || firstItem.id\n : undefined,\n // Single addon (backwards compatible) - skip if store order\n addonId:\n purchaseType === 'ADDON' && addonItems.length === 1 && !billingOrderId\n ? (addonItems[0].metadata?.addonId as string) || addonItems[0].id\n : undefined,\n quantity:\n purchaseType === 'ADDON' && addonItems.length === 1 && !billingOrderId\n ? addonItems[0].quantity\n : undefined,\n // Multiple addons (cart checkout) - skip if store order\n addonIds:\n purchaseType === 'ADDON' && addonItems.length > 1 && !billingOrderId\n ? addonIds\n : undefined,\n quantities:\n purchaseType === 'ADDON' && addonItems.length > 1 && !billingOrderId\n ? quantities\n : undefined,\n // For addon purchases - subscription to attach to (skip for store orders)\n subscriptionId:\n purchaseType === 'ADDON' && !billingOrderId ? selectedSubscriptionId : undefined,\n // Saved card support\n paymentMethodId: selectedPaymentMethodId || undefined,\n // Save card if: user checked save card OR auto-renewal is enabled (which requires a saved card)\n // Only save if not using existing card (selectedPaymentMethodId is null)\n saveCard: !selectedPaymentMethodId ? saveCard || autoRenew : undefined,\n // Include upgrade context if changing plans\n upgradeFromSubscriptionId: upgradeFromSubscriptionId || undefined,\n // Include downgrade context if downgrading\n downgradeFromSubscriptionId: downgradeFromSubscriptionId || undefined,\n // Annual plan flag (for pricing calculation)\n isAnnual: isAnnualPlan || undefined,\n // Promo code (validated and applied on backend)\n promoCode: promoCode || undefined,\n // Auto-renewal (for plan purchases only)\n autoRenew: purchaseType === 'PLAN' && autoRenew ? true : undefined,\n autoRenewPaymentMethodId: autoRenewPMId,\n },\n },\n });\n\n const response = data?.initiatePayment;\n\n if (!response?.success) {\n setCheckoutState('error');\n setError(response?.error ?? 'Payment initiation failed');\n return {\n success: false,\n provider: provider,\n subtotal: chargeAmount,\n taxAmount: 0,\n taxRate: 0,\n total: chargeAmount,\n taxType: 'NONE',\n jurisdiction: 'UNKNOWN',\n currency: selectedCurrency,\n error: response?.error ?? 'Payment initiation failed',\n };\n }\n\n // Note: Tax is already calculated upfront in the effect, don't overwrite\n // The response tax is in converted currency which would mess up display\n setCheckoutState('success');\n\n return response;\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : 'An error occurred';\n setCheckoutState('error');\n setError(errorMessage);\n return {\n success: false,\n provider: 'AUTO',\n subtotal: 0,\n taxAmount: 0,\n taxRate: 0,\n total: 0,\n taxType: 'NONE',\n jurisdiction: 'UNKNOWN',\n currency: selectedCurrency,\n error: errorMessage,\n };\n }\n },\n [\n billingAccount,\n items,\n selectedCurrency,\n selectedProvider,\n subtotal,\n discountAmount,\n promoCode,\n selectedPaymentMethodId,\n saveCard,\n autoRenew,\n autoRenewPaymentMethodId,\n selectedSubscriptionId,\n upgradeFromSubscriptionId,\n downgradeFromSubscriptionId,\n initiatePaymentMutation,\n ensureStoreOrderBillingOrderMutation,\n spendCreditsMutation,\n ]\n );\n\n const reset = useCallback(() => {\n setItems([]);\n setBillingAccount(null);\n setSelectedProvider('AUTO');\n setSelectedCurrency(defaultCurrency);\n setPromoCode('');\n setPromoDiscount(0);\n setPromoDiscountType('PERCENTAGE');\n setUseCredits(false);\n setCreditsToUse(0);\n setCheckoutState('idle');\n setError(null);\n setTaxAmount(0);\n setTaxRate(0);\n setTaxType('NONE');\n setSelectedPaymentMethodId(null);\n setSaveCard(defaultSaveCard);\n setAutoRenew(false);\n setAutoRenewPaymentMethodId(null);\n setSelectedSubscriptionId(defaultSubscriptionId ?? null);\n }, [defaultCurrency, defaultSaveCard, defaultSubscriptionId]);\n\n return {\n // State\n items,\n billingAccount,\n selectedProvider,\n selectedCurrency,\n promoCode,\n promoDiscount,\n promoDiscountType,\n useCredits,\n creditsToUse,\n checkoutState,\n error,\n\n // Saved card state\n selectedPaymentMethodId,\n saveCard,\n\n // Auto-pay state\n autoRenew,\n autoRenewPaymentMethodId,\n\n // Subscription state (for addon purchases)\n selectedSubscriptionId,\n\n // Computed\n subtotal,\n subtotalUSD,\n discountAmount,\n taxAmount,\n taxRate,\n taxType,\n total,\n totalInSelectedCurrency,\n availableCredits,\n hasEnoughCredits,\n canPayWithCredits,\n isLoading,\n isProcessing,\n isCalculatingTax: false, // Tax is calculated synchronously now\n conversionRate,\n convertedAmount,\n\n // Actions\n addItem,\n removeItem,\n updateItemQuantity,\n updateItemPrice,\n clearItems,\n setBillingAccount: handleSetBillingAccount,\n setPaymentProvider: handleSetPaymentProvider,\n setCurrency: handleSetCurrency,\n setPromoCode,\n validatePromoCode,\n clearPromoCode,\n setUseCredits,\n setCreditsToUse: handleSetCreditsToUse,\n setSelectedPaymentMethodId,\n setSaveCard,\n setAutoRenew,\n setAutoRenewPaymentMethodId,\n setSelectedSubscriptionId,\n initiatePayment,\n reset,\n };\n}\n"],"mappings":"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"}
|