@nextblock-cms/ecom 0.22.0 → 0.22.1
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/README.md +43 -43
- package/index.cjs +1 -1
- package/index.es.js +31 -31
- package/lib/components/Checkout.cjs +1 -1
- package/lib/components/Checkout.es.js +2 -1
- package/lib/coupon-server.cjs +1 -1
- package/lib/coupon-server.d.ts +65 -1
- package/lib/coupon-server.es.js +151 -91
- package/lib/coupons.cjs +1 -1
- package/lib/coupons.d.ts +13 -0
- package/lib/coupons.es.js +5 -4
- package/lib/order-access-token.cjs +1 -0
- package/lib/order-access-token.d.ts +2 -0
- package/lib/order-access-token.es.js +18 -0
- package/lib/pages/cms/orders/actions.cjs +3 -3
- package/lib/pages/cms/orders/actions.es.js +48 -26
- package/lib/pages/cms/orders/export-actions.cjs +1 -1
- package/lib/pages/cms/orders/export-actions.d.ts +42 -1
- package/lib/pages/cms/orders/export-actions.es.js +18 -16
- package/lib/pages/cms/orders/server-actions.cjs +1 -1
- package/lib/pages/cms/orders/server-actions.es.js +25 -15
- package/lib/pages/cms/products/actions.cjs +4 -4
- package/lib/pages/cms/products/actions.es.js +44 -41
- package/lib/pages/cms/products/inventory/actions.cjs +2 -2
- package/lib/pages/cms/products/inventory/actions.es.js +23 -23
- package/lib/pages/cms/products/server-actions.cjs +1 -1
- package/lib/pages/cms/products/server-actions.es.js +109 -108
- package/lib/pages/cms/shipping/server-actions.cjs +1 -1
- package/lib/pages/cms/shipping/server-actions.es.js +27 -26
- package/lib/payment-config.cjs +1 -1
- package/lib/payment-config.d.ts +4 -2
- package/lib/payment-config.es.js +73 -72
- package/lib/product-actions.cjs +1 -1
- package/lib/product-actions.es.js +6 -6
- package/lib/providers/checkout-items.cjs +1 -0
- package/lib/providers/checkout-items.d.ts +30 -0
- package/lib/providers/checkout-items.es.js +43 -0
- package/lib/providers/freemius.cjs +1 -1
- package/lib/providers/freemius.d.ts +1 -1
- package/lib/providers/freemius.es.js +166 -144
- package/lib/providers/stripe.cjs +1 -1
- package/lib/providers/stripe.d.ts +1 -1
- package/lib/providers/stripe.es.js +191 -146
- package/lib/server-actions/coupon-actions.cjs +1 -1
- package/lib/server-actions/coupon-actions.d.ts +11 -12
- package/lib/server-actions/coupon-actions.es.js +19 -11
- package/lib/server-actions/require-ecom-role.cjs +1 -0
- package/lib/server-actions/require-ecom-role.d.ts +5825 -0
- package/lib/server-actions/require-ecom-role.es.js +24 -0
- package/lib/server-actions/shipping-actions.cjs +1 -1
- package/lib/server-actions/shipping-actions.es.js +4 -4
- package/lib/server-actions/tax-actions.cjs +1 -1
- package/lib/server-actions/tax-actions.es.js +22 -18
- package/lib/shipping/checkout-shipping-method.cjs +7 -0
- package/lib/shipping/checkout-shipping-method.d.ts +115 -0
- package/lib/shipping/checkout-shipping-method.es.js +167 -0
- package/lib/shipping/resolver.cjs +1 -7
- package/lib/shipping/resolver.d.ts +7 -12
- package/lib/shipping/resolver.es.js +13 -38
- package/package.json +4 -4
- package/server.cjs +1 -1
- package/server.d.ts +1 -0
- package/server.es.js +49 -48
|
@@ -3,17 +3,19 @@ import { getCouponQuote as r, recordCouponRedemption as i } from "../coupon-serv
|
|
|
3
3
|
import { normalizeOrderCustomerDetails as a } from "../customer.es.js";
|
|
4
4
|
import { fillMissingUserProfileCheckoutDetails as o, upsertDefaultUserAddresses as s } from "../customer-addresses.es.js";
|
|
5
5
|
import { hydrateFreemiusEnvFromDb as c } from "../payment-config.es.js";
|
|
6
|
-
import {
|
|
7
|
-
import u from "
|
|
8
|
-
import {
|
|
6
|
+
import { mintOrderAccessToken as l } from "../order-access-token.es.js";
|
|
7
|
+
import { normalizeCheckoutItems as u } from "./checkout-items.es.js";
|
|
8
|
+
import { createClient as d } from "@supabase/supabase-js";
|
|
9
|
+
import f from "crypto";
|
|
10
|
+
import { Freemius as p } from "@freemius/sdk";
|
|
9
11
|
//#region src/lib/providers/freemius.ts
|
|
10
|
-
function
|
|
12
|
+
function m(e) {
|
|
11
13
|
let t = process.env[e];
|
|
12
14
|
if (!t) return null;
|
|
13
15
|
let n = t.trim();
|
|
14
16
|
return n.startsWith("\"") && n.endsWith("\"") || n.startsWith("'") && n.endsWith("'") ? n.slice(1, -1).trim() : n;
|
|
15
17
|
}
|
|
16
|
-
function
|
|
18
|
+
function h(e) {
|
|
17
19
|
let t = e?.trim();
|
|
18
20
|
if (!t) return {
|
|
19
21
|
firstName: null,
|
|
@@ -25,39 +27,39 @@ function p(e) {
|
|
|
25
27
|
lastName: r.length > 0 ? r.join(" ") : null
|
|
26
28
|
};
|
|
27
29
|
}
|
|
28
|
-
function
|
|
30
|
+
function g(e) {
|
|
29
31
|
if (e == null || e === "") return 0;
|
|
30
32
|
let t = Number(e);
|
|
31
33
|
return !Number.isFinite(t) || t <= 0 ? 0 : Math.round(t);
|
|
32
34
|
}
|
|
33
|
-
function
|
|
35
|
+
function _(e) {
|
|
34
36
|
return e === !0 || e === 1 || e === "1" || e === "true";
|
|
35
37
|
}
|
|
36
|
-
function
|
|
38
|
+
function v(e, t) {
|
|
37
39
|
let n = e?.[t];
|
|
38
40
|
return Array.isArray(n) ? n : n && typeof n == "object" ? [n] : [];
|
|
39
41
|
}
|
|
40
|
-
function
|
|
42
|
+
function y(e, t) {
|
|
41
43
|
for (let n of t) {
|
|
42
|
-
let t =
|
|
44
|
+
let t = v(e, n);
|
|
43
45
|
if (t.length > 0) return t;
|
|
44
46
|
}
|
|
45
47
|
return Array.isArray(e) ? e : [];
|
|
46
48
|
}
|
|
47
|
-
function
|
|
49
|
+
function b(e) {
|
|
48
50
|
if (e == null || e === "") return null;
|
|
49
51
|
let t = Number(e);
|
|
50
52
|
return Number.isFinite(t) ? t : null;
|
|
51
53
|
}
|
|
52
|
-
function
|
|
54
|
+
function x(e) {
|
|
53
55
|
return e === null ? 0 : e > 5e3 ? (console.warn(`[Freemius Sync] Suspiciously high price detected: ${e}. Assuming it is already in cents.`), Math.round(e)) : Math.round(e * 100);
|
|
54
56
|
}
|
|
55
|
-
function
|
|
57
|
+
function S(e) {
|
|
56
58
|
let t = Number(e);
|
|
57
59
|
return !Number.isFinite(t) || t < 1 ? 1 : Math.round(t);
|
|
58
60
|
}
|
|
59
|
-
function
|
|
60
|
-
let e =
|
|
61
|
+
function C() {
|
|
62
|
+
let e = m("FREEMIUS_CHECKOUT_PRODUCTS_JSON");
|
|
61
63
|
if (!e) return null;
|
|
62
64
|
try {
|
|
63
65
|
let t = JSON.parse(e);
|
|
@@ -66,8 +68,8 @@ function x() {
|
|
|
66
68
|
return console.error("[Freemius Checkout] Failed to parse FREEMIUS_CHECKOUT_PRODUCTS_JSON:", e), null;
|
|
67
69
|
}
|
|
68
70
|
}
|
|
69
|
-
function
|
|
70
|
-
let t =
|
|
71
|
+
function w(e) {
|
|
72
|
+
let t = C(), n = String(e), r = t?.[n], i = m("FREEMIUS_PRODUCT_ID"), a = m("FREEMIUS_ECOMMERCE_SANDBOX_PUBLIC_KEY"), o = m("FREEMIUS_ECOMMERCE_SANDBOX_SECRET_KEY");
|
|
71
73
|
return r?.publicKey ? {
|
|
72
74
|
publicKey: r.publicKey,
|
|
73
75
|
secretKey: r.secretKey ?? null,
|
|
@@ -76,85 +78,99 @@ function S(e) {
|
|
|
76
78
|
} : process.env.FREEMIUS_SANDBOX_ENABLED === "true" && i && i === n && a ? {
|
|
77
79
|
publicKey: a,
|
|
78
80
|
secretKey: o,
|
|
79
|
-
apiKey:
|
|
81
|
+
apiKey: m("FREEMIUS_API_KEY"),
|
|
80
82
|
source: "single-product-sandbox-env"
|
|
81
|
-
} : i && i === n &&
|
|
82
|
-
publicKey:
|
|
83
|
-
secretKey:
|
|
84
|
-
apiKey:
|
|
83
|
+
} : i && i === n && m("FREEMIUS_PUBLIC_KEY") ? {
|
|
84
|
+
publicKey: m("FREEMIUS_PUBLIC_KEY"),
|
|
85
|
+
secretKey: m("FREEMIUS_SECRET_KEY"),
|
|
86
|
+
apiKey: m("FREEMIUS_API_KEY"),
|
|
85
87
|
source: "single-product-env"
|
|
86
88
|
} : {
|
|
87
|
-
publicKey:
|
|
88
|
-
secretKey:
|
|
89
|
-
apiKey:
|
|
89
|
+
publicKey: m("FREEMIUS_PUBLIC_KEY"),
|
|
90
|
+
secretKey: m("FREEMIUS_SECRET_KEY"),
|
|
91
|
+
apiKey: m("FREEMIUS_API_KEY"),
|
|
90
92
|
source: "legacy-env"
|
|
91
93
|
};
|
|
92
94
|
}
|
|
93
|
-
async function
|
|
95
|
+
async function T(e) {
|
|
94
96
|
if (!e.apiKey) throw Error("Missing Freemius API key for SDK sandbox generation.");
|
|
95
|
-
return new
|
|
97
|
+
return new p({
|
|
96
98
|
productId: Number(e.productId),
|
|
97
99
|
apiKey: e.apiKey,
|
|
98
100
|
secretKey: e.secretKey,
|
|
99
101
|
publicKey: e.publicKey
|
|
100
102
|
}).checkout.getSandboxParams();
|
|
101
103
|
}
|
|
102
|
-
var
|
|
104
|
+
var E = class {
|
|
103
105
|
getProviderName() {
|
|
104
106
|
return "Freemius";
|
|
105
107
|
}
|
|
106
|
-
async createCheckoutSession({ items:
|
|
107
|
-
let
|
|
108
|
-
if (!
|
|
108
|
+
async createCheckoutSession({ items: p, customerEmail: _, customerPhone: v, userId: y, billingAddress: b, shippingAddress: x, currencyCode: S, couponCode: C, couponContextItems: E }) {
|
|
109
|
+
let D = process.env.NEXT_PUBLIC_SUPABASE_URL || process.env.SUPABASE_URL, O = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.SUPABASE_SECRET_KEY;
|
|
110
|
+
if (!D || !O) return {
|
|
109
111
|
error: "Missing Supabase credentials for checkout (Service Key required).",
|
|
110
112
|
url: null
|
|
111
113
|
};
|
|
112
|
-
let
|
|
113
|
-
if (!
|
|
114
|
+
let k = d(D, O);
|
|
115
|
+
if (!p || p.length === 0) return {
|
|
114
116
|
error: "Cart is empty",
|
|
115
117
|
url: null
|
|
116
118
|
};
|
|
117
|
-
if (
|
|
119
|
+
if (p.length !== 1) return {
|
|
118
120
|
error: "Freemius items must be checked out one at a time.",
|
|
119
121
|
url: null
|
|
120
122
|
};
|
|
121
|
-
let
|
|
122
|
-
if (
|
|
123
|
+
let A = u(p);
|
|
124
|
+
if (!A.ok) return {
|
|
125
|
+
url: null,
|
|
126
|
+
...A.error
|
|
127
|
+
};
|
|
128
|
+
let ee = A.items;
|
|
129
|
+
if (E && E.length > 0) {
|
|
130
|
+
let e = u(E);
|
|
131
|
+
if (!e.ok) return {
|
|
132
|
+
url: null,
|
|
133
|
+
...e.error
|
|
134
|
+
};
|
|
135
|
+
ee = e.items;
|
|
136
|
+
}
|
|
137
|
+
let { data: te, error: ne } = await k.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 }), j = te ?? [];
|
|
138
|
+
if (ne || j.length === 0) return {
|
|
123
139
|
error: "Failed to resolve store currencies",
|
|
124
140
|
url: null
|
|
125
141
|
};
|
|
126
|
-
let
|
|
127
|
-
if (
|
|
142
|
+
let re = e(j), M = j.find((e) => e.code === (S || "").toUpperCase()) ?? re, N = A.items[0], { data: P, error: ie } = await k.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", N.product_id).single();
|
|
143
|
+
if (ie || !P) return {
|
|
128
144
|
error: "Product not found",
|
|
129
145
|
url: null
|
|
130
146
|
};
|
|
131
|
-
let
|
|
132
|
-
if (!
|
|
147
|
+
let F = P.freemius_plan_id, I = P.freemius_product_id;
|
|
148
|
+
if (!F || !I) return {
|
|
133
149
|
error: "Product is not configured for Freemius checkout (missing Plan ID or Product ID)",
|
|
134
150
|
url: null
|
|
135
151
|
};
|
|
136
|
-
let
|
|
137
|
-
prices:
|
|
138
|
-
salePrices:
|
|
139
|
-
fallbackPrice:
|
|
140
|
-
fallbackSalePrice:
|
|
141
|
-
saleStartAt:
|
|
142
|
-
saleEndAt:
|
|
143
|
-
scheduledPrice:
|
|
144
|
-
scheduledPrices:
|
|
145
|
-
scheduledPriceAt:
|
|
146
|
-
currencyCode:
|
|
147
|
-
currencies:
|
|
148
|
-
}),
|
|
149
|
-
saleStartAt:
|
|
150
|
-
saleEndAt:
|
|
151
|
-
}), R =
|
|
152
|
-
if (
|
|
152
|
+
let L = n({
|
|
153
|
+
prices: P.prices || {},
|
|
154
|
+
salePrices: P.sale_prices || {},
|
|
155
|
+
fallbackPrice: P.price,
|
|
156
|
+
fallbackSalePrice: P.sale_price,
|
|
157
|
+
saleStartAt: P.sale_start_at,
|
|
158
|
+
saleEndAt: P.sale_end_at,
|
|
159
|
+
scheduledPrice: P.scheduled_price,
|
|
160
|
+
scheduledPrices: P.scheduled_prices || {},
|
|
161
|
+
scheduledPriceAt: P.scheduled_price_at,
|
|
162
|
+
currencyCode: M.code,
|
|
163
|
+
currencies: j
|
|
164
|
+
}), ae = L.sale_price ?? L.price, oe = t({
|
|
165
|
+
saleStartAt: P.sale_start_at,
|
|
166
|
+
saleEndAt: P.sale_end_at
|
|
167
|
+
}), R = ae * N.quantity, z = null, B = 0;
|
|
168
|
+
if (C) {
|
|
153
169
|
let e = await r({
|
|
154
|
-
client:
|
|
155
|
-
code:
|
|
156
|
-
items:
|
|
157
|
-
currencyCode:
|
|
170
|
+
client: k,
|
|
171
|
+
code: C,
|
|
172
|
+
items: ee,
|
|
173
|
+
currencyCode: M.code
|
|
158
174
|
});
|
|
159
175
|
if (!e.success) return {
|
|
160
176
|
error: e.error,
|
|
@@ -162,13 +178,13 @@ var w = class {
|
|
|
162
178
|
errorStatus: 400,
|
|
163
179
|
url: null
|
|
164
180
|
};
|
|
165
|
-
z = e.quote, B = Math.min(R, z.lineDiscounts.filter((e) => e.product_id ===
|
|
181
|
+
z = e.quote, B = Math.min(R, z.lineDiscounts.filter((e) => e.product_id === P.id).reduce((e, t) => e + t.discount, 0));
|
|
166
182
|
}
|
|
167
|
-
let V =
|
|
183
|
+
let V = g(P.trial_period_days), H = V > 0 ? N.trial_preference ? N.trial_preference : P.trial_requires_payment_method ? "paid" : "free" : null, U = "pending", W = h(b?.recipient_name ?? null), { data: G, error: K } = await k.from("orders").insert({
|
|
168
184
|
status: U,
|
|
169
185
|
total: Math.max(0, R - B),
|
|
170
|
-
currency:
|
|
171
|
-
exchange_rate_at_purchase:
|
|
186
|
+
currency: M.code,
|
|
187
|
+
exchange_rate_at_purchase: M.exchange_rate,
|
|
172
188
|
subtotal: R,
|
|
173
189
|
discount_total: B,
|
|
174
190
|
coupon_id: z?.couponId ?? null,
|
|
@@ -183,57 +199,62 @@ var w = class {
|
|
|
183
199
|
final_amount_owned_by: "freemius"
|
|
184
200
|
} : null,
|
|
185
201
|
provider: "freemius",
|
|
186
|
-
freemius_product_id: String(
|
|
187
|
-
freemius_plan_id: String(
|
|
188
|
-
user_id:
|
|
202
|
+
freemius_product_id: String(I),
|
|
203
|
+
freemius_plan_id: String(F),
|
|
204
|
+
user_id: y || null,
|
|
189
205
|
customer_details: a({
|
|
190
|
-
email:
|
|
191
|
-
phone:
|
|
192
|
-
name:
|
|
193
|
-
billing:
|
|
194
|
-
shipping:
|
|
206
|
+
email: _,
|
|
207
|
+
phone: v,
|
|
208
|
+
name: b?.recipient_name,
|
|
209
|
+
billing: b,
|
|
210
|
+
shipping: x
|
|
195
211
|
})
|
|
196
212
|
}).select("id").single();
|
|
197
213
|
if (K || !G) return console.error("Failed to create pending order:", K), {
|
|
198
214
|
error: "Failed to initiate order",
|
|
199
215
|
url: null
|
|
200
216
|
};
|
|
201
|
-
let { error: q } = await
|
|
217
|
+
let { error: q } = await k.from("order_items").insert([{
|
|
202
218
|
order_id: G.id,
|
|
203
|
-
product_id:
|
|
204
|
-
quantity:
|
|
205
|
-
price_at_purchase:
|
|
219
|
+
product_id: P.id,
|
|
220
|
+
quantity: N.quantity,
|
|
221
|
+
price_at_purchase: ae
|
|
206
222
|
}]);
|
|
207
|
-
if (q && console.error("Failed to insert order items:", q),
|
|
223
|
+
if (q && console.error("Failed to insert order items:", q), y) try {
|
|
208
224
|
await s({
|
|
209
|
-
userId:
|
|
210
|
-
billingAddress:
|
|
211
|
-
shippingAddress:
|
|
212
|
-
client:
|
|
225
|
+
userId: y,
|
|
226
|
+
billingAddress: b,
|
|
227
|
+
shippingAddress: x,
|
|
228
|
+
client: k
|
|
213
229
|
}), await o({
|
|
214
|
-
userId:
|
|
215
|
-
fullName:
|
|
216
|
-
phone:
|
|
217
|
-
client:
|
|
230
|
+
userId: y,
|
|
231
|
+
fullName: b?.recipient_name ?? x?.recipient_name ?? null,
|
|
232
|
+
phone: v,
|
|
233
|
+
client: k
|
|
218
234
|
});
|
|
219
235
|
} catch (e) {
|
|
220
236
|
console.error("Failed to sync checkout profile defaults before checkout:", e);
|
|
221
237
|
}
|
|
222
|
-
z && await i({
|
|
223
|
-
client:
|
|
238
|
+
if (z && !(await i({
|
|
239
|
+
client: k,
|
|
224
240
|
quote: z,
|
|
225
241
|
orderId: G.id,
|
|
226
242
|
provider: "freemius",
|
|
227
243
|
discountTotal: B,
|
|
228
|
-
userId:
|
|
229
|
-
customerEmail:
|
|
244
|
+
userId: y,
|
|
245
|
+
customerEmail: _,
|
|
230
246
|
metadata: {
|
|
231
|
-
currency:
|
|
247
|
+
currency: M.code,
|
|
232
248
|
subtotal: R,
|
|
233
249
|
final_amount_owned_by: "freemius"
|
|
234
250
|
}
|
|
235
|
-
})
|
|
236
|
-
|
|
251
|
+
})).success) return await k.from("orders").update({ status: "failed" }).eq("id", G.id), {
|
|
252
|
+
url: null,
|
|
253
|
+
error: "Coupon is no longer available.",
|
|
254
|
+
errorKey: "ecommerce.coupon_invalid"
|
|
255
|
+
};
|
|
256
|
+
await c();
|
|
257
|
+
let J = process.env.FREEMIUS_SANDBOX_ENABLED === "true", Y = w(I), X = Y.publicKey, Z = Y.secretKey, se = Y.apiKey;
|
|
237
258
|
if (!X || J && !Z) return console.error("Freemius checkout blocked: missing credentials (PUBLIC_KEY or SECRET_KEY) in CMS → Payments or environment."), {
|
|
238
259
|
error: "This store is not able to take payments right now. Please contact the seller to complete your purchase.",
|
|
239
260
|
errorKey: "ecommerce.checkout_payments_unavailable",
|
|
@@ -241,38 +262,38 @@ var w = class {
|
|
|
241
262
|
url: null
|
|
242
263
|
};
|
|
243
264
|
if (J && Y.source === "legacy-env") {
|
|
244
|
-
let e =
|
|
245
|
-
console.warn(`[Freemius Checkout] Sandbox is enabled for product ${
|
|
265
|
+
let e = m("FREEMIUS_PRODUCT_ID"), t = !!m("FREEMIUS_ECOMMERCE_SANDBOX_PUBLIC_KEY"), n = !!m("FREEMIUS_ECOMMERCE_SANDBOX_SECRET_KEY");
|
|
266
|
+
console.warn(`[Freemius Checkout] Sandbox is enabled for product ${I}, 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.`, {
|
|
246
267
|
configuredProductId: e,
|
|
247
|
-
productIdsMatch: e === String(
|
|
268
|
+
productIdsMatch: e === String(I),
|
|
248
269
|
hasSandboxOverridePublicKey: t,
|
|
249
270
|
hasSandboxOverrideSecretKey: n,
|
|
250
|
-
hasCheckoutProductsJson: !!
|
|
271
|
+
hasCheckoutProductsJson: !!m("FREEMIUS_CHECKOUT_PRODUCTS_JSON")
|
|
251
272
|
});
|
|
252
273
|
}
|
|
253
274
|
let Q = !1;
|
|
254
275
|
if (J && Z && X) try {
|
|
255
|
-
Q = await
|
|
256
|
-
productId:
|
|
276
|
+
Q = await T({
|
|
277
|
+
productId: I,
|
|
257
278
|
publicKey: X,
|
|
258
279
|
secretKey: Z,
|
|
259
|
-
apiKey:
|
|
280
|
+
apiKey: se
|
|
260
281
|
});
|
|
261
282
|
} catch (e) {
|
|
262
283
|
console.warn("Freemius Checkout - SDK sandbox generation failed. Falling back to manual token generation.", e, {
|
|
263
284
|
credentialSource: Y.source,
|
|
264
|
-
hasApiKey: !!
|
|
285
|
+
hasApiKey: !!se
|
|
265
286
|
});
|
|
266
|
-
let t = Math.floor(Date.now() / 1e3).toString(), n = `${t}${
|
|
287
|
+
let t = Math.floor(Date.now() / 1e3).toString(), n = `${t}${I}${Z}${X}checkout`;
|
|
267
288
|
Q = {
|
|
268
289
|
ctx: t,
|
|
269
|
-
token:
|
|
290
|
+
token: f.createHash("md5").update(n).digest("hex")
|
|
270
291
|
};
|
|
271
292
|
}
|
|
272
|
-
let $ = new URL(`https://checkout.freemius.com/app/${
|
|
273
|
-
if (J && Z && X ? ($.searchParams.append("sandbox", Q.token), $.searchParams.append("s_ctx_ts", Q.ctx)) : J && $.searchParams.append("sandbox", "true"),
|
|
274
|
-
else if (
|
|
275
|
-
let { data: e } = await
|
|
293
|
+
let $ = new URL(`https://checkout.freemius.com/app/${I}/plan/${F}/`);
|
|
294
|
+
if (J && Z && X ? ($.searchParams.append("sandbox", Q.token), $.searchParams.append("s_ctx_ts", Q.ctx)) : J && $.searchParams.append("sandbox", "true"), _ && $.searchParams.append("user_email", _), W.firstName && $.searchParams.append("user_firstname", W.firstName), W.lastName && $.searchParams.append("user_lastname", W.lastName), $.searchParams.append("currency", M.code.toLowerCase()), N.billing_cycle && $.searchParams.append("billing_cycle", N.billing_cycle), H && $.searchParams.append("trial", H), z) $.searchParams.append("coupon", z.code);
|
|
295
|
+
else if (oe) {
|
|
296
|
+
let { data: e } = await k.from("product_freemius_sale_coupons").select("freemius_coupon_code, is_active, starts_at, ends_at, sync_status").eq("product_id", P.id).maybeSingle();
|
|
276
297
|
e?.is_active && e.sync_status === "synced" && e.freemius_coupon_code && t({
|
|
277
298
|
saleStartAt: e.starts_at,
|
|
278
299
|
saleEndAt: e.ends_at
|
|
@@ -282,27 +303,28 @@ var w = class {
|
|
|
282
303
|
url: $.toString(),
|
|
283
304
|
customProps: {
|
|
284
305
|
provider: "freemius",
|
|
285
|
-
plugin_id:
|
|
286
|
-
plan_id:
|
|
306
|
+
plugin_id: I,
|
|
307
|
+
plan_id: F,
|
|
287
308
|
public_key: X,
|
|
288
|
-
user_email:
|
|
309
|
+
user_email: _,
|
|
289
310
|
user_firstname: W.firstName,
|
|
290
311
|
user_lastname: W.lastName,
|
|
291
312
|
credential_source: Y.source,
|
|
292
313
|
sandbox: Q,
|
|
293
|
-
billing_cycle:
|
|
314
|
+
billing_cycle: N.billing_cycle,
|
|
294
315
|
trial: H,
|
|
295
316
|
trial_period_days: V,
|
|
296
|
-
trial_requires_payment_method:
|
|
317
|
+
trial_requires_payment_method: P.trial_requires_payment_method,
|
|
297
318
|
initial_order_status: U,
|
|
298
319
|
coupon: z?.code ?? null,
|
|
299
|
-
order_id: G.id
|
|
320
|
+
order_id: G.id,
|
|
321
|
+
success_token: l(G.id)
|
|
300
322
|
}
|
|
301
323
|
};
|
|
302
324
|
}
|
|
303
325
|
};
|
|
304
|
-
async function
|
|
305
|
-
let i = (/* @__PURE__ */ new Date()).toUTCString().replace("GMT", "+0000"), a = `GET\n\n\n${i}\n${e}`, o =
|
|
326
|
+
async function D(e, t, n, r) {
|
|
327
|
+
let i = (/* @__PURE__ */ new Date()).toUTCString().replace("GMT", "+0000"), a = `GET\n\n\n${i}\n${e}`, o = f.createHmac("sha256", r).update(a).digest("hex"), s = `FS ${t}:${n}:${Buffer.from(o).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "")}`, c = await fetch(`https://api.freemius.com${e}`, { headers: {
|
|
306
328
|
Authorization: s,
|
|
307
329
|
Date: i,
|
|
308
330
|
Accept: "application/json"
|
|
@@ -313,16 +335,16 @@ async function T(e, t, n, r) {
|
|
|
313
335
|
}
|
|
314
336
|
return c.json();
|
|
315
337
|
}
|
|
316
|
-
async function
|
|
317
|
-
let e =
|
|
338
|
+
async function O() {
|
|
339
|
+
let e = m("FREEMIUS_DEVELOPER_ID"), t = m("FREEMIUS_PUBLIC_KEY"), n = m("FREEMIUS_SECRET_KEY"), r = process.env.NEXT_PUBLIC_SUPABASE_URL || process.env.SUPABASE_URL, i = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.SUPABASE_SECRET_KEY;
|
|
318
340
|
if (!e || !t || !n || !r || !i) throw Error("Missing necessary environment variables for Freemius Sync.");
|
|
319
|
-
let a =
|
|
341
|
+
let a = d(r, i, { auth: {
|
|
320
342
|
autoRefreshToken: !1,
|
|
321
343
|
persistSession: !1
|
|
322
|
-
} }), o = (r) =>
|
|
344
|
+
} }), o = (r) => D(r, e, t, n);
|
|
323
345
|
try {
|
|
324
346
|
console.log(`[Freemius Sync] Fetching all plugins for developer ${e}...`);
|
|
325
|
-
let t =
|
|
347
|
+
let t = y(await o(`/v1/developers/${e}/plugins.json`), ["plugins", "plugin"]);
|
|
326
348
|
console.log(`[Freemius Sync] Found ${t.length} plugins. Syncing plans...`);
|
|
327
349
|
let n = 0, { data: r } = await a.from("languages").select("id").eq("code", "en").single(), i = r?.id;
|
|
328
350
|
if (!i) throw Error("English language not found in database. Cannot sync products.");
|
|
@@ -332,7 +354,7 @@ async function E() {
|
|
|
332
354
|
console.warn("[Freemius Sync] Skipping plugin without an id:", r);
|
|
333
355
|
continue;
|
|
334
356
|
}
|
|
335
|
-
let s = await
|
|
357
|
+
let s = await A(a, e, t, r.title || r.name || `Freemius Product ${t}`, o, i);
|
|
336
358
|
n += s;
|
|
337
359
|
}
|
|
338
360
|
return {
|
|
@@ -343,22 +365,22 @@ async function E() {
|
|
|
343
365
|
throw console.error("[Freemius Sync] Global Error:", e), e;
|
|
344
366
|
}
|
|
345
367
|
}
|
|
346
|
-
async function
|
|
347
|
-
let t =
|
|
368
|
+
async function k(e) {
|
|
369
|
+
let t = m("FREEMIUS_DEVELOPER_ID"), n = m("FREEMIUS_PUBLIC_KEY"), r = m("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;
|
|
348
370
|
if (!t || !n || !r || !i || !a) throw Error("Missing environment variables for Freemius Sync.");
|
|
349
|
-
let o =
|
|
350
|
-
if (!
|
|
351
|
-
let
|
|
371
|
+
let o = d(i, a), s = (e) => D(e, t, n, r), { data: c } = await o.from("languages").select("id").eq("code", "en").single(), l = c?.id;
|
|
372
|
+
if (!l) throw Error("English language not found in database. Cannot sync products.");
|
|
373
|
+
let u = await s(`/v1/developers/${t}/plugins/${e}.json`), f = u.plugin ?? u;
|
|
352
374
|
return {
|
|
353
375
|
success: !0,
|
|
354
|
-
count: await
|
|
376
|
+
count: await A(o, t, e, f.title || f.name || `Freemius Product ${e}`, s, l)
|
|
355
377
|
};
|
|
356
378
|
}
|
|
357
|
-
async function
|
|
379
|
+
async function A(e, t, n, r, i, a) {
|
|
358
380
|
console.log(`[Freemius Sync] Fetching plans for plugin: ${r} (${n})...`);
|
|
359
381
|
let o = 0;
|
|
360
382
|
try {
|
|
361
|
-
let s =
|
|
383
|
+
let s = y(await i(`/v1/developers/${t}/plugins/${n}/plans.json`), ["plans", "plan"]);
|
|
362
384
|
console.log(`[Freemius Sync] Received ${s.length} plans for plugin ${n}.`);
|
|
363
385
|
for (let c of s) {
|
|
364
386
|
let s = c.id?.toString();
|
|
@@ -378,25 +400,25 @@ async function O(e, t, n, r, i, a) {
|
|
|
378
400
|
} catch (e) {
|
|
379
401
|
console.warn(`[Freemius Sync] Could not fetch trial details for plan ${s}:`, e instanceof Error ? e.message : e);
|
|
380
402
|
}
|
|
381
|
-
let f =
|
|
403
|
+
let f = g(d.trial_period), p = f > 0 && _(d.is_require_subscription), m = 0, h = [];
|
|
382
404
|
try {
|
|
383
|
-
if (
|
|
405
|
+
if (h = y(await i(`/v1/developers/${t}/plugins/${n}/plans/${s}/pricing.json`), [
|
|
384
406
|
"pricing",
|
|
385
407
|
"prices",
|
|
386
408
|
"pricings"
|
|
387
|
-
]),
|
|
388
|
-
let e =
|
|
389
|
-
|
|
409
|
+
]), h.length > 0) {
|
|
410
|
+
let e = h[0];
|
|
411
|
+
m = x(b(e.annual_price) ?? b(e.monthly_price) ?? b(e.lifetime_price));
|
|
390
412
|
}
|
|
391
|
-
console.log(`[Freemius Sync] Plan: ${u} -> Resolved Price (cents): ${
|
|
413
|
+
console.log(`[Freemius Sync] Plan: ${u} -> Resolved Price (cents): ${m}`);
|
|
392
414
|
} catch (e) {
|
|
393
415
|
console.warn(`[Freemius Sync] Could not fetch pricing for plan ${s}:`, e instanceof Error ? e.message : e);
|
|
394
416
|
}
|
|
395
|
-
let
|
|
417
|
+
let v = `${r}-${u}`.toLowerCase().replace(/[^\w\s-]/g, "").replace(/[\s_]+/g, "-").replace(/^-+|-+$/g, ""), C = {
|
|
396
418
|
title: `${r} - ${u}`,
|
|
397
|
-
slug:
|
|
419
|
+
slug: v,
|
|
398
420
|
short_description: c.description || "",
|
|
399
|
-
price:
|
|
421
|
+
price: m,
|
|
400
422
|
product_type: "digital",
|
|
401
423
|
payment_provider: "freemius",
|
|
402
424
|
freemius_plan_id: s,
|
|
@@ -430,13 +452,13 @@ async function O(e, t, n, r, i, a) {
|
|
|
430
452
|
}).select("id").single();
|
|
431
453
|
n && console.warn(`[Freemius Sync] Could not insert local plan for ${C.sku}:`, n.message || n), t && (k = t.id);
|
|
432
454
|
}
|
|
433
|
-
if (k &&
|
|
434
|
-
let n =
|
|
455
|
+
if (k && h.length > 0) for (let t of h) {
|
|
456
|
+
let n = S(t.licenses ?? t.license_quota ?? t.quota), { data: r, error: i } = await e.from("freemius_pricing").select("id").eq("plan_id", k).eq("license_quota", n).maybeSingle();
|
|
435
457
|
i && console.warn(`[Freemius Sync] Could not check pricing for plan ${k}, quota ${n}:`, i.message || i);
|
|
436
458
|
let a = {
|
|
437
|
-
api_monthly_price:
|
|
438
|
-
api_annual_price:
|
|
439
|
-
api_lifetime_price:
|
|
459
|
+
api_monthly_price: b(t.monthly_price),
|
|
460
|
+
api_annual_price: b(t.annual_price),
|
|
461
|
+
api_lifetime_price: b(t.lifetime_price),
|
|
440
462
|
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
441
463
|
};
|
|
442
464
|
if (r) {
|
|
@@ -459,4 +481,4 @@ async function O(e, t, n, r, i, a) {
|
|
|
459
481
|
return o;
|
|
460
482
|
}
|
|
461
483
|
//#endregion
|
|
462
|
-
export {
|
|
484
|
+
export { E as FreemiusProvider, C as parseFreemiusCheckoutCredentialsMap, m as readFreemiusEnvValue, w as resolveFreemiusCheckoutCredentials, O as syncFreemiusProductsToSupabase, k as syncSingleFreemiusProduct };
|
package/lib/providers/stripe.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const e=require("../types.cjs"),t=require("../currency.cjs"),n=require("../coupons.cjs"),r=require("../coupon-server.cjs"),i=require("../countries.cjs"),a=require("../variation-utils.cjs"),o=require("../inventory-settings.cjs"),s=require("../tax-calculation.cjs"),c=require("../customer.cjs"),l=require("../customer-addresses.cjs"),u=require("../order-tax-details.cjs"),d=require("../stripe/client.cjs");let ee=require("@supabase/supabase-js");var te=i.countries.map(e=>e.code),f=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`.split(`.`));function p(e){if(e)return{line1:e.line1||void 0,line2:e.line2||void 0,city:e.city||void 0,state:e.state||void 0,postal_code:e.postal_code||void 0,country:e.country_code||void 0}}function ne(e){let t=e?.trim().replace(`_`,`-`);if(!t)return;let n=[t,t.toLowerCase(),t.split(`-`)[0].toLowerCase()];for(let e of n)if(f.has(e))return e}function re(e,t){return a.resolveTranslatedText(e.name,e.name_translations||null,t)}async function ie(e){if(!e.email)return null;let t=await d.getStripeClient(),n=p(e.shippingAddress),r=e.shippingAddress?.recipient_name||e.billingAddress?.recipient_name||void 0,i={email:e.email,name:e.billingAddress?.recipient_name||e.shippingAddress?.recipient_name||void 0,phone:e.phone||void 0,address:p(e.billingAddress),metadata:e.userId?{userId:e.userId}:void 0,...n&&r?{shipping:{name:r,phone:e.phone||void 0,address:n}}:{}};try{let n=(await t.customers.list({email:e.email,limit:1})).data[0];return n?(await t.customers.update(n.id,i),n.id):(await t.customers.create(i)).id}catch(e){return console.error(`Failed to upsert Stripe customer for checkout prefill:`,e),null}}var m=class{getProviderName(){return`Stripe`}async createCheckoutSession({items:i,customerEmail:a,customerPhone:f,userId:p,billingAddress:m,shippingAddress:h,shippingMethodId:g,currencyCode:ae,locale:_,couponCode:v,couponContextItems:y}){let oe=await d.getStripeClient(),b=process.env.NEXT_PUBLIC_SUPABASE_URL||process.env.SUPABASE_URL,x=process.env.SUPABASE_SERVICE_ROLE_KEY||process.env.SUPABASE_SECRET_KEY;if(!b||!x)return console.error(`Missing Supabase credentials for checkout (Service Key required).`),{error:`Internal Server Error`,url:null};let S=(0,ee.createClient)(b,x),C=process.env.NEXT_PUBLIC_URL||`http://localhost:4200`,se=i.some(t=>!e.isDigitalItem(t));if(!i.length)return{error:`Cart is empty`,url:null};let{data:ce,error:le}=await S.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}),w=ce??[];if(le||w.length===0)return console.error(`Error fetching currencies for checkout:`,le),{error:`Failed to resolve store currencies`,url:null};let T=t.getDefaultCurrency(w),E=w.find(e=>e.code===(ae||``).toUpperCase())??T,D=E.code.toLowerCase(),O=await o.getEcommerceInventorySettings(S),ue=i.map(e=>e.product_id),k=i.map(e=>e.variant_id).filter(e=>!!e),{data:A,error:j}=await S.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`,ue);if(j||!A)return console.error(`Error fetching products for validation:`,j),{error:`Failed to validate product prices`,url:null};let{data:de,error:M}=k.length?await S.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`,k):{data:[],error:null};if(M)return console.error(`Error fetching variants for validation:`,M),{error:`Failed to validate product variants`,url:null};let N=new Map(A.map(e=>[e.id,e])),P=new Map((de||[]).map(e=>[e.id,e])),F=new Set,I=new Map;for(let e of i){let t=N.get(e.product_id);if(!t)continue;let n=(e.variant_id?P.get(e.variant_id):null)?.sku||t.sku;n&&(F.add(n),I.set(n,(I.get(n)??0)+e.quantity))}let{data:fe,error:L}=F.size?await S.from(`inventory_items`).select(`sku, quantity`).in(`sku`,[...F]):{data:[],error:null};if(L)return console.error(`Error fetching SKU inventory for validation:`,L),{error:`Failed to validate SKU inventory`,url:null};let R=new Map((fe||[]).map(e=>[e.sku,Math.max(0,e.quantity??0)])),z=[],B=[],pe=[],V=0,H=0,U=null;if(v){let e=await r.getCouponQuote({client:S,code:v,items:y&&y.length>0?y:i,currencyCode:E.code});if(!e.success)return{error:e.error,errorKey:e.errorKey,errorStatus:400,url:null};U=e.quote}let me=r.getQuoteLineDiscountMap(U),he=e=>{let t=e.unitAmount*e.quantity,n=Math.max(0,t-e.lineDiscount);if(n<=0)return;let r=Math.floor(n/e.quantity),i=n-r*e.quantity,a=(t,n)=>{n<=0||t<=0||z.push({price_data:{currency:e.currency,product_data:{name:e.name,tax_code:s.getStripeTaxCodeForProduct(e.isTaxable),metadata:{productId:e.productId,variantId:e.variantId||``}},tax_behavior:`exclusive`,unit_amount:t},quantity:n})};a(r,e.quantity-i),a(r+1,i)};for(let e of i){let r=N.get(e.product_id);if(!r)return console.warn(`Product ${e.product_id} not found in DB.`),{url:null,...o.createInventoryUnavailableError(e.title)};let i=t.resolveEffectivePriceForCurrency({prices:t.normalizePriceMap(r.prices),salePrices:r.sale_prices||{},fallbackPrice:r.price,fallbackSalePrice:r.sale_price,saleStartAt:r.sale_start_at,saleEndAt:r.sale_end_at,scheduledPrice:r.scheduled_price,scheduledPrices:t.normalizePriceMap(r.scheduled_prices),scheduledPriceAt:r.scheduled_price_at,currencyCode:E.code,currencies:w}),a=i.sale_price??i.price,s=r.title,c=null;if(e.variant_id){let n=P.get(e.variant_id);if(!n||n.product_id!==e.product_id)return{url:null,...o.createInventoryUnavailableError(e.title)};let i=I.get(n.sku)??e.quantity,l=R.has(n.sku)?R.get(n.sku)??0:Math.max(0,n.stock_quantity??0);if(O.trackQuantities&&i>l)return{url:null,...o.createInventoryInsufficientError(e.title,l)};let u=t.resolveEffectivePriceForCurrency({prices:t.normalizePriceMap(n.prices),salePrices:n.sale_prices||{},fallbackPrice:n.price,fallbackSalePrice:n.sale_price,saleStartAt:n.sale_start_at,saleEndAt:n.sale_end_at,scheduledPrice:n.scheduled_price,scheduledPrices:t.normalizePriceMap(n.scheduled_prices),scheduledPriceAt:n.scheduled_price_at,currencyCode:E.code,currencies:w});a=u.sale_price??u.price,c=n.id,s=e.variant_label?`${r.title} - ${e.variant_label}`:`${r.title} - ${n.sku}`}else{let t=I.get(r.sku)??e.quantity,n=R.has(r.sku)?R.get(r.sku)??0:Math.max(0,r.stock??0);if(O.trackQuantities&&t>n)return{url:null,...o.createInventoryInsufficientError(e.title,n)}}let l=r.is_taxable??!0;if(a<0)return{error:`A product variation produced an invalid price.`,url:null};let u=Math.min(a*e.quantity,me.get(n.getCartLineCouponKey(e))??0);he({name:s,currency:D,productId:r.id,variantId:c,isTaxable:l,unitAmount:a,quantity:e.quantity,lineDiscount:u}),V+=a*e.quantity,H+=u,pe.push({product_id:r.id,quantity:e.quantity,price_at_purchase:a,variant_id:c}),B.push({product_id:r.id,quantity:e.quantity,unit_amount:a,discount_amount:u,is_taxable:l})}if(z.length===0&&H>=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(z.length===0)return{error:`No valid items in cart`,url:null};let W=0,G=null;if(g){let{data:e,error:n}=await S.from(`shipping_zone_methods`).select(`id, name, name_translations, cost_amount, cost_currency`).eq(`id`,g).single();if(n)return console.error(`Failed to load shipping method:`,n),{error:`Failed to load shipping method`,url:null};W=t.convertMinorUnitAmount({amount:e.cost_amount??0,fromCurrencyCode:e.cost_currency||T.code,toCurrencyCode:E.code,currencies:w}),G=re(e,_)}let K=se?h??m:m,q;try{q=await s.calculateCheckoutTaxes(S,{items:B,destination:{country_code:K?.country_code,state:K?.state}})}catch(e){return console.error(`Failed to calculate checkout taxes:`,e),{error:`Failed to calculate taxes`,url:null}}W>0&&G&&z.push({price_data:{currency:D,product_data:{name:G,tax_code:q.enabled&&q.mode===`automatic`?s.STRIPE_TAX_CODE_SHIPPING:s.STRIPE_TAX_CODE_NONTAXABLE},tax_behavior:`exclusive`,unit_amount:W},quantity:1});let J=q.enabled&&q.mode===`manual`&&!q.isPendingExternalCalculation?q.amount:0;J>0&&z.push({price_data:{currency:D,product_data:{name:`Tax`,tax_code:s.STRIPE_TAX_CODE_NONTAXABLE},tax_behavior:`exclusive`,unit_amount:J},quantity:1});let ge=c.normalizeOrderCustomerDetails({email:a,phone:f,name:m?.recipient_name,billing:m,shipping:h}),Y=E.code,_e=u.buildOrderTaxDetailsFromCalculation({calculation:q,subtotal:V,shippingTotal:W,total:Math.max(0,V-H)+W+J,currency:Y}),ve=Math.max(0,V-H)+W+J,{data:X,error:Z}=await S.from(`orders`).insert({status:`pending`,total:ve,currency:Y,exchange_rate_at_purchase:E.exchange_rate,subtotal:V,shipping_total:W,tax_total:J,tax_details:_e,coupon_id:U?.couponId??null,coupon_code:U?.code??null,discount_total:H,discount_details:U?{code:U.code,discount_type:U.discountType,discount_amount:U.discountAmount,provider:`stripe`,provider_discounts:U.providerDiscounts,line_discounts:U.lineDiscounts}:null,provider:`stripe`,user_id:p,customer_details:ge}).select(`id`).single();if(Z||!X)return console.error(`Failed to create pending order:`,Z),{error:`Failed to initiate order`,url:null};let Q=X.id,{error:ye}=await S.from(`order_items`).insert(pe.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(ye)return console.error(`Failed to insert order items:`,ye),await S.from(`orders`).update({status:`failed`}).eq(`id`,Q),{error:`Failed to record order items`,url:null};if(p)try{await l.upsertDefaultUserAddresses({userId:p,billingAddress:m,shippingAddress:h,client:S}),await l.fillMissingUserProfileCheckoutDetails({userId:p,fullName:m?.recipient_name??h?.recipient_name??null,phone:f,client:S})}catch(e){console.error(`Failed to sync checkout profile defaults before checkout:`,e)}U&&await r.recordCouponRedemption({client:S,quote:U,orderId:Q,provider:`stripe`,discountTotal:H,userId:p,customerEmail:a,metadata:{currency:E.code,subtotal:V,shipping_total:W,tax_total:J}});let $=await ie({email:a,phone:f,userId:p,billingAddress:m,shippingAddress:h});try{let e=await oe.checkout.sessions.create({mode:`payment`,success_url:`${C}/checkout/success?session_id={CHECKOUT_SESSION_ID}`,cancel_url:`${C}/checkout`,locale:ne(_),line_items:z,automatic_tax:q.enabled&&q.mode===`automatic`?{enabled:!0}:void 0,billing_address_collection:`auto`,customer:$||void 0,customer_email:$?void 0:a||void 0,customer_creation:$?void 0:`if_required`,customer_update:$?{name:`auto`,address:`auto`,shipping:`auto`}:void 0,shipping_address_collection:se?{allowed_countries:te}:void 0,metadata:{orderId:Q,taxMode:q.mode,currencyCode:E.code,couponCode:U?.code||``}}),{error:t}=await S.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 S.from(`orders`).update({status:`failed`}).eq(`id`,Q),e?.name===`StripeNotConfiguredError`||e?.type===`StripeAuthenticationError`?{error:`This store is not able to take payments right now. Please contact the seller to complete your purchase.`,errorKey:`ecommerce.checkout_payments_unavailable`,errorStatus:503,url:null}:{error:`Something went wrong while preparing your checkout. Please try again.`,errorKey:`ecommerce.checkout_internal_server_error`,url:null}}}};exports.StripeProvider=m;
|
|
1
|
+
const e=require("../types.cjs"),t=require("../currency.cjs"),n=require("../coupons.cjs"),r=require("../coupon-server.cjs"),i=require("../countries.cjs"),a=require("../shipping/checkout-shipping-method.cjs"),o=require("../inventory-settings.cjs"),s=require("../tax-calculation.cjs"),c=require("../customer.cjs"),l=require("../customer-addresses.cjs"),u=require("./checkout-items.cjs"),ee=require("../order-tax-details.cjs"),d=require("../stripe/client.cjs");let te=require("@supabase/supabase-js");var ne=i.countries.map(e=>e.code),f=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`.split(`.`));function p(e){if(e)return{line1:e.line1||void 0,line2:e.line2||void 0,city:e.city||void 0,state:e.state||void 0,postal_code:e.postal_code||void 0,country:e.country_code||void 0}}function re(e){let t=e?.trim().replace(`_`,`-`);if(!t)return;let n=[t,t.toLowerCase(),t.split(`-`)[0].toLowerCase()];for(let e of n)if(f.has(e))return e}async function ie(e){if(!e.email)return null;let t=await d.getStripeClient(),n=p(e.shippingAddress),r=e.shippingAddress?.recipient_name||e.billingAddress?.recipient_name||void 0,i={email:e.email,name:e.billingAddress?.recipient_name||e.shippingAddress?.recipient_name||void 0,phone:e.phone||void 0,address:p(e.billingAddress),metadata:e.userId?{userId:e.userId}:void 0,...n&&r?{shipping:{name:r,phone:e.phone||void 0,address:n}}:{}};try{let n=(await t.customers.list({email:e.email,limit:1})).data[0];return n?(await t.customers.update(n.id,i),n.id):(await t.customers.create(i)).id}catch(e){return console.error(`Failed to upsert Stripe customer for checkout prefill:`,e),null}}var m=class{getProviderName(){return`Stripe`}async createCheckoutSession({items:i,customerEmail:f,customerPhone:p,userId:m,billingAddress:h,shippingAddress:g,shippingMethodId:_,currencyCode:ae,locale:v,couponCode:y,couponContextItems:b}){let oe=await d.getStripeClient(),x=process.env.NEXT_PUBLIC_SUPABASE_URL||process.env.SUPABASE_URL,S=process.env.SUPABASE_SERVICE_ROLE_KEY||process.env.SUPABASE_SECRET_KEY;if(!x||!S)return console.error(`Missing Supabase credentials for checkout (Service Key required).`),{error:`Internal Server Error`,url:null};let C=(0,te.createClient)(x,S),se=process.env.NEXT_PUBLIC_URL||`http://localhost:4200`;if(!i?.length)return{error:`Cart is empty`,url:null};let w=u.normalizeCheckoutItems(i);if(!w.ok)return{url:null,...w.error};let T=w.items,ce=T;if(b&&b.length>0){let e=u.normalizeCheckoutItems(b);if(!e.ok)return{url:null,...e.error};ce=e.items}let E=T.some(t=>!e.isDigitalItem(t)),{data:le,error:ue}=await C.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}),D=le??[];if(ue||D.length===0)return console.error(`Error fetching currencies for checkout:`,ue),{error:`Failed to resolve store currencies`,url:null};let de=t.getDefaultCurrency(D),O=D.find(e=>e.code===(ae||``).toUpperCase())??de,k=O.code.toLowerCase(),A=await o.getEcommerceInventorySettings(C),fe=T.map(e=>e.product_id),j=T.map(e=>e.variant_id).filter(e=>!!e),{data:M,error:N}=await C.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(N||!M)return console.error(`Error fetching products for validation:`,N),{error:`Failed to validate product prices`,url:null};let{data:pe,error:P}=j.length?await C.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`,j):{data:[],error:null};if(P)return console.error(`Error fetching variants for validation:`,P),{error:`Failed to validate product variants`,url:null};let F=new Map(M.map(e=>[e.id,e])),I=new Map((pe||[]).map(e=>[e.id,e])),L=new Set,R=new Map;for(let e of T){let t=F.get(e.product_id);if(!t)continue;let n=(e.variant_id?I.get(e.variant_id):null)?.sku||t.sku;n&&(L.add(n),R.set(n,(R.get(n)??0)+e.quantity))}let{data:me,error:z}=L.size?await C.from(`inventory_items`).select(`sku, quantity`).in(`sku`,[...L]):{data:[],error:null};if(z)return console.error(`Error fetching SKU inventory for validation:`,z),{error:`Failed to validate SKU inventory`,url:null};let B=new Map((me||[]).map(e=>[e.sku,Math.max(0,e.quantity??0)])),V=[],H=[],U=[],W=0,G=0,K=null;if(y){let e=await r.getCouponQuote({client:C,code:y,items:ce,currencyCode:O.code});if(!e.success)return{error:e.error,errorKey:e.errorKey,errorStatus:400,url:null};K=e.quote}let he=r.getQuoteLineDiscountMap(K),ge=e=>{let t=e.unitAmount*e.quantity,n=Math.max(0,t-e.lineDiscount);if(n<=0)return;let r=Math.floor(n/e.quantity),i=n-r*e.quantity,a=(t,n)=>{n<=0||t<=0||V.push({price_data:{currency:e.currency,product_data:{name:e.name,tax_code:s.getStripeTaxCodeForProduct(e.isTaxable),metadata:{productId:e.productId,variantId:e.variantId||``}},tax_behavior:`exclusive`,unit_amount:t},quantity:n})};a(r,e.quantity-i),a(r+1,i)};for(let e of T){let r=F.get(e.product_id);if(!r)return console.warn(`Product ${e.product_id} not found in DB.`),{url:null,...o.createInventoryUnavailableError(e.title)};let i=t.resolveEffectivePriceForCurrency({prices:t.normalizePriceMap(r.prices),salePrices:r.sale_prices||{},fallbackPrice:r.price,fallbackSalePrice:r.sale_price,saleStartAt:r.sale_start_at,saleEndAt:r.sale_end_at,scheduledPrice:r.scheduled_price,scheduledPrices:t.normalizePriceMap(r.scheduled_prices),scheduledPriceAt:r.scheduled_price_at,currencyCode:O.code,currencies:D}),a=i.sale_price??i.price,s=r.title,c=null;if(e.variant_id){let n=I.get(e.variant_id);if(!n||n.product_id!==e.product_id)return{url:null,...o.createInventoryUnavailableError(e.title)};let i=R.get(n.sku)??e.quantity,l=B.has(n.sku)?B.get(n.sku)??0:Math.max(0,n.stock_quantity??0);if(A.trackQuantities&&i>l)return{url:null,...o.createInventoryInsufficientError(e.title,l)};let u=t.resolveEffectivePriceForCurrency({prices:t.normalizePriceMap(n.prices),salePrices:n.sale_prices||{},fallbackPrice:n.price,fallbackSalePrice:n.sale_price,saleStartAt:n.sale_start_at,saleEndAt:n.sale_end_at,scheduledPrice:n.scheduled_price,scheduledPrices:t.normalizePriceMap(n.scheduled_prices),scheduledPriceAt:n.scheduled_price_at,currencyCode:O.code,currencies:D});a=u.sale_price??u.price,c=n.id,s=e.variant_label?`${r.title} - ${e.variant_label}`:`${r.title} - ${n.sku}`}else{let t=R.get(r.sku)??e.quantity,n=B.has(r.sku)?B.get(r.sku)??0:Math.max(0,r.stock??0);if(A.trackQuantities&&t>n)return{url:null,...o.createInventoryInsufficientError(e.title,n)}}let l=r.is_taxable??!0;if(a<0)return{error:`A product variation produced an invalid price.`,url:null};let u=Math.min(a*e.quantity,he.get(n.getCartLineCouponKey(e))??0);ge({name:s,currency:k,productId:r.id,variantId:c,isTaxable:l,unitAmount:a,quantity:e.quantity,lineDiscount:u}),W+=a*e.quantity,G+=u,U.push({product_id:r.id,quantity:e.quantity,price_at_purchase:a,variant_id:c}),H.push({product_id:r.id,quantity:e.quantity,unit_amount:a,discount_amount:u,is_taxable:l})}if(V.length===0&&G>=W)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(V.length===0)return{error:`No valid items in cart`,url:null};let q=0,J=null;if(E){let e=a.normalizeShippingDestination({country:g?.country_code,state:g?.state,postal_code:g?.postal_code});if(!e)return{url:null,...a.createShippingAddressRequiredError()};let t={destination:e,cartTotal:Math.max(0,W-G),currencies:D,currencyCode:O.code};if(_){let e=await a.resolveCheckoutShippingMethod(C,{...t,methodId:_,languageCode:v});if(!e.ok)return e.reason===`load_failed`?(console.error(`Failed to load shipping method:`,e.error),{error:`Failed to load shipping method`,url:null}):(console.warn(`Checkout rejected shipping method ${_}: ${e.reason}`),{url:null,...a.createInvalidShippingMethodError()});q=e.method.amount,J=e.method.name}else{let e=await a.listEligibleShippingMethods(C,t);if(!e.ok)return console.error(`Failed to load shipping methods:`,e.error),{error:`Failed to load shipping method`,url:null};if(e.methods.length>0)return{url:null,...a.createShippingMethodRequiredError()}}}let _e=E?g??h:h,Y;try{Y=await s.calculateCheckoutTaxes(C,{items:H,destination:{country_code:_e?.country_code,state:_e?.state}})}catch(e){return console.error(`Failed to calculate checkout taxes:`,e),{error:`Failed to calculate taxes`,url:null}}q>0&&J&&V.push({price_data:{currency:k,product_data:{name:J,tax_code:Y.enabled&&Y.mode===`automatic`?s.STRIPE_TAX_CODE_SHIPPING:s.STRIPE_TAX_CODE_NONTAXABLE},tax_behavior:`exclusive`,unit_amount:q},quantity:1});let X=Y.enabled&&Y.mode===`manual`&&!Y.isPendingExternalCalculation?Y.amount:0;X>0&&V.push({price_data:{currency:k,product_data:{name:`Tax`,tax_code:s.STRIPE_TAX_CODE_NONTAXABLE},tax_behavior:`exclusive`,unit_amount:X},quantity:1});let ve=c.normalizeOrderCustomerDetails({email:f,phone:p,name:h?.recipient_name,billing:h,shipping:g}),Z=O.code,ye=ee.buildOrderTaxDetailsFromCalculation({calculation:Y,subtotal:W,shippingTotal:q,total:Math.max(0,W-G)+q+X,currency:Z}),be=Math.max(0,W-G)+q+X,{data:xe,error:Se}=await C.from(`orders`).insert({status:`pending`,total:be,currency:Z,exchange_rate_at_purchase:O.exchange_rate,subtotal:W,shipping_total:q,tax_total:X,tax_details:ye,coupon_id:K?.couponId??null,coupon_code:K?.code??null,discount_total:G,discount_details:K?{code:K.code,discount_type:K.discountType,discount_amount:K.discountAmount,provider:`stripe`,provider_discounts:K.providerDiscounts,line_discounts:K.lineDiscounts}:null,provider:`stripe`,user_id:m,customer_details:ve}).select(`id`).single();if(Se||!xe)return console.error(`Failed to create pending order:`,Se),{error:`Failed to initiate order`,url:null};let Q=xe.id,{error:Ce}=await C.from(`order_items`).insert(U.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(Ce)return console.error(`Failed to insert order items:`,Ce),await C.from(`orders`).update({status:`failed`}).eq(`id`,Q),{error:`Failed to record order items`,url:null};if(m)try{await l.upsertDefaultUserAddresses({userId:m,billingAddress:h,shippingAddress:g,client:C}),await l.fillMissingUserProfileCheckoutDetails({userId:m,fullName:h?.recipient_name??g?.recipient_name??null,phone:p,client:C})}catch(e){console.error(`Failed to sync checkout profile defaults before checkout:`,e)}if(K&&!(await r.recordCouponRedemption({client:C,quote:K,orderId:Q,provider:`stripe`,discountTotal:G,userId:m,customerEmail:f,metadata:{currency:O.code,subtotal:W,shipping_total:q,tax_total:X}})).success)return await C.from(`orders`).update({status:`failed`}).eq(`id`,Q),{url:null,error:`Coupon is no longer available.`,errorKey:`ecommerce.coupon_invalid`};let $=await ie({email:f,phone:p,userId:m,billingAddress:h,shippingAddress:g});try{let e=await oe.checkout.sessions.create({mode:`payment`,success_url:`${se}/checkout/success?session_id={CHECKOUT_SESSION_ID}`,cancel_url:`${se}/checkout`,locale:re(v),line_items:V,automatic_tax:Y.enabled&&Y.mode===`automatic`?{enabled:!0}:void 0,billing_address_collection:`auto`,customer:$||void 0,customer_email:$?void 0:f||void 0,customer_creation:$?void 0:`if_required`,customer_update:$?{name:`auto`,address:`auto`,shipping:`auto`}:void 0,shipping_address_collection:E?{allowed_countries:ne}:void 0,metadata:{orderId:Q,taxMode:Y.mode,currencyCode:O.code,couponCode:K?.code||``}}),{error:t}=await C.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 C.from(`orders`).update({status:`failed`}).eq(`id`,Q),e?.name===`StripeNotConfiguredError`||e?.type===`StripeAuthenticationError`?{error:`This store is not able to take payments right now. Please contact the seller to complete your purchase.`,errorKey:`ecommerce.checkout_payments_unavailable`,errorStatus:503,url:null}:{error:`Something went wrong while preparing your checkout. Please try again.`,errorKey:`ecommerce.checkout_internal_server_error`,url:null}}}};exports.StripeProvider=m;
|
|
@@ -2,7 +2,7 @@ import { CheckoutSessionInput } from '../customer';
|
|
|
2
2
|
import { PaymentProvider } from '../types';
|
|
3
3
|
export declare class StripeProvider implements PaymentProvider {
|
|
4
4
|
getProviderName(): string;
|
|
5
|
-
createCheckoutSession({ items:
|
|
5
|
+
createCheckoutSession({ items: submittedCartItems, customerEmail, customerPhone, userId, billingAddress, shippingAddress, shippingMethodId, currencyCode, locale, couponCode, couponContextItems: submittedCouponContextItems, }: CheckoutSessionInput): Promise<{
|
|
6
6
|
url: string | null;
|
|
7
7
|
error?: string;
|
|
8
8
|
errorKey?: string;
|