@nextblock-cms/ecom 0.10.2 → 0.10.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/CustomerProfileForm.cjs.js +1 -1
- package/lib/components/CustomerProfileForm.es.js +162 -159
- package/lib/freemius-coupons.cjs.js +1 -1
- package/lib/freemius-coupons.es.js +51 -49
- package/lib/pages/cms/payments/PaymentsClient.cjs.js +1 -1
- package/lib/pages/cms/payments/PaymentsClient.d.ts +4 -1
- package/lib/pages/cms/payments/PaymentsClient.es.js +255 -127
- package/lib/pages/cms/payments/PaymentsPage.cjs.js +1 -1
- package/lib/pages/cms/payments/PaymentsPage.es.js +20 -16
- package/lib/pages/cms/payments/actions.cjs.js +1 -1
- package/lib/pages/cms/payments/actions.d.ts +1 -0
- package/lib/pages/cms/payments/actions.es.js +41 -13
- package/lib/pages/cms/payments/queries.cjs.js +1 -1
- package/lib/pages/cms/payments/queries.es.js +13 -24
- package/lib/payment-config.cjs.js +1 -0
- package/lib/payment-config.d.ts +57 -0
- package/lib/payment-config.es.js +137 -0
- package/lib/providers/freemius.cjs.js +2 -2
- package/lib/providers/freemius.es.js +108 -107
- package/lib/providers/stripe.cjs.js +1 -1
- package/lib/providers/stripe.es.js +167 -167
- package/lib/stripe/checkout.cjs.js +1 -1
- package/lib/stripe/checkout.es.js +14 -14
- package/lib/stripe/client.cjs.js +1 -1
- package/lib/stripe/client.d.ts +1 -1
- package/lib/stripe/client.es.js +8 -5
- package/lib/stripe/order-sync.cjs.js +1 -1
- package/lib/stripe/order-sync.es.js +50 -50
- package/lib/stripe/webhooks.cjs.js +1 -1
- package/lib/stripe/webhooks.es.js +12 -10
- package/package.json +4 -4
- package/server.cjs.js +1 -1
- package/server.d.ts +1 -0
- package/server.es.js +188 -178
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { createClient as H } from "@supabase/supabase-js";
|
|
2
2
|
import Q from "crypto";
|
|
3
3
|
import { Freemius as te } from "@freemius/sdk";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
4
|
+
import { hydrateFreemiusEnvFromDb as ne } from "../payment-config.es.js";
|
|
5
|
+
import { normalizeOrderCustomerDetails as ie } from "../customer.es.js";
|
|
6
|
+
import { getCouponQuote as se, recordCouponRedemption as oe } from "../coupon-server.es.js";
|
|
7
|
+
import { upsertDefaultUserAddresses as ae, fillMissingUserProfileCheckoutDetails as ce } from "../customer-addresses.es.js";
|
|
8
|
+
import { getDefaultCurrency as ue, resolveEffectivePriceForCurrency as le, isSaleWindowActive as z } from "../currency.es.js";
|
|
8
9
|
function o(e) {
|
|
9
10
|
const r = process.env[e];
|
|
10
11
|
if (!r)
|
|
@@ -12,7 +13,7 @@ function o(e) {
|
|
|
12
13
|
const t = r.trim();
|
|
13
14
|
return t.startsWith('"') && t.endsWith('"') || t.startsWith("'") && t.endsWith("'") ? t.slice(1, -1).trim() : t;
|
|
14
15
|
}
|
|
15
|
-
function
|
|
16
|
+
function de(e) {
|
|
16
17
|
const r = e?.trim();
|
|
17
18
|
if (!r)
|
|
18
19
|
return {
|
|
@@ -31,16 +32,16 @@ function G(e) {
|
|
|
31
32
|
const r = Number(e);
|
|
32
33
|
return !Number.isFinite(r) || r <= 0 ? 0 : Math.round(r);
|
|
33
34
|
}
|
|
34
|
-
function
|
|
35
|
+
function pe(e) {
|
|
35
36
|
return e === !0 || e === 1 || e === "1" || e === "true";
|
|
36
37
|
}
|
|
37
|
-
function
|
|
38
|
+
function _e(e, r) {
|
|
38
39
|
const t = e?.[r];
|
|
39
40
|
return Array.isArray(t) ? t : t && typeof t == "object" ? [t] : [];
|
|
40
41
|
}
|
|
41
42
|
function X(e, r) {
|
|
42
43
|
for (const t of r) {
|
|
43
|
-
const i =
|
|
44
|
+
const i = _e(e, t);
|
|
44
45
|
if (i.length > 0)
|
|
45
46
|
return i;
|
|
46
47
|
}
|
|
@@ -52,16 +53,16 @@ function D(e) {
|
|
|
52
53
|
const r = Number(e);
|
|
53
54
|
return Number.isFinite(r) ? r : null;
|
|
54
55
|
}
|
|
55
|
-
function
|
|
56
|
+
function me(e) {
|
|
56
57
|
return e === null ? 0 : e > 5e3 ? (console.warn(
|
|
57
58
|
`[Freemius Sync] Suspiciously high price detected: ${e}. Assuming it is already in cents.`
|
|
58
59
|
), Math.round(e)) : Math.round(e * 100);
|
|
59
60
|
}
|
|
60
|
-
function
|
|
61
|
+
function fe(e) {
|
|
61
62
|
const r = Number(e);
|
|
62
63
|
return !Number.isFinite(r) || r < 1 ? 1 : Math.round(r);
|
|
63
64
|
}
|
|
64
|
-
function
|
|
65
|
+
function Ee() {
|
|
65
66
|
const e = o("FREEMIUS_CHECKOUT_PRODUCTS_JSON");
|
|
66
67
|
if (!e)
|
|
67
68
|
return null;
|
|
@@ -75,8 +76,8 @@ function fe() {
|
|
|
75
76
|
), null;
|
|
76
77
|
}
|
|
77
78
|
}
|
|
78
|
-
function
|
|
79
|
-
const r =
|
|
79
|
+
function ge(e) {
|
|
80
|
+
const r = Ee(), t = String(e), i = r?.[t], a = o("FREEMIUS_PRODUCT_ID"), d = o(
|
|
80
81
|
"FREEMIUS_ECOMMERCE_SANDBOX_PUBLIC_KEY"
|
|
81
82
|
), f = o(
|
|
82
83
|
"FREEMIUS_ECOMMERCE_SANDBOX_SECRET_KEY"
|
|
@@ -86,12 +87,12 @@ function Ee(e) {
|
|
|
86
87
|
secretKey: i.secretKey ?? null,
|
|
87
88
|
apiKey: i.apiKey ?? null,
|
|
88
89
|
source: "product-map"
|
|
89
|
-
} : process.env.FREEMIUS_SANDBOX_ENABLED === "true" &&
|
|
90
|
+
} : process.env.FREEMIUS_SANDBOX_ENABLED === "true" && a && a === t && d ? {
|
|
90
91
|
publicKey: d,
|
|
91
92
|
secretKey: f,
|
|
92
93
|
apiKey: o("FREEMIUS_API_KEY"),
|
|
93
94
|
source: "single-product-sandbox-env"
|
|
94
|
-
} :
|
|
95
|
+
} : a && a === t && o("FREEMIUS_PUBLIC_KEY") ? {
|
|
95
96
|
publicKey: o("FREEMIUS_PUBLIC_KEY"),
|
|
96
97
|
secretKey: o("FREEMIUS_SECRET_KEY"),
|
|
97
98
|
apiKey: o("FREEMIUS_API_KEY"),
|
|
@@ -103,7 +104,7 @@ function Ee(e) {
|
|
|
103
104
|
source: "legacy-env"
|
|
104
105
|
};
|
|
105
106
|
}
|
|
106
|
-
async function
|
|
107
|
+
async function Se(e) {
|
|
107
108
|
if (!e.apiKey)
|
|
108
109
|
throw new Error("Missing Freemius API key for SDK sandbox generation.");
|
|
109
110
|
return new te({
|
|
@@ -113,7 +114,7 @@ async function ge(e) {
|
|
|
113
114
|
publicKey: e.publicKey
|
|
114
115
|
}).checkout.getSandboxParams();
|
|
115
116
|
}
|
|
116
|
-
class
|
|
117
|
+
class we {
|
|
117
118
|
getProviderName() {
|
|
118
119
|
return "Freemius";
|
|
119
120
|
}
|
|
@@ -121,17 +122,17 @@ class Ue {
|
|
|
121
122
|
items: r,
|
|
122
123
|
customerEmail: t,
|
|
123
124
|
customerPhone: i,
|
|
124
|
-
userId:
|
|
125
|
+
userId: a,
|
|
125
126
|
billingAddress: d,
|
|
126
127
|
shippingAddress: f,
|
|
127
128
|
currencyCode: m,
|
|
128
129
|
couponCode: K,
|
|
129
130
|
couponContextItems: E
|
|
130
131
|
}) {
|
|
131
|
-
const
|
|
132
|
-
if (!
|
|
132
|
+
const c = process.env.NEXT_PUBLIC_SUPABASE_URL || process.env.SUPABASE_URL, u = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.SUPABASE_SECRET_KEY;
|
|
133
|
+
if (!c || !u)
|
|
133
134
|
return { error: "Missing Supabase credentials for checkout (Service Key required).", url: null };
|
|
134
|
-
const p = H(
|
|
135
|
+
const p = H(c, u);
|
|
135
136
|
if (!r || r.length === 0)
|
|
136
137
|
return { error: "Cart is empty", url: null };
|
|
137
138
|
if (r.length !== 1)
|
|
@@ -141,11 +142,11 @@ class Ue {
|
|
|
141
142
|
).eq("is_active", !0).order("code", { ascending: !0 }), v = g ?? [];
|
|
142
143
|
if (U || v.length === 0)
|
|
143
144
|
return { error: "Failed to resolve store currencies", url: null };
|
|
144
|
-
const j =
|
|
145
|
+
const j = ue(v), b = v.find((l) => l.code === (m || "").toUpperCase()) ?? j, S = r[0], { data: s, error: I } = await p.from("products").select("id, title, price, prices, sale_price, sale_prices, sale_start_at, sale_end_at, scheduled_price, scheduled_prices, scheduled_price_at, freemius_plan_id, freemius_product_id, trial_period_days, trial_requires_payment_method").eq("id", S.product_id).single();
|
|
145
146
|
if (I || !s)
|
|
146
147
|
return { error: "Product not found", url: null };
|
|
147
|
-
const
|
|
148
|
-
if (!
|
|
148
|
+
const R = s.freemius_plan_id, P = s.freemius_product_id;
|
|
149
|
+
if (!R || !P)
|
|
149
150
|
return { error: "Product is not configured for Freemius checkout (missing Plan ID or Product ID)", url: null };
|
|
150
151
|
const k = le({
|
|
151
152
|
prices: s.prices || {},
|
|
@@ -165,25 +166,25 @@ class Ue {
|
|
|
165
166
|
}), y = N * S.quantity;
|
|
166
167
|
let n = null, _ = 0;
|
|
167
168
|
if (K) {
|
|
168
|
-
const
|
|
169
|
+
const l = await se({
|
|
169
170
|
client: p,
|
|
170
171
|
code: K,
|
|
171
172
|
items: E && E.length > 0 ? E : r,
|
|
172
173
|
currencyCode: b.code
|
|
173
174
|
});
|
|
174
|
-
if (!
|
|
175
|
+
if (!l.success)
|
|
175
176
|
return {
|
|
176
|
-
error:
|
|
177
|
-
errorKey:
|
|
177
|
+
error: l.error,
|
|
178
|
+
errorKey: l.errorKey,
|
|
178
179
|
errorStatus: 400,
|
|
179
180
|
url: null
|
|
180
181
|
};
|
|
181
|
-
n =
|
|
182
|
+
n = l.quote, _ = Math.min(
|
|
182
183
|
y,
|
|
183
184
|
n.lineDiscounts.filter(($) => $.product_id === s.id).reduce(($, T) => $ + T.discount, 0)
|
|
184
185
|
);
|
|
185
186
|
}
|
|
186
|
-
const F = G(s.trial_period_days),
|
|
187
|
+
const F = G(s.trial_period_days), w = F > 0 ? S.trial_preference ? S.trial_preference : s.trial_requires_payment_method ? "paid" : "free" : null, B = "pending", h = de(
|
|
187
188
|
d?.recipient_name ?? null
|
|
188
189
|
), { data: Y, error: V } = await p.from("orders").insert({
|
|
189
190
|
status: B,
|
|
@@ -205,9 +206,9 @@ class Ue {
|
|
|
205
206
|
} : null,
|
|
206
207
|
provider: "freemius",
|
|
207
208
|
freemius_product_id: String(P),
|
|
208
|
-
freemius_plan_id: String(
|
|
209
|
-
user_id:
|
|
210
|
-
customer_details:
|
|
209
|
+
freemius_plan_id: String(R),
|
|
210
|
+
user_id: a || null,
|
|
211
|
+
customer_details: ie({
|
|
211
212
|
email: t,
|
|
212
213
|
phone: i,
|
|
213
214
|
name: d?.recipient_name,
|
|
@@ -223,49 +224,49 @@ class Ue {
|
|
|
223
224
|
quantity: S.quantity,
|
|
224
225
|
price_at_purchase: N
|
|
225
226
|
}]);
|
|
226
|
-
if (J && console.error("Failed to insert order items:", J),
|
|
227
|
+
if (J && console.error("Failed to insert order items:", J), a)
|
|
227
228
|
try {
|
|
228
|
-
await
|
|
229
|
-
userId:
|
|
229
|
+
await ae({
|
|
230
|
+
userId: a,
|
|
230
231
|
billingAddress: d,
|
|
231
232
|
shippingAddress: f,
|
|
232
233
|
client: p
|
|
233
234
|
}), await ce({
|
|
234
|
-
userId:
|
|
235
|
+
userId: a,
|
|
235
236
|
fullName: d?.recipient_name ?? f?.recipient_name ?? null,
|
|
236
237
|
phone: i,
|
|
237
238
|
client: p
|
|
238
239
|
});
|
|
239
|
-
} catch (
|
|
240
|
+
} catch (l) {
|
|
240
241
|
console.error(
|
|
241
242
|
"Failed to sync checkout profile defaults before checkout:",
|
|
242
|
-
|
|
243
|
+
l
|
|
243
244
|
);
|
|
244
245
|
}
|
|
245
|
-
n && await
|
|
246
|
+
n && await oe({
|
|
246
247
|
client: p,
|
|
247
248
|
quote: n,
|
|
248
249
|
orderId: Y.id,
|
|
249
250
|
provider: "freemius",
|
|
250
251
|
discountTotal: _,
|
|
251
|
-
userId:
|
|
252
|
+
userId: a,
|
|
252
253
|
customerEmail: t,
|
|
253
254
|
metadata: {
|
|
254
255
|
currency: b.code,
|
|
255
256
|
subtotal: y,
|
|
256
257
|
final_amount_owned_by: "freemius"
|
|
257
258
|
}
|
|
258
|
-
});
|
|
259
|
-
const O = process.env.FREEMIUS_SANDBOX_ENABLED === "true", M =
|
|
259
|
+
}), await ne();
|
|
260
|
+
const O = process.env.FREEMIUS_SANDBOX_ENABLED === "true", M = ge(P), A = M.publicKey, L = M.secretKey, W = M.apiKey;
|
|
260
261
|
if (!A || O && !L)
|
|
261
262
|
return { error: "Missing FREEMIUS credentials (PUBLIC_KEY or SECRET_KEY) in environment variables.", url: null };
|
|
262
263
|
if (O && M.source === "legacy-env") {
|
|
263
|
-
const
|
|
264
|
+
const l = o("FREEMIUS_PRODUCT_ID"), $ = !!o("FREEMIUS_ECOMMERCE_SANDBOX_PUBLIC_KEY"), T = !!o("FREEMIUS_ECOMMERCE_SANDBOX_SECRET_KEY");
|
|
264
265
|
console.warn(
|
|
265
266
|
`[Freemius Checkout] Sandbox is enabled for product ${P}, but no product-scoped checkout credentials were selected. Falling back to legacy FREEMIUS_PUBLIC_KEY/FREEMIUS_SECRET_KEY may open live checkout instead of sandbox.`,
|
|
266
267
|
{
|
|
267
|
-
configuredProductId:
|
|
268
|
-
productIdsMatch:
|
|
268
|
+
configuredProductId: l,
|
|
269
|
+
productIdsMatch: l === String(P),
|
|
269
270
|
hasSandboxOverridePublicKey: $,
|
|
270
271
|
hasSandboxOverrideSecretKey: T,
|
|
271
272
|
hasCheckoutProductsJson: !!o("FREEMIUS_CHECKOUT_PRODUCTS_JSON")
|
|
@@ -275,16 +276,16 @@ class Ue {
|
|
|
275
276
|
let q = !1;
|
|
276
277
|
if (O && L && A)
|
|
277
278
|
try {
|
|
278
|
-
q = await
|
|
279
|
+
q = await Se({
|
|
279
280
|
productId: P,
|
|
280
281
|
publicKey: A,
|
|
281
282
|
secretKey: L,
|
|
282
283
|
apiKey: W
|
|
283
284
|
});
|
|
284
|
-
} catch (
|
|
285
|
+
} catch (l) {
|
|
285
286
|
console.warn(
|
|
286
287
|
"Freemius Checkout - SDK sandbox generation failed. Falling back to manual token generation.",
|
|
287
|
-
|
|
288
|
+
l,
|
|
288
289
|
{
|
|
289
290
|
credentialSource: M.source,
|
|
290
291
|
hasApiKey: !!W
|
|
@@ -296,22 +297,22 @@ class Ue {
|
|
|
296
297
|
token: re
|
|
297
298
|
};
|
|
298
299
|
}
|
|
299
|
-
const C = new URL(`https://checkout.freemius.com/app/${P}/plan/${
|
|
300
|
-
if (O && L && A ? (C.searchParams.append("sandbox", q.token), C.searchParams.append("s_ctx_ts", q.ctx)) : O && C.searchParams.append("sandbox", "true"), t && C.searchParams.append("user_email", t), h.firstName && C.searchParams.append("user_firstname", h.firstName), h.lastName && C.searchParams.append("user_lastname", h.lastName), C.searchParams.append("currency", b.code.toLowerCase()), S.billing_cycle && C.searchParams.append("billing_cycle", S.billing_cycle),
|
|
300
|
+
const C = new URL(`https://checkout.freemius.com/app/${P}/plan/${R}/`);
|
|
301
|
+
if (O && L && A ? (C.searchParams.append("sandbox", q.token), C.searchParams.append("s_ctx_ts", q.ctx)) : O && C.searchParams.append("sandbox", "true"), t && C.searchParams.append("user_email", t), h.firstName && C.searchParams.append("user_firstname", h.firstName), h.lastName && C.searchParams.append("user_lastname", h.lastName), C.searchParams.append("currency", b.code.toLowerCase()), S.billing_cycle && C.searchParams.append("billing_cycle", S.billing_cycle), w && C.searchParams.append("trial", w), n)
|
|
301
302
|
C.searchParams.append("coupon", n.code);
|
|
302
303
|
else if (x) {
|
|
303
|
-
const { data:
|
|
304
|
-
|
|
305
|
-
saleStartAt:
|
|
306
|
-
saleEndAt:
|
|
307
|
-
}) && C.searchParams.append("coupon",
|
|
304
|
+
const { data: l } = await p.from("product_freemius_sale_coupons").select("freemius_coupon_code, is_active, starts_at, ends_at, sync_status").eq("product_id", s.id).maybeSingle();
|
|
305
|
+
l?.is_active && l.sync_status === "synced" && l.freemius_coupon_code && z({
|
|
306
|
+
saleStartAt: l.starts_at,
|
|
307
|
+
saleEndAt: l.ends_at
|
|
308
|
+
}) && C.searchParams.append("coupon", l.freemius_coupon_code);
|
|
308
309
|
}
|
|
309
310
|
return {
|
|
310
311
|
url: C.toString(),
|
|
311
312
|
customProps: {
|
|
312
313
|
provider: "freemius",
|
|
313
314
|
plugin_id: P,
|
|
314
|
-
plan_id:
|
|
315
|
+
plan_id: R,
|
|
315
316
|
public_key: A,
|
|
316
317
|
user_email: t,
|
|
317
318
|
user_firstname: h.firstName,
|
|
@@ -319,7 +320,7 @@ class Ue {
|
|
|
319
320
|
credential_source: M.source,
|
|
320
321
|
sandbox: q,
|
|
321
322
|
billing_cycle: S.billing_cycle,
|
|
322
|
-
trial:
|
|
323
|
+
trial: w,
|
|
323
324
|
trial_period_days: F,
|
|
324
325
|
trial_requires_payment_method: s.trial_requires_payment_method,
|
|
325
326
|
initial_order_status: B,
|
|
@@ -334,24 +335,24 @@ async function Z(e, r, t, i) {
|
|
|
334
335
|
|
|
335
336
|
|
|
336
337
|
${d}
|
|
337
|
-
${e}`, m = Q.createHmac("sha256", i).update(f).digest("hex"), K = Buffer.from(m).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, ""), E = `FS ${r}:${t}:${K}`,
|
|
338
|
+
${e}`, m = Q.createHmac("sha256", i).update(f).digest("hex"), K = Buffer.from(m).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, ""), E = `FS ${r}:${t}:${K}`, c = await fetch(`https://api.freemius.com${e}`, {
|
|
338
339
|
headers: {
|
|
339
340
|
Authorization: E,
|
|
340
341
|
Date: d,
|
|
341
342
|
Accept: "application/json"
|
|
342
343
|
}
|
|
343
344
|
});
|
|
344
|
-
if (!
|
|
345
|
-
const
|
|
346
|
-
throw console.error(`[Freemius API] [ERROR] ${e} returned ${
|
|
345
|
+
if (!c.ok) {
|
|
346
|
+
const u = await c.text();
|
|
347
|
+
throw console.error(`[Freemius API] [ERROR] ${e} returned ${c.status}: ${u}`), new Error(`Freemius API failed on ${e}: ${c.status} - ${u}`);
|
|
347
348
|
}
|
|
348
|
-
return
|
|
349
|
+
return c.json();
|
|
349
350
|
}
|
|
350
|
-
async function
|
|
351
|
-
const e = o("FREEMIUS_DEVELOPER_ID"), r = o("FREEMIUS_PUBLIC_KEY"), t = o("FREEMIUS_SECRET_KEY"), i = process.env.NEXT_PUBLIC_SUPABASE_URL || process.env.SUPABASE_URL,
|
|
352
|
-
if (!e || !r || !t || !i || !
|
|
351
|
+
async function Re() {
|
|
352
|
+
const e = o("FREEMIUS_DEVELOPER_ID"), r = o("FREEMIUS_PUBLIC_KEY"), t = o("FREEMIUS_SECRET_KEY"), i = process.env.NEXT_PUBLIC_SUPABASE_URL || process.env.SUPABASE_URL, a = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.SUPABASE_SECRET_KEY;
|
|
353
|
+
if (!e || !r || !t || !i || !a)
|
|
353
354
|
throw new Error("Missing necessary environment variables for Freemius Sync.");
|
|
354
|
-
const d = H(i,
|
|
355
|
+
const d = H(i, a, {
|
|
355
356
|
auth: { autoRefreshToken: !1, persistSession: !1 }
|
|
356
357
|
}), f = (m) => Z(m, e, r, t);
|
|
357
358
|
try {
|
|
@@ -359,8 +360,8 @@ async function be() {
|
|
|
359
360
|
const m = await f(`/v1/developers/${e}/plugins.json`), K = X(m, ["plugins", "plugin"]);
|
|
360
361
|
console.log(`[Freemius Sync] Found ${K.length} plugins. Syncing plans...`);
|
|
361
362
|
let E = 0;
|
|
362
|
-
const { data:
|
|
363
|
-
if (!
|
|
363
|
+
const { data: c } = await d.from("languages").select("id").eq("code", "en").single(), u = c?.id;
|
|
364
|
+
if (!u)
|
|
364
365
|
throw new Error("English language not found in database. Cannot sync products.");
|
|
365
366
|
for (const p of K) {
|
|
366
367
|
const g = p.id?.toString();
|
|
@@ -374,7 +375,7 @@ async function be() {
|
|
|
374
375
|
g,
|
|
375
376
|
p.title || p.name || `Freemius Product ${g}`,
|
|
376
377
|
f,
|
|
377
|
-
|
|
378
|
+
u
|
|
378
379
|
);
|
|
379
380
|
E += U;
|
|
380
381
|
}
|
|
@@ -383,88 +384,88 @@ async function be() {
|
|
|
383
384
|
throw console.error("[Freemius Sync] Global Error:", m), m;
|
|
384
385
|
}
|
|
385
386
|
}
|
|
386
|
-
async function
|
|
387
|
-
const r = o("FREEMIUS_DEVELOPER_ID"), t = o("FREEMIUS_PUBLIC_KEY"), i = o("FREEMIUS_SECRET_KEY"),
|
|
388
|
-
if (!r || !t || !i || !
|
|
387
|
+
async function $e(e) {
|
|
388
|
+
const r = o("FREEMIUS_DEVELOPER_ID"), t = o("FREEMIUS_PUBLIC_KEY"), i = o("FREEMIUS_SECRET_KEY"), a = process.env.NEXT_PUBLIC_SUPABASE_URL || process.env.SUPABASE_URL, d = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.SUPABASE_SECRET_KEY;
|
|
389
|
+
if (!r || !t || !i || !a || !d)
|
|
389
390
|
throw new Error("Missing environment variables for Freemius Sync.");
|
|
390
|
-
const f = H(
|
|
391
|
+
const f = H(a, d), m = (g) => Z(g, r, t, i), { data: K } = await f.from("languages").select("id").eq("code", "en").single(), E = K?.id;
|
|
391
392
|
if (!E)
|
|
392
393
|
throw new Error("English language not found in database. Cannot sync products.");
|
|
393
|
-
const
|
|
394
|
+
const c = await m(`/v1/developers/${r}/plugins/${e}.json`), u = c.plugin ?? c;
|
|
394
395
|
return { success: !0, count: await ee(
|
|
395
396
|
f,
|
|
396
397
|
r,
|
|
397
398
|
e,
|
|
398
|
-
|
|
399
|
+
u.title || u.name || `Freemius Product ${e}`,
|
|
399
400
|
m,
|
|
400
401
|
E
|
|
401
402
|
) };
|
|
402
403
|
}
|
|
403
|
-
async function ee(e, r, t, i,
|
|
404
|
+
async function ee(e, r, t, i, a, d) {
|
|
404
405
|
console.log(`[Freemius Sync] Fetching plans for plugin: ${i} (${t})...`);
|
|
405
406
|
let f = 0;
|
|
406
407
|
try {
|
|
407
|
-
const m = `/v1/developers/${r}/plugins/${t}/plans.json`, K = await
|
|
408
|
+
const m = `/v1/developers/${r}/plugins/${t}/plans.json`, K = await a(m), E = X(K, ["plans", "plan"]);
|
|
408
409
|
console.log(`[Freemius Sync] Received ${E.length} plans for plugin ${t}.`);
|
|
409
|
-
for (const
|
|
410
|
-
const
|
|
411
|
-
if (!
|
|
412
|
-
console.warn("[Freemius Sync] Skipping plan without an id:",
|
|
410
|
+
for (const c of E) {
|
|
411
|
+
const u = c.id?.toString();
|
|
412
|
+
if (!u) {
|
|
413
|
+
console.warn("[Freemius Sync] Skipping plan without an id:", c);
|
|
413
414
|
continue;
|
|
414
415
|
}
|
|
415
|
-
const p =
|
|
416
|
-
console.log(`[Freemius Sync] Processing plan: ${g} (${
|
|
417
|
-
let U =
|
|
416
|
+
const p = c.name || c.title || u, g = c.title || p;
|
|
417
|
+
console.log(`[Freemius Sync] Processing plan: ${g} (${u})...`);
|
|
418
|
+
let U = c;
|
|
418
419
|
if (U.trial_period === void 0 || U.is_require_subscription === void 0)
|
|
419
420
|
try {
|
|
420
|
-
const n = `/v1/developers/${r}/plugins/${t}/plans/${
|
|
421
|
+
const n = `/v1/developers/${r}/plugins/${t}/plans/${u}.json`, _ = await a(n), F = _.plan ?? _;
|
|
421
422
|
U = {
|
|
422
423
|
...U,
|
|
423
424
|
...F
|
|
424
425
|
};
|
|
425
426
|
} catch (n) {
|
|
426
427
|
console.warn(
|
|
427
|
-
`[Freemius Sync] Could not fetch trial details for plan ${
|
|
428
|
+
`[Freemius Sync] Could not fetch trial details for plan ${u}:`,
|
|
428
429
|
n instanceof Error ? n.message : n
|
|
429
430
|
);
|
|
430
431
|
}
|
|
431
|
-
const v = G(U.trial_period), j = v > 0 &&
|
|
432
|
+
const v = G(U.trial_period), j = v > 0 && pe(U.is_require_subscription);
|
|
432
433
|
let b = 0, S = [];
|
|
433
434
|
try {
|
|
434
|
-
const n = `/v1/developers/${r}/plugins/${t}/plans/${
|
|
435
|
+
const n = `/v1/developers/${r}/plugins/${t}/plans/${u}/pricing.json`, _ = await a(n);
|
|
435
436
|
if (S = X(_, [
|
|
436
437
|
"pricing",
|
|
437
438
|
"prices",
|
|
438
439
|
"pricings"
|
|
439
440
|
]), S.length > 0) {
|
|
440
|
-
const F = S[0],
|
|
441
|
-
b =
|
|
441
|
+
const F = S[0], w = D(F.annual_price) ?? D(F.monthly_price) ?? D(F.lifetime_price);
|
|
442
|
+
b = me(w);
|
|
442
443
|
}
|
|
443
444
|
console.log(`[Freemius Sync] Plan: ${g} -> Resolved Price (cents): ${b}`);
|
|
444
445
|
} catch (n) {
|
|
445
|
-
console.warn(`[Freemius Sync] Could not fetch pricing for plan ${
|
|
446
|
+
console.warn(`[Freemius Sync] Could not fetch pricing for plan ${u}:`, n instanceof Error ? n.message : n);
|
|
446
447
|
}
|
|
447
448
|
const s = `${i}-${g}`.toLowerCase().replace(/[^\w\s-]/g, "").replace(/[\s_]+/g, "-").replace(/^-+|-+$/g, ""), I = {
|
|
448
449
|
title: `${i} - ${g}`,
|
|
449
450
|
slug: s,
|
|
450
|
-
short_description:
|
|
451
|
+
short_description: c.description || "",
|
|
451
452
|
price: b,
|
|
452
453
|
product_type: "digital",
|
|
453
454
|
payment_provider: "freemius",
|
|
454
|
-
freemius_plan_id:
|
|
455
|
+
freemius_plan_id: u,
|
|
455
456
|
freemius_product_id: t,
|
|
456
457
|
trial_period_days: v,
|
|
457
458
|
trial_requires_payment_method: j,
|
|
458
459
|
status: "active",
|
|
459
460
|
stock: 999,
|
|
460
|
-
sku: `FM-${t}-${
|
|
461
|
+
sku: `FM-${t}-${u}`,
|
|
461
462
|
language_id: d
|
|
462
|
-
}, { data:
|
|
463
|
-
if (P || !
|
|
463
|
+
}, { data: R, error: P } = await e.from("products").upsert(I, { onConflict: "language_id, sku" }).select();
|
|
464
|
+
if (P || !R || R.length === 0) {
|
|
464
465
|
console.error(`[Freemius Sync] Error upserting product ${I.sku}:`, P);
|
|
465
466
|
continue;
|
|
466
467
|
}
|
|
467
|
-
const k =
|
|
468
|
+
const k = R[0].id, { data: N, error: x } = await e.from("freemius_plans").select("id").eq("product_id", k).eq("name", p).maybeSingle();
|
|
468
469
|
x && console.warn(
|
|
469
470
|
`[Freemius Sync] Could not check existing local plan for ${I.sku}:`,
|
|
470
471
|
x.message || x
|
|
@@ -490,12 +491,12 @@ async function ee(e, r, t, i, c, d) {
|
|
|
490
491
|
}
|
|
491
492
|
if (y && S.length > 0)
|
|
492
493
|
for (const n of S) {
|
|
493
|
-
const _ =
|
|
494
|
+
const _ = fe(
|
|
494
495
|
n.licenses ?? n.license_quota ?? n.quota
|
|
495
|
-
), { data: F, error:
|
|
496
|
-
|
|
496
|
+
), { data: F, error: w } = await e.from("freemius_pricing").select("id").eq("plan_id", y).eq("license_quota", _).maybeSingle();
|
|
497
|
+
w && console.warn(
|
|
497
498
|
`[Freemius Sync] Could not check pricing for plan ${y}, quota ${_}:`,
|
|
498
|
-
|
|
499
|
+
w.message || w
|
|
499
500
|
);
|
|
500
501
|
const B = {
|
|
501
502
|
api_monthly_price: D(n.monthly_price),
|
|
@@ -529,10 +530,10 @@ async function ee(e, r, t, i, c, d) {
|
|
|
529
530
|
return f;
|
|
530
531
|
}
|
|
531
532
|
export {
|
|
532
|
-
|
|
533
|
-
|
|
533
|
+
we as FreemiusProvider,
|
|
534
|
+
Ee as parseFreemiusCheckoutCredentialsMap,
|
|
534
535
|
o as readFreemiusEnvValue,
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
536
|
+
ge as resolveFreemiusCheckoutCredentials,
|
|
537
|
+
Re as syncFreemiusProductsToSupabase,
|
|
538
|
+
$e as syncSingleFreemiusProduct
|
|
538
539
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const U=require("../stripe/client.cjs.js"),xe=require("@supabase/supabase-js"),ke=require("../countries.cjs.js"),Pe=require("../customer.cjs.js"),O=require("../coupon-server.cjs.js"),be=require("../coupons.cjs.js"),_e=require("../customer-addresses.cjs.js"),T=require("../inventory-settings.cjs.js"),w=require("../tax-calculation.cjs.js"),h=require("../currency.cjs.js"),Te=require("../order-tax-details.cjs.js"),we=require("../types.cjs.js"),Ae=require("../variation-utils.cjs.js"),Ie=ke.countries.map(r=>r.code),De=new Set(["bg","cs","da","de","el","en","en-GB","es","es-419","et","fi","fil","fr","hr","hu","id","it","ja","ko","lt","lv","ms","mt","nb","nl","pl","pt","pt-BR","ro","ru","sk","sl","sv","th","tr","vi","zh","zh-HK","zh-TW"]);function pe(r){if(r)return{line1:r.line1||void 0,line2:r.line2||void 0,city:r.city||void 0,state:r.state||void 0,postal_code:r.postal_code||void 0,country:r.country_code||void 0}}function Me(r){const i=r?.trim().replace("_","-");if(!i)return;const l=[i,i.toLowerCase(),i.split("-")[0].toLowerCase()];for(const d of l)if(De.has(d))return d}function Ue(r,i){return Ae.resolveTranslatedText(r.name,r.name_translations||null,i)}async function Le(r){if(!r.email)return null;const i=pe(r.shippingAddress),l=r.shippingAddress?.recipient_name||r.billingAddress?.recipient_name||void 0,d={email:r.email,name:r.billingAddress?.recipient_name||r.shippingAddress?.recipient_name||void 0,phone:r.phone||void 0,address:pe(r.billingAddress),metadata:r.userId?{userId:r.userId}:void 0,...i&&l?{shipping:{name:l,phone:r.phone||void 0,address:i}}:{}};try{const s=(await U.stripe.customers.list({email:r.email,limit:1})).data[0];return s?(await U.stripe.customers.update(s.id,d),s.id):(await U.stripe.customers.create(d)).id}catch(_){return console.error("Failed to upsert Stripe customer for checkout prefill:",_),null}}class Fe{getProviderName(){return"Stripe"}async createCheckoutSession({items:i,customerEmail:l,customerPhone:d,userId:_,billingAddress:s,shippingAddress:y,shippingMethodId:B,currencyCode:me,locale:z,couponCode:K,couponContextItems:L}){const Q=process.env.NEXT_PUBLIC_SUPABASE_URL||process.env.SUPABASE_URL,$=process.env.SUPABASE_SERVICE_ROLE_KEY||process.env.SUPABASE_SECRET_KEY;if(!Q||!$)return console.error("Missing Supabase credentials for checkout (Service Key required)."),{error:"Internal Server Error",url:null};const a=xe.createClient(Q,$),X=process.env.NEXT_PUBLIC_URL||"http://localhost:4200",H=i.some(e=>!we.isDigitalItem(e));if(!i.length)return{error:"Cart is empty",url:null};const{data:he,error:j}=await a.from("currencies").select("code, symbol, exchange_rate, is_default, is_active, auto_sync_product_prices, auto_update_exchange_rate, exchange_rate_source, exchange_rate_updated_at, rounding_mode, rounding_increment, rounding_charm_amount").eq("is_active",!0).order("code",{ascending:!0}),g=he??[];if(j||g.length===0)return console.error("Error fetching currencies for checkout:",j),{error:"Failed to resolve store currencies",url:null};const G=h.getDefaultCurrency(g),p=g.find(e=>e.code===(me||"").toUpperCase())??G,F=p.code.toLowerCase(),V=await T.getEcommerceInventorySettings(a),ve=i.map(e=>e.product_id),W=i.map(e=>e.variant_id).filter(e=>!!e),{data:Y,error:J}=await a.from("products").select("id, title, sku, price, prices, sale_price, sale_prices, sale_start_at, sale_end_at, scheduled_price, scheduled_prices, scheduled_price_at, stock, is_taxable").in("id",ve);if(J||!Y)return console.error("Error fetching products for validation:",J),{error:"Failed to validate product prices",url:null};const{data:fe,error:Z}=W.length?await a.from("product_variants").select("id, product_id, sku, price, prices, sale_price, sale_prices, sale_start_at, sale_end_at, scheduled_price, scheduled_prices, scheduled_price_at, stock_quantity").in("id",W):{data:[],error:null};if(Z)return console.error("Error fetching variants for validation:",Z),{error:"Failed to validate product variants",url:null};const ee=new Map(Y.map(e=>[e.id,e])),te=new Map((fe||[]).map(e=>[e.id,e])),R=new Set,A=new Map;for(const e of i){const t=ee.get(e.product_id);if(!t)continue;const n=(e.variant_id?te.get(e.variant_id):null)?.sku||t.sku;n&&(R.add(n),A.set(n,(A.get(n)??0)+e.quantity))}const{data:ye,error:re}=R.size?await a.from("inventory_items").select("sku, quantity").in("sku",[...R]):{data:[],error:null};if(re)return console.error("Error fetching SKU inventory for validation:",re),{error:"Failed to validate SKU inventory",url:null};const I=new Map((ye||[]).map(e=>[e.sku,Math.max(0,e.quantity??0)])),S=[],oe=[],ie=[];let v=0,C=0,c=null;if(K){const e=await O.getCouponQuote({client:a,code:K,items:L&&L.length>0?L:i,currencyCode:p.code});if(!e.success)return{error:e.error,errorKey:e.errorKey,errorStatus:400,url:null};c=e.quote}const ge=O.getQuoteLineDiscountMap(c),Se=e=>{const t=e.unitAmount*e.quantity,f=Math.max(0,t-e.lineDiscount);if(f<=0)return;const n=Math.floor(f/e.quantity),P=f-n*e.quantity,x=(b,k)=>{k<=0||b<=0||S.push({price_data:{currency:e.currency,product_data:{name:e.name,tax_code:w.getStripeTaxCodeForProduct(e.isTaxable),metadata:{productId:e.productId,variantId:e.variantId||""}},tax_behavior:"exclusive",unit_amount:b},quantity:k})};x(n,e.quantity-P),x(n+1,P)};for(const e of i){const t=ee.get(e.product_id);if(!t)return console.warn(`Product ${e.product_id} not found in DB.`),{url:null,...T.createInventoryUnavailableError(e.title)};const f=h.resolveEffectivePriceForCurrency({prices:h.normalizePriceMap(t.prices),salePrices:t.sale_prices||{},fallbackPrice:t.price,fallbackSalePrice:t.sale_price,saleStartAt:t.sale_start_at,saleEndAt:t.sale_end_at,scheduledPrice:t.scheduled_price,scheduledPrices:h.normalizePriceMap(t.scheduled_prices),scheduledPriceAt:t.scheduled_price_at,currencyCode:p.code,currencies:g});let n=f.sale_price??f.price,P=t.title,x=null;if(e.variant_id){const o=te.get(e.variant_id);if(!o||o.product_id!==e.product_id)return{url:null,...T.createInventoryUnavailableError(e.title)};const M=A.get(o.sku)??e.quantity,le=I.has(o.sku)?I.get(o.sku)??0:Math.max(0,o.stock_quantity??0);if(V.trackQuantities&&M>le)return{url:null,...T.createInventoryInsufficientError(e.title,le)};const de=h.resolveEffectivePriceForCurrency({prices:h.normalizePriceMap(o.prices),salePrices:o.sale_prices||{},fallbackPrice:o.price,fallbackSalePrice:o.sale_price,saleStartAt:o.sale_start_at,saleEndAt:o.sale_end_at,scheduledPrice:o.scheduled_price,scheduledPrices:h.normalizePriceMap(o.scheduled_prices),scheduledPriceAt:o.scheduled_price_at,currencyCode:p.code,currencies:g});n=de.sale_price??de.price,x=o.id,P=e.variant_label?`${t.title} - ${e.variant_label}`:`${t.title} - ${o.sku}`}else{const o=A.get(t.sku)??e.quantity,M=I.has(t.sku)?I.get(t.sku)??0:Math.max(0,t.stock??0);if(V.trackQuantities&&o>M)return{url:null,...T.createInventoryInsufficientError(e.title,M)}}const b=t.is_taxable??!0;if(n<0)return{error:"A product variation produced an invalid price.",url:null};const k=Math.min(n*e.quantity,ge.get(be.getCartLineCouponKey(e))??0);Se({name:P,currency:F,productId:t.id,variantId:x,isTaxable:b,unitAmount:n,quantity:e.quantity,lineDiscount:k}),v+=n*e.quantity,C+=k,ie.push({product_id:t.id,quantity:e.quantity,price_at_purchase:n,variant_id:x}),oe.push({product_id:t.id,quantity:e.quantity,unit_amount:n,discount_amount:k,is_taxable:b})}if(S.length===0&&C>=v)return{error:"This coupon would reduce the Stripe order to zero. Use a smaller discount for Stripe checkout.",errorKey:"ecommerce.coupon_zero_total_not_supported",errorStatus:400,url:null};if(S.length===0)return{error:"No valid items in cart",url:null};let m=0,N=null;if(B){const{data:e,error:t}=await a.from("shipping_zone_methods").select("id, name, name_translations, cost_amount, cost_currency").eq("id",B).single();if(t)return console.error("Failed to load shipping method:",t),{error:"Failed to load shipping method",url:null};m=h.convertMinorUnitAmount({amount:e.cost_amount??0,fromCurrencyCode:e.cost_currency||G.code,toCurrencyCode:p.code,currencies:g}),N=Ue(e,z)}const ae=H?y??s:s;let u;try{u=await w.calculateCheckoutTaxes(a,{items:oe,destination:{country_code:ae?.country_code,state:ae?.state}})}catch(e){return console.error("Failed to calculate checkout taxes:",e),{error:"Failed to calculate taxes",url:null}}m>0&&N&&S.push({price_data:{currency:F,product_data:{name:N,tax_code:u.enabled&&u.mode==="automatic"?w.STRIPE_TAX_CODE_SHIPPING:w.STRIPE_TAX_CODE_NONTAXABLE},tax_behavior:"exclusive",unit_amount:m},quantity:1});const E=u.enabled&&u.mode==="manual"&&!u.isPendingExternalCalculation?u.amount:0;E>0&&S.push({price_data:{currency:F,product_data:{name:"Tax",tax_code:w.STRIPE_TAX_CODE_NONTAXABLE},tax_behavior:"exclusive",unit_amount:E},quantity:1});const Ce=Pe.normalizeOrderCustomerDetails({email:l,phone:d,name:s?.recipient_name,billing:s,shipping:y}),ne=p.code,Ee=Te.buildOrderTaxDetailsFromCalculation({calculation:u,subtotal:v,shippingTotal:m,total:Math.max(0,v-C)+m+E,currency:ne}),qe=Math.max(0,v-C)+m+E,{data:ce,error:se}=await a.from("orders").insert({status:"pending",total:qe,currency:ne,exchange_rate_at_purchase:p.exchange_rate,subtotal:v,shipping_total:m,tax_total:E,tax_details:Ee,coupon_id:c?.couponId??null,coupon_code:c?.code??null,discount_total:C,discount_details:c?{code:c.code,discount_type:c.discountType,discount_amount:c.discountAmount,provider:"stripe",provider_discounts:c.providerDiscounts,line_discounts:c.lineDiscounts}:null,provider:"stripe",user_id:_,customer_details:Ce}).select("id").single();if(se||!ce)return console.error("Failed to create pending order:",se),{error:"Failed to initiate order",url:null};const q=ce.id,{error:ue}=await a.from("order_items").insert(ie.map(e=>({order_id:q,product_id:e.product_id,variant_id:e.variant_id??null,quantity:e.quantity,price_at_purchase:e.price_at_purchase})));if(ue)return console.error("Failed to insert order items:",ue),await a.from("orders").update({status:"failed"}).eq("id",q),{error:"Failed to record order items",url:null};if(_)try{await _e.upsertDefaultUserAddresses({userId:_,billingAddress:s,shippingAddress:y,client:a}),await _e.fillMissingUserProfileCheckoutDetails({userId:_,fullName:s?.recipient_name??y?.recipient_name??null,phone:d,client:a})}catch(e){console.error("Failed to sync checkout profile defaults before checkout:",e)}c&&await O.recordCouponRedemption({client:a,quote:c,orderId:q,provider:"stripe",discountTotal:C,userId:_,customerEmail:l,metadata:{currency:p.code,subtotal:v,shipping_total:m,tax_total:E}});const D=await Le({email:l,phone:d,userId:_,billingAddress:s,shippingAddress:y});try{const e=await U.stripe.checkout.sessions.create({mode:"payment",success_url:`${X}/checkout/success?session_id={CHECKOUT_SESSION_ID}`,cancel_url:`${X}/checkout`,locale:Me(z),line_items:S,automatic_tax:u.enabled&&u.mode==="automatic"?{enabled:!0}:void 0,billing_address_collection:"auto",customer:D||void 0,customer_email:D?void 0:l||void 0,customer_creation:D?void 0:"if_required",customer_update:D?{name:"auto",address:"auto",shipping:"auto"}:void 0,shipping_address_collection:H?{allowed_countries:Ie}:void 0,metadata:{orderId:q,taxMode:u.mode,currencyCode:p.code,couponCode:c?.code||""}}),{error:t}=await a.from("orders").update({stripe_session_id:e.id}).eq("id",q);return t&&console.error("Failed to save Stripe session ID on order:",t),{url:e.url}}catch(e){return console.error("Stripe session creation failed:",e),await a.from("orders").update({status:"failed"}).eq("id",q),{error:e.message,url:null}}}}exports.StripeProvider=Fe;
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const pe=require("../stripe/client.cjs.js"),ke=require("@supabase/supabase-js"),Pe=require("../countries.cjs.js"),be=require("../customer.cjs.js"),O=require("../coupon-server.cjs.js"),Te=require("../coupons.cjs.js"),de=require("../customer-addresses.cjs.js"),T=require("../inventory-settings.cjs.js"),w=require("../tax-calculation.cjs.js"),v=require("../currency.cjs.js"),we=require("../order-tax-details.cjs.js"),Ae=require("../types.cjs.js"),Ie=require("../variation-utils.cjs.js"),De=Pe.countries.map(r=>r.code),Me=new Set(["bg","cs","da","de","el","en","en-GB","es","es-419","et","fi","fil","fr","hr","hu","id","it","ja","ko","lt","lv","ms","mt","nb","nl","pl","pt","pt-BR","ro","ru","sk","sl","sv","th","tr","vi","zh","zh-HK","zh-TW"]);function _e(r){if(r)return{line1:r.line1||void 0,line2:r.line2||void 0,city:r.city||void 0,state:r.state||void 0,postal_code:r.postal_code||void 0,country:r.country_code||void 0}}function Ue(r){const i=r?.trim().replace("_","-");if(!i)return;const l=[i,i.toLowerCase(),i.split("-")[0].toLowerCase()];for(const d of l)if(Me.has(d))return d}function Le(r,i){return Ie.resolveTranslatedText(r.name,r.name_translations||null,i)}async function Fe(r){if(!r.email)return null;const i=await pe.getStripeClient(),l=_e(r.shippingAddress),d=r.shippingAddress?.recipient_name||r.billingAddress?.recipient_name||void 0,_={email:r.email,name:r.billingAddress?.recipient_name||r.shippingAddress?.recipient_name||void 0,phone:r.phone||void 0,address:_e(r.billingAddress),metadata:r.userId?{userId:r.userId}:void 0,...l&&d?{shipping:{name:d,phone:r.phone||void 0,address:l}}:{}};try{const p=(await i.customers.list({email:r.email,limit:1})).data[0];return p?(await i.customers.update(p.id,_),p.id):(await i.customers.create(_)).id}catch(u){return console.error("Failed to upsert Stripe customer for checkout prefill:",u),null}}class Re{getProviderName(){return"Stripe"}async createCheckoutSession({items:i,customerEmail:l,customerPhone:d,userId:_,billingAddress:u,shippingAddress:p,shippingMethodId:U,currencyCode:me,locale:B,couponCode:z,couponContextItems:L}){const he=await pe.getStripeClient(),K=process.env.NEXT_PUBLIC_SUPABASE_URL||process.env.SUPABASE_URL,Q=process.env.SUPABASE_SERVICE_ROLE_KEY||process.env.SUPABASE_SECRET_KEY;if(!K||!Q)return console.error("Missing Supabase credentials for checkout (Service Key required)."),{error:"Internal Server Error",url:null};const a=ke.createClient(K,Q),$=process.env.NEXT_PUBLIC_URL||"http://localhost:4200",X=i.some(e=>!Ae.isDigitalItem(e));if(!i.length)return{error:"Cart is empty",url:null};const{data:ve,error:H}=await a.from("currencies").select("code, symbol, exchange_rate, is_default, is_active, auto_sync_product_prices, auto_update_exchange_rate, exchange_rate_source, exchange_rate_updated_at, rounding_mode, rounding_increment, rounding_charm_amount").eq("is_active",!0).order("code",{ascending:!0}),g=ve??[];if(H||g.length===0)return console.error("Error fetching currencies for checkout:",H),{error:"Failed to resolve store currencies",url:null};const j=v.getDefaultCurrency(g),m=g.find(e=>e.code===(me||"").toUpperCase())??j,F=m.code.toLowerCase(),G=await T.getEcommerceInventorySettings(a),fe=i.map(e=>e.product_id),V=i.map(e=>e.variant_id).filter(e=>!!e),{data:W,error:Y}=await a.from("products").select("id, title, sku, price, prices, sale_price, sale_prices, sale_start_at, sale_end_at, scheduled_price, scheduled_prices, scheduled_price_at, stock, is_taxable").in("id",fe);if(Y||!W)return console.error("Error fetching products for validation:",Y),{error:"Failed to validate product prices",url:null};const{data:ye,error:J}=V.length?await a.from("product_variants").select("id, product_id, sku, price, prices, sale_price, sale_prices, sale_start_at, sale_end_at, scheduled_price, scheduled_prices, scheduled_price_at, stock_quantity").in("id",V):{data:[],error:null};if(J)return console.error("Error fetching variants for validation:",J),{error:"Failed to validate product variants",url:null};const Z=new Map(W.map(e=>[e.id,e])),ee=new Map((ye||[]).map(e=>[e.id,e])),R=new Set,A=new Map;for(const e of i){const t=Z.get(e.product_id);if(!t)continue;const n=(e.variant_id?ee.get(e.variant_id):null)?.sku||t.sku;n&&(R.add(n),A.set(n,(A.get(n)??0)+e.quantity))}const{data:ge,error:te}=R.size?await a.from("inventory_items").select("sku, quantity").in("sku",[...R]):{data:[],error:null};if(te)return console.error("Error fetching SKU inventory for validation:",te),{error:"Failed to validate SKU inventory",url:null};const I=new Map((ge||[]).map(e=>[e.sku,Math.max(0,e.quantity??0)])),S=[],re=[],oe=[];let f=0,C=0,c=null;if(z){const e=await O.getCouponQuote({client:a,code:z,items:L&&L.length>0?L:i,currencyCode:m.code});if(!e.success)return{error:e.error,errorKey:e.errorKey,errorStatus:400,url:null};c=e.quote}const Se=O.getQuoteLineDiscountMap(c),Ce=e=>{const t=e.unitAmount*e.quantity,y=Math.max(0,t-e.lineDiscount);if(y<=0)return;const n=Math.floor(y/e.quantity),P=y-n*e.quantity,x=(b,k)=>{k<=0||b<=0||S.push({price_data:{currency:e.currency,product_data:{name:e.name,tax_code:w.getStripeTaxCodeForProduct(e.isTaxable),metadata:{productId:e.productId,variantId:e.variantId||""}},tax_behavior:"exclusive",unit_amount:b},quantity:k})};x(n,e.quantity-P),x(n+1,P)};for(const e of i){const t=Z.get(e.product_id);if(!t)return console.warn(`Product ${e.product_id} not found in DB.`),{url:null,...T.createInventoryUnavailableError(e.title)};const y=v.resolveEffectivePriceForCurrency({prices:v.normalizePriceMap(t.prices),salePrices:t.sale_prices||{},fallbackPrice:t.price,fallbackSalePrice:t.sale_price,saleStartAt:t.sale_start_at,saleEndAt:t.sale_end_at,scheduledPrice:t.scheduled_price,scheduledPrices:v.normalizePriceMap(t.scheduled_prices),scheduledPriceAt:t.scheduled_price_at,currencyCode:m.code,currencies:g});let n=y.sale_price??y.price,P=t.title,x=null;if(e.variant_id){const o=ee.get(e.variant_id);if(!o||o.product_id!==e.product_id)return{url:null,...T.createInventoryUnavailableError(e.title)};const M=A.get(o.sku)??e.quantity,ue=I.has(o.sku)?I.get(o.sku)??0:Math.max(0,o.stock_quantity??0);if(G.trackQuantities&&M>ue)return{url:null,...T.createInventoryInsufficientError(e.title,ue)};const le=v.resolveEffectivePriceForCurrency({prices:v.normalizePriceMap(o.prices),salePrices:o.sale_prices||{},fallbackPrice:o.price,fallbackSalePrice:o.sale_price,saleStartAt:o.sale_start_at,saleEndAt:o.sale_end_at,scheduledPrice:o.scheduled_price,scheduledPrices:v.normalizePriceMap(o.scheduled_prices),scheduledPriceAt:o.scheduled_price_at,currencyCode:m.code,currencies:g});n=le.sale_price??le.price,x=o.id,P=e.variant_label?`${t.title} - ${e.variant_label}`:`${t.title} - ${o.sku}`}else{const o=A.get(t.sku)??e.quantity,M=I.has(t.sku)?I.get(t.sku)??0:Math.max(0,t.stock??0);if(G.trackQuantities&&o>M)return{url:null,...T.createInventoryInsufficientError(e.title,M)}}const b=t.is_taxable??!0;if(n<0)return{error:"A product variation produced an invalid price.",url:null};const k=Math.min(n*e.quantity,Se.get(Te.getCartLineCouponKey(e))??0);Ce({name:P,currency:F,productId:t.id,variantId:x,isTaxable:b,unitAmount:n,quantity:e.quantity,lineDiscount:k}),f+=n*e.quantity,C+=k,oe.push({product_id:t.id,quantity:e.quantity,price_at_purchase:n,variant_id:x}),re.push({product_id:t.id,quantity:e.quantity,unit_amount:n,discount_amount:k,is_taxable:b})}if(S.length===0&&C>=f)return{error:"This coupon would reduce the Stripe order to zero. Use a smaller discount for Stripe checkout.",errorKey:"ecommerce.coupon_zero_total_not_supported",errorStatus:400,url:null};if(S.length===0)return{error:"No valid items in cart",url:null};let h=0,N=null;if(U){const{data:e,error:t}=await a.from("shipping_zone_methods").select("id, name, name_translations, cost_amount, cost_currency").eq("id",U).single();if(t)return console.error("Failed to load shipping method:",t),{error:"Failed to load shipping method",url:null};h=v.convertMinorUnitAmount({amount:e.cost_amount??0,fromCurrencyCode:e.cost_currency||j.code,toCurrencyCode:m.code,currencies:g}),N=Le(e,B)}const ie=X?p??u:u;let s;try{s=await w.calculateCheckoutTaxes(a,{items:re,destination:{country_code:ie?.country_code,state:ie?.state}})}catch(e){return console.error("Failed to calculate checkout taxes:",e),{error:"Failed to calculate taxes",url:null}}h>0&&N&&S.push({price_data:{currency:F,product_data:{name:N,tax_code:s.enabled&&s.mode==="automatic"?w.STRIPE_TAX_CODE_SHIPPING:w.STRIPE_TAX_CODE_NONTAXABLE},tax_behavior:"exclusive",unit_amount:h},quantity:1});const E=s.enabled&&s.mode==="manual"&&!s.isPendingExternalCalculation?s.amount:0;E>0&&S.push({price_data:{currency:F,product_data:{name:"Tax",tax_code:w.STRIPE_TAX_CODE_NONTAXABLE},tax_behavior:"exclusive",unit_amount:E},quantity:1});const Ee=be.normalizeOrderCustomerDetails({email:l,phone:d,name:u?.recipient_name,billing:u,shipping:p}),ae=m.code,qe=we.buildOrderTaxDetailsFromCalculation({calculation:s,subtotal:f,shippingTotal:h,total:Math.max(0,f-C)+h+E,currency:ae}),xe=Math.max(0,f-C)+h+E,{data:ne,error:ce}=await a.from("orders").insert({status:"pending",total:xe,currency:ae,exchange_rate_at_purchase:m.exchange_rate,subtotal:f,shipping_total:h,tax_total:E,tax_details:qe,coupon_id:c?.couponId??null,coupon_code:c?.code??null,discount_total:C,discount_details:c?{code:c.code,discount_type:c.discountType,discount_amount:c.discountAmount,provider:"stripe",provider_discounts:c.providerDiscounts,line_discounts:c.lineDiscounts}:null,provider:"stripe",user_id:_,customer_details:Ee}).select("id").single();if(ce||!ne)return console.error("Failed to create pending order:",ce),{error:"Failed to initiate order",url:null};const q=ne.id,{error:se}=await a.from("order_items").insert(oe.map(e=>({order_id:q,product_id:e.product_id,variant_id:e.variant_id??null,quantity:e.quantity,price_at_purchase:e.price_at_purchase})));if(se)return console.error("Failed to insert order items:",se),await a.from("orders").update({status:"failed"}).eq("id",q),{error:"Failed to record order items",url:null};if(_)try{await de.upsertDefaultUserAddresses({userId:_,billingAddress:u,shippingAddress:p,client:a}),await de.fillMissingUserProfileCheckoutDetails({userId:_,fullName:u?.recipient_name??p?.recipient_name??null,phone:d,client:a})}catch(e){console.error("Failed to sync checkout profile defaults before checkout:",e)}c&&await O.recordCouponRedemption({client:a,quote:c,orderId:q,provider:"stripe",discountTotal:C,userId:_,customerEmail:l,metadata:{currency:m.code,subtotal:f,shipping_total:h,tax_total:E}});const D=await Fe({email:l,phone:d,userId:_,billingAddress:u,shippingAddress:p});try{const e=await he.checkout.sessions.create({mode:"payment",success_url:`${$}/checkout/success?session_id={CHECKOUT_SESSION_ID}`,cancel_url:`${$}/checkout`,locale:Ue(B),line_items:S,automatic_tax:s.enabled&&s.mode==="automatic"?{enabled:!0}:void 0,billing_address_collection:"auto",customer:D||void 0,customer_email:D?void 0:l||void 0,customer_creation:D?void 0:"if_required",customer_update:D?{name:"auto",address:"auto",shipping:"auto"}:void 0,shipping_address_collection:X?{allowed_countries:De}:void 0,metadata:{orderId:q,taxMode:s.mode,currencyCode:m.code,couponCode:c?.code||""}}),{error:t}=await a.from("orders").update({stripe_session_id:e.id}).eq("id",q);return t&&console.error("Failed to save Stripe session ID on order:",t),{url:e.url}}catch(e){return console.error("Stripe session creation failed:",e),await a.from("orders").update({status:"failed"}).eq("id",q),{error:e.message,url:null}}}}exports.StripeProvider=Re;
|