@nextblock-cms/ecom 0.22.0 → 0.22.2
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/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
package/lib/coupon-server.es.js
CHANGED
|
@@ -1,58 +1,59 @@
|
|
|
1
1
|
import { getDefaultCurrency as e, normalizeCurrencyRecord as t, normalizePriceMap as n, normalizeSalePriceMap as r, resolveEffectivePriceForCurrency as i } from "./currency.es.js";
|
|
2
|
-
import {
|
|
2
|
+
import { COUPON_INVALID_ERROR_KEY as a, COUPON_INVALID_ERROR_MESSAGE as o, emptyProviderDiscounts as s, getCartLineCouponKey as c, normalizeCouponCode as l } from "./coupons.es.js";
|
|
3
|
+
import "server-only";
|
|
3
4
|
//#region src/lib/coupon-server.ts
|
|
4
|
-
function
|
|
5
|
+
function u(e) {
|
|
5
6
|
if (!e) return null;
|
|
6
7
|
let t = new Date(e);
|
|
7
8
|
return Number.isNaN(t.getTime()) ? null : t;
|
|
8
9
|
}
|
|
9
|
-
function
|
|
10
|
+
function d(e, t) {
|
|
10
11
|
return e === "all" || e === t;
|
|
11
12
|
}
|
|
12
|
-
function
|
|
13
|
+
function f(e) {
|
|
13
14
|
return e === "stripe" || e === "freemius" ? e : null;
|
|
14
15
|
}
|
|
15
|
-
async function
|
|
16
|
+
async function p(e) {
|
|
16
17
|
let { data: n, error: r } = await e.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 });
|
|
17
18
|
if (r) throw Error(r.message);
|
|
18
19
|
return (n || []).map((e) => t(e));
|
|
19
20
|
}
|
|
20
|
-
async function
|
|
21
|
-
let { client: a, items:
|
|
22
|
-
if (
|
|
23
|
-
let l =
|
|
24
|
-
if (
|
|
21
|
+
async function m(t) {
|
|
22
|
+
let { client: a, items: o } = t, s = await p(a);
|
|
23
|
+
if (s.length === 0) throw Error("No active currencies are configured.");
|
|
24
|
+
let l = s.find((e) => e.code === (t.currencyCode || "").toUpperCase()) ?? e(s), u = [...new Set(o.map((e) => e.product_id).filter(Boolean))], d = [...new Set(o.map((e) => e.variant_id).filter((e) => !!e))];
|
|
25
|
+
if (u.length === 0) return {
|
|
25
26
|
lines: [],
|
|
26
|
-
currencies:
|
|
27
|
+
currencies: s,
|
|
27
28
|
selectedCurrency: l
|
|
28
29
|
};
|
|
29
|
-
let [{ data: m, error: h }, g] = await Promise.all([a.from("products").select("id, title, price, prices, sale_price, sale_prices, sale_start_at, sale_end_at, scheduled_price, scheduled_prices, scheduled_price_at, product_type, payment_provider, freemius_product_id, freemius_plan_id").in("id",
|
|
30
|
+
let [{ data: m, error: h }, g] = await Promise.all([a.from("products").select("id, title, price, prices, sale_price, sale_prices, sale_start_at, sale_end_at, scheduled_price, scheduled_prices, scheduled_price_at, product_type, payment_provider, freemius_product_id, freemius_plan_id").in("id", u), d.length ? a.from("product_variants").select("id, product_id, price, prices, sale_price, sale_prices, sale_start_at, sale_end_at, scheduled_price, scheduled_prices, scheduled_price_at").in("id", d) : Promise.resolve({
|
|
30
31
|
data: [],
|
|
31
32
|
error: null
|
|
32
33
|
})]);
|
|
33
34
|
if (h) throw Error(h.message);
|
|
34
35
|
if (g.error) throw Error(g.error.message);
|
|
35
36
|
let _ = m || [], v = g.data || [], y = new Map(_.map((e) => [e.id, e])), b = new Map(v.map((e) => [e.id, e])), x = [];
|
|
36
|
-
for (let e of
|
|
37
|
+
for (let e of o) {
|
|
37
38
|
let t = y.get(e.product_id);
|
|
38
39
|
if (!t) continue;
|
|
39
|
-
let a =
|
|
40
|
-
prices: n(
|
|
41
|
-
salePrices: r(
|
|
42
|
-
fallbackPrice:
|
|
43
|
-
fallbackSalePrice:
|
|
44
|
-
saleStartAt:
|
|
45
|
-
saleEndAt:
|
|
46
|
-
scheduledPrice:
|
|
47
|
-
scheduledPrices: n(
|
|
48
|
-
scheduledPriceAt:
|
|
40
|
+
let a = f(t.payment_provider) ?? (t.product_type === "digital" ? "freemius" : "stripe"), o = e.variant_id ? b.get(e.variant_id) : null, u = o && o.product_id === t.id ? o : t, d = i({
|
|
41
|
+
prices: n(u.prices),
|
|
42
|
+
salePrices: r(u.sale_prices),
|
|
43
|
+
fallbackPrice: u.price,
|
|
44
|
+
fallbackSalePrice: u.sale_price,
|
|
45
|
+
saleStartAt: u.sale_start_at,
|
|
46
|
+
saleEndAt: u.sale_end_at,
|
|
47
|
+
scheduledPrice: u.scheduled_price,
|
|
48
|
+
scheduledPrices: n(u.scheduled_prices),
|
|
49
|
+
scheduledPriceAt: u.scheduled_price_at,
|
|
49
50
|
currencyCode: l.code,
|
|
50
|
-
currencies:
|
|
51
|
-
}), p = a === "freemius" ? 1 : Math.max(1, Number(e.quantity) || 1), m = Math.max(0,
|
|
51
|
+
currencies: s
|
|
52
|
+
}), p = a === "freemius" ? 1 : Math.max(1, Number(e.quantity) || 1), m = Math.max(0, d.sale_price ?? d.price);
|
|
52
53
|
x.push({
|
|
53
|
-
key:
|
|
54
|
+
key: c(e),
|
|
54
55
|
product_id: t.id,
|
|
55
|
-
variant_id:
|
|
56
|
+
variant_id: o?.id ?? null,
|
|
56
57
|
title: t.title,
|
|
57
58
|
quantity: p,
|
|
58
59
|
provider: a,
|
|
@@ -63,16 +64,16 @@ async function f(t) {
|
|
|
63
64
|
}
|
|
64
65
|
return {
|
|
65
66
|
lines: x,
|
|
66
|
-
currencies:
|
|
67
|
+
currencies: s,
|
|
67
68
|
selectedCurrency: l
|
|
68
69
|
};
|
|
69
70
|
}
|
|
70
|
-
async function
|
|
71
|
+
async function h(e, t) {
|
|
71
72
|
let { data: n, error: r } = await e.from("coupon_products").select("product_id").eq("coupon_id", t);
|
|
72
73
|
if (r) throw Error(r.message);
|
|
73
74
|
return new Set((n || []).map((e) => e.product_id));
|
|
74
75
|
}
|
|
75
|
-
function
|
|
76
|
+
function g(e, t) {
|
|
76
77
|
let n = e.reduce((e, t) => e + t.subtotal, 0), r = Math.min(t, n);
|
|
77
78
|
if (n <= 0 || r <= 0) return /* @__PURE__ */ new Map();
|
|
78
79
|
let i = /* @__PURE__ */ new Map(), a = 0;
|
|
@@ -81,59 +82,70 @@ function m(e, t) {
|
|
|
81
82
|
a += s, i.set(t.key, s);
|
|
82
83
|
}), i;
|
|
83
84
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
if (!t) return {
|
|
87
|
-
success: !1,
|
|
88
|
-
error: "Enter a coupon code.",
|
|
89
|
-
errorKey: "ecommerce.coupon_code_required"
|
|
90
|
-
};
|
|
91
|
-
if (!e.items.length) return {
|
|
92
|
-
success: !1,
|
|
93
|
-
error: "Add an item to your cart before applying a coupon.",
|
|
94
|
-
errorKey: "ecommerce.coupon_cart_empty"
|
|
95
|
-
};
|
|
96
|
-
let { data: n, error: r } = await e.client.from("coupons").select("id, code, name, provider_scope, discount_type, discount_amount, is_active, starts_at, ends_at, redemption_limit, redemptions_count").ilike("code", t).maybeSingle();
|
|
97
|
-
if (r) throw Error(r.message);
|
|
98
|
-
if (!n) return {
|
|
99
|
-
success: !1,
|
|
85
|
+
var _ = {
|
|
86
|
+
not_found: {
|
|
100
87
|
error: "Coupon code not found.",
|
|
101
88
|
errorKey: "ecommerce.coupon_not_found"
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if (!i.is_active) return {
|
|
105
|
-
success: !1,
|
|
89
|
+
},
|
|
90
|
+
inactive: {
|
|
106
91
|
error: "This coupon is not active.",
|
|
107
92
|
errorKey: "ecommerce.coupon_inactive"
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
success: !1,
|
|
93
|
+
},
|
|
94
|
+
not_started: {
|
|
111
95
|
error: "This coupon is not active yet.",
|
|
112
96
|
errorKey: "ecommerce.coupon_not_started"
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
success: !1,
|
|
97
|
+
},
|
|
98
|
+
expired: {
|
|
116
99
|
error: "This coupon has expired.",
|
|
117
100
|
errorKey: "ecommerce.coupon_expired"
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
success: !1,
|
|
101
|
+
},
|
|
102
|
+
limit_reached: {
|
|
121
103
|
error: "This coupon has reached its redemption limit.",
|
|
122
104
|
errorKey: "ecommerce.coupon_limit_reached"
|
|
105
|
+
},
|
|
106
|
+
not_applicable: {
|
|
107
|
+
error: "This coupon does not apply to the items in your cart.",
|
|
108
|
+
errorKey: "ecommerce.coupon_not_applicable"
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
function v(e, t) {
|
|
112
|
+
return {
|
|
113
|
+
success: !1,
|
|
114
|
+
...t ? _[e] : {
|
|
115
|
+
error: o,
|
|
116
|
+
errorKey: a
|
|
117
|
+
}
|
|
123
118
|
};
|
|
124
|
-
|
|
119
|
+
}
|
|
120
|
+
async function y(e) {
|
|
121
|
+
let t = l(e.code), n = e.detailedErrors === !0;
|
|
122
|
+
if (!t) return {
|
|
123
|
+
success: !1,
|
|
124
|
+
error: "Enter a coupon code.",
|
|
125
|
+
errorKey: "ecommerce.coupon_code_required"
|
|
126
|
+
};
|
|
127
|
+
if (!e.items.length) return {
|
|
128
|
+
success: !1,
|
|
129
|
+
error: "Add an item to your cart before applying a coupon.",
|
|
130
|
+
errorKey: "ecommerce.coupon_cart_empty"
|
|
131
|
+
};
|
|
132
|
+
let { data: r, error: i } = await e.client.from("coupons").select("id, code, name, provider_scope, discount_type, discount_amount, is_active, starts_at, ends_at, redemption_limit, redemptions_count").ilike("code", t).maybeSingle();
|
|
133
|
+
if (i) throw Error(i.message);
|
|
134
|
+
if (!r) return v("not_found", n);
|
|
135
|
+
let a = r, o = /* @__PURE__ */ new Date(), c = u(a.starts_at), f = u(a.ends_at);
|
|
136
|
+
if (!a.is_active) return v("inactive", n);
|
|
137
|
+
if (c && c > o) return v("not_started", n);
|
|
138
|
+
if (f && f <= o) return v("expired", n);
|
|
139
|
+
if (a.redemption_limit !== null && (a.redemptions_count ?? 0) >= a.redemption_limit) return v("limit_reached", n);
|
|
140
|
+
let [{ lines: p }, _] = await Promise.all([m({
|
|
125
141
|
client: e.client,
|
|
126
142
|
items: e.items,
|
|
127
143
|
currencyCode: e.currencyCode
|
|
128
|
-
}),
|
|
129
|
-
if (
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
};
|
|
134
|
-
let y = i.discount_type === "fixed" ? m(v, i.discount_amount) : null, b = a(), x = v.map((e) => {
|
|
135
|
-
let t = i.discount_type === "percent" ? Math.min(e.subtotal, Math.round(e.subtotal * i.discount_amount / 100)) : y?.get(e.key) ?? 0;
|
|
136
|
-
return b[e.provider] += t, {
|
|
144
|
+
}), h(e.client, a.id)]), y = _.size > 0, b = p.filter((e) => e.subtotal > 0 && d(a.provider_scope, e.provider) && (!y || _.has(e.product_id)));
|
|
145
|
+
if (b.length === 0) return v("not_applicable", n);
|
|
146
|
+
let x = a.discount_type === "fixed" ? g(b, a.discount_amount) : null, S = s(), C = b.map((e) => {
|
|
147
|
+
let t = a.discount_type === "percent" ? Math.min(e.subtotal, Math.round(e.subtotal * a.discount_amount / 100)) : x?.get(e.key) ?? 0;
|
|
148
|
+
return S[e.provider] += t, {
|
|
137
149
|
key: e.key,
|
|
138
150
|
product_id: e.product_id,
|
|
139
151
|
variant_id: e.variant_id,
|
|
@@ -147,26 +159,40 @@ async function h(e) {
|
|
|
147
159
|
return {
|
|
148
160
|
success: !0,
|
|
149
161
|
quote: {
|
|
150
|
-
couponId:
|
|
151
|
-
code:
|
|
152
|
-
name:
|
|
153
|
-
discountType:
|
|
154
|
-
discountAmount:
|
|
155
|
-
providerScope:
|
|
156
|
-
eligibleSubtotal:
|
|
157
|
-
discountTotal:
|
|
158
|
-
providerDiscounts:
|
|
159
|
-
lineDiscounts:
|
|
162
|
+
couponId: a.id,
|
|
163
|
+
code: a.code,
|
|
164
|
+
name: a.name,
|
|
165
|
+
discountType: a.discount_type,
|
|
166
|
+
discountAmount: a.discount_amount,
|
|
167
|
+
providerScope: a.provider_scope,
|
|
168
|
+
eligibleSubtotal: b.reduce((e, t) => e + t.subtotal, 0),
|
|
169
|
+
discountTotal: S.stripe + S.freemius,
|
|
170
|
+
providerDiscounts: S,
|
|
171
|
+
lineDiscounts: C
|
|
160
172
|
}
|
|
161
173
|
};
|
|
162
174
|
}
|
|
163
|
-
function
|
|
175
|
+
function b(e) {
|
|
164
176
|
let t = /* @__PURE__ */ new Map();
|
|
165
177
|
for (let n of e?.lineDiscounts ?? []) t.set(n.key, n.discount);
|
|
166
178
|
return t;
|
|
167
179
|
}
|
|
168
|
-
async function
|
|
169
|
-
let
|
|
180
|
+
async function x(e) {
|
|
181
|
+
let t = (e) => ({
|
|
182
|
+
success: !1,
|
|
183
|
+
reason: e,
|
|
184
|
+
error: o,
|
|
185
|
+
errorKey: a
|
|
186
|
+
}), { data: n, error: r } = await e.client.from("coupon_redemptions").select("id").eq("order_id", e.orderId).maybeSingle();
|
|
187
|
+
if (r) return console.error("Failed to look up existing coupon redemption:", r), t("error");
|
|
188
|
+
if (n) return {
|
|
189
|
+
success: !0,
|
|
190
|
+
alreadyRecorded: !0
|
|
191
|
+
};
|
|
192
|
+
let { data: i, error: s } = await e.client.rpc("redeem_coupon_atomically", { p_coupon_id: e.quote.couponId });
|
|
193
|
+
if (s) return console.error("Failed to reserve coupon redemption:", s), t("error");
|
|
194
|
+
if (i !== !0) return t("limit_reached");
|
|
195
|
+
let { error: c } = await e.client.from("coupon_redemptions").insert({
|
|
170
196
|
coupon_id: e.quote.couponId,
|
|
171
197
|
order_id: e.orderId,
|
|
172
198
|
coupon_code: e.quote.code,
|
|
@@ -176,15 +202,49 @@ async function _(e) {
|
|
|
176
202
|
customer_email: e.customerEmail || null,
|
|
177
203
|
metadata: e.metadata || {}
|
|
178
204
|
});
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
205
|
+
return c && console.error("Failed to record coupon redemption:", c), {
|
|
206
|
+
success: !0,
|
|
207
|
+
alreadyRecorded: !1
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
var S = 30, C = 12 / 60, w = 5e3, T = "unknown";
|
|
211
|
+
function E(e) {
|
|
212
|
+
let t = e?.capacity ?? S, n = e?.refillPerSecond ?? C, r = e?.maxBuckets ?? w, i = t / n * 1e3, a = /* @__PURE__ */ new Map();
|
|
213
|
+
function o(e) {
|
|
214
|
+
for (let [t, n] of a) e - n.updatedAt >= i && a.delete(t);
|
|
215
|
+
for (let e of a.keys()) {
|
|
216
|
+
if (a.size < r) break;
|
|
217
|
+
a.delete(e);
|
|
218
|
+
}
|
|
182
219
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
220
|
+
return {
|
|
221
|
+
consume(e, i = Date.now()) {
|
|
222
|
+
let s = a.get(e);
|
|
223
|
+
if (!s) a.size >= r && o(i), s = {
|
|
224
|
+
tokens: t,
|
|
225
|
+
updatedAt: i
|
|
226
|
+
}, a.set(e, s);
|
|
227
|
+
else {
|
|
228
|
+
let e = Math.max(0, i - s.updatedAt) / 1e3;
|
|
229
|
+
s.tokens = Math.min(t, s.tokens + e * n), s.updatedAt = i;
|
|
230
|
+
}
|
|
231
|
+
return s.tokens < 1 ? !1 : (--s.tokens, !0);
|
|
232
|
+
},
|
|
233
|
+
get size() {
|
|
234
|
+
return a.size;
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
var D = E();
|
|
239
|
+
function O(e, t) {
|
|
240
|
+
return D.consume(e, t);
|
|
241
|
+
}
|
|
242
|
+
function k(e) {
|
|
243
|
+
let [t] = (e ?? "").split(",");
|
|
244
|
+
return (t?.trim() ?? "") || null;
|
|
245
|
+
}
|
|
246
|
+
function A(e) {
|
|
247
|
+
return k(e.get("x-vercel-forwarded-for")) ?? k(e.get("x-real-ip")) ?? k(e.get("x-forwarded-for")) ?? "unknown";
|
|
188
248
|
}
|
|
189
249
|
//#endregion
|
|
190
|
-
export {
|
|
250
|
+
export { T as COUPON_VALIDATION_UNKNOWN_CLIENT_KEY, O as consumeCouponValidationToken, E as createCouponValidationThrottle, y as getCouponQuote, b as getQuoteLineDiscountMap, x as recordCouponRedemption, A as resolveCouponValidationThrottleKey };
|
package/lib/coupons.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
function
|
|
1
|
+
var e=`ecommerce.coupon_invalid`,t=`This coupon code is invalid or can't be applied to your cart.`,n=`ecommerce.coupon_validation_failed`,r=`Too many coupon attempts. Wait a moment and try again.`;function i(e){return(e||``).trim().replace(/\s+/g,``).toUpperCase()}function a(e){return`${e.product_id}:${e.variant_id||`base`}`}function o(){return{stripe:0,freemius:0}}exports.COUPON_INVALID_ERROR_KEY=e,exports.COUPON_INVALID_ERROR_MESSAGE=t,exports.COUPON_THROTTLED_ERROR_KEY=n,exports.COUPON_THROTTLED_ERROR_MESSAGE=r,exports.emptyProviderDiscounts=o,exports.getCartLineCouponKey=a,exports.normalizeCouponCode=i;
|
package/lib/coupons.d.ts
CHANGED
|
@@ -36,6 +36,19 @@ export type CouponQuoteResult = {
|
|
|
36
36
|
error: string;
|
|
37
37
|
errorKey?: string;
|
|
38
38
|
};
|
|
39
|
+
/**
|
|
40
|
+
* The one message a shopper sees for every coupon the store will not honour: unknown code,
|
|
41
|
+
* inactive, not started, expired, exhausted, or not matching the cart. One key for all of them
|
|
42
|
+
* so the public validation action cannot be used to confirm which codes exist.
|
|
43
|
+
*
|
|
44
|
+
* Seed EN + FR for this key in a forward migration (`02015` is the model); until then the
|
|
45
|
+
* client falls back to the English `error` text.
|
|
46
|
+
*/
|
|
47
|
+
export declare const COUPON_INVALID_ERROR_KEY = "ecommerce.coupon_invalid";
|
|
48
|
+
export declare const COUPON_INVALID_ERROR_MESSAGE = "This coupon code is invalid or can't be applied to your cart.";
|
|
49
|
+
/** Returned by the public validation action once a client has used up its attempts. */
|
|
50
|
+
export declare const COUPON_THROTTLED_ERROR_KEY = "ecommerce.coupon_validation_failed";
|
|
51
|
+
export declare const COUPON_THROTTLED_ERROR_MESSAGE = "Too many coupon attempts. Wait a moment and try again.";
|
|
39
52
|
export declare function normalizeCouponCode(code?: string | null): string;
|
|
40
53
|
export declare function getCartLineCouponKey(item: Pick<CartItem, 'product_id' | 'variant_id'>): string;
|
|
41
54
|
export declare function emptyProviderDiscounts(): Record<EcommercePaymentProvider, number>;
|
package/lib/coupons.es.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
//#region src/lib/coupons.ts
|
|
2
|
-
|
|
2
|
+
var e = "ecommerce.coupon_invalid", t = "This coupon code is invalid or can't be applied to your cart.", n = "ecommerce.coupon_validation_failed", r = "Too many coupon attempts. Wait a moment and try again.";
|
|
3
|
+
function i(e) {
|
|
3
4
|
return (e || "").trim().replace(/\s+/g, "").toUpperCase();
|
|
4
5
|
}
|
|
5
|
-
function
|
|
6
|
+
function a(e) {
|
|
6
7
|
return `${e.product_id}:${e.variant_id || "base"}`;
|
|
7
8
|
}
|
|
8
|
-
function
|
|
9
|
+
function o() {
|
|
9
10
|
return {
|
|
10
11
|
stripe: 0,
|
|
11
12
|
freemius: 0
|
|
12
13
|
};
|
|
13
14
|
}
|
|
14
15
|
//#endregion
|
|
15
|
-
export { n as emptyProviderDiscounts,
|
|
16
|
+
export { e as COUPON_INVALID_ERROR_KEY, t as COUPON_INVALID_ERROR_MESSAGE, n as COUPON_THROTTLED_ERROR_KEY, r as COUPON_THROTTLED_ERROR_MESSAGE, o as emptyProviderDiscounts, a as getCartLineCouponKey, i as normalizeCouponCode };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=require("../_virtual/_rolldown/runtime.cjs");require("server-only");let t=require("node:crypto");t=e.__toESM(t,1);function n(){let e=process.env.SUPABASE_SERVICE_ROLE_KEY||process.env.SUPABASE_SECRET_KEY||process.env.NB_2FA_SECRET;if(!e)throw Error(`Missing SUPABASE_SERVICE_ROLE_KEY / SUPABASE_SECRET_KEY for order access tokens.`);return e}function r(e){return t.default.createHmac(`sha256`,n()).update(`order:${e}`).digest(`base64url`)}function i(e,n){if(!n||typeof n!=`string`)return!1;let i=r(e),a=Buffer.from(i),o=Buffer.from(n);return a.length===o.length&&t.default.timingSafeEqual(a,o)}exports.mintOrderAccessToken=r,exports.verifyOrderAccessToken=i;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import "server-only";
|
|
2
|
+
import e from "node:crypto";
|
|
3
|
+
//#region src/lib/order-access-token.ts
|
|
4
|
+
function t() {
|
|
5
|
+
let e = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.SUPABASE_SECRET_KEY || process.env.NB_2FA_SECRET;
|
|
6
|
+
if (!e) throw Error("Missing SUPABASE_SERVICE_ROLE_KEY / SUPABASE_SECRET_KEY for order access tokens.");
|
|
7
|
+
return e;
|
|
8
|
+
}
|
|
9
|
+
function n(n) {
|
|
10
|
+
return e.createHmac("sha256", t()).update(`order:${n}`).digest("base64url");
|
|
11
|
+
}
|
|
12
|
+
function r(t, r) {
|
|
13
|
+
if (!r || typeof r != "string") return !1;
|
|
14
|
+
let i = n(t), a = Buffer.from(i), o = Buffer.from(r);
|
|
15
|
+
return a.length === o.length && e.timingSafeEqual(a, o);
|
|
16
|
+
}
|
|
17
|
+
//#endregion
|
|
18
|
+
export { n as mintOrderAccessToken, r as verifyOrderAccessToken };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
"use server";
|
|
1
|
+
"use server";const e=require("../../../server-actions/require-ecom-role.cjs");let t=require("@nextblock-cms/db/server");var n=20,r=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function i(e){let t=typeof e==`string`?e.trim():``;return!t||!r.test(t)?null:t.toLowerCase()}async function a(){return await e.getEcomActorRole()===`ADMIN`?(0,t.getServiceRoleSupabaseClient)():null}async function o(e=1,t,r){let o=i(r);if(r&&!o)return{data:[],total:0,totalPages:0};let s=await a();if(!s)return{data:[],total:0,totalPages:0};let c=((Number.isInteger(e)&&e>0?e:1)-1)*n,l=c+n-1,u=s.from(`orders`).select(`
|
|
2
2
|
*,
|
|
3
3
|
order_items (*)
|
|
4
|
-
`,{count:`exact`}).order(`created_at`,{ascending:!1}).range(
|
|
4
|
+
`,{count:`exact`}).order(`created_at`,{ascending:!1}).range(c,l);t&&t!==`all`&&(u=u.eq(`status`,t)),o&&(u=u.or(`id.eq.${o},user_id.eq.${o}`));let{data:d,error:f,count:p}=await u;if(f)throw console.error(`Error fetching orders:`,f),Error(`Failed to fetch orders`);let m=d||[],h=Array.from(new Set(m.map(e=>e.user_id).filter(Boolean))),g=new Map;if(h.length>0){let{data:e,error:t}=await s.from(`profiles`).select(`*`).in(`id`,h);!t&&e&&e.forEach(e=>g.set(e.id,e))}return{data:m.map(e=>({...e,customer:e.user_id?g.get(e.user_id):null})),total:p||0,totalPages:p?Math.ceil(p/n):0}}async function s(e){let t=await a();if(!t)return null;let{data:n,error:r}=await t.from(`orders`).select(`
|
|
5
5
|
*,
|
|
6
6
|
order_items (*)
|
|
7
|
-
`).eq(`id`,
|
|
7
|
+
`).eq(`id`,e).single();if(r||!n)return console.error(`Error fetching order ${e}:`,r),null;let i=n,o=null;if(i.user_id){let{data:e}=await t.from(`profiles`).select(`*`).eq(`id`,i.user_id).single();o=e}let s=i.order_items.map(e=>e.product_id).filter(Boolean),c=new Map;if(s.length>0){let{data:e}=await t.from(`products`).select(`id, title, slug, product_media(media(file_path))`).in(`id`,s);e&&e.forEach(e=>{let t=null;e.product_media&&e.product_media.length>0&&e.product_media[0].media&&(t=e.product_media[0].media.file_path),c.set(e.id,{title:e.title,slug:e.slug,image_url:t})})}let l=i.order_items.map(e=>({...e,product:e.product_id?c.get(e.product_id):null}));return{...i,order_items:l,customer:o}}exports.getOrderDetails=s,exports.getOrders=o;
|
|
@@ -1,37 +1,59 @@
|
|
|
1
1
|
"use server";
|
|
2
|
-
import {
|
|
2
|
+
import { getEcomActorRole as e } from "../../../server-actions/require-ecom-role.es.js";
|
|
3
|
+
import { getServiceRoleSupabaseClient as t } from "@nextblock-cms/db/server";
|
|
3
4
|
//#region src/lib/pages/cms/orders/actions.ts
|
|
4
|
-
var
|
|
5
|
-
|
|
6
|
-
let
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
5
|
+
var n = 20, r = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
6
|
+
function i(e) {
|
|
7
|
+
let t = typeof e == "string" ? e.trim() : "";
|
|
8
|
+
return !t || !r.test(t) ? null : t.toLowerCase();
|
|
9
|
+
}
|
|
10
|
+
async function a() {
|
|
11
|
+
return await e() === "ADMIN" ? t() : null;
|
|
12
|
+
}
|
|
13
|
+
async function o(e = 1, t, r) {
|
|
14
|
+
let o = i(r);
|
|
15
|
+
if (r && !o) return {
|
|
16
|
+
data: [],
|
|
17
|
+
total: 0,
|
|
18
|
+
totalPages: 0
|
|
19
|
+
};
|
|
20
|
+
let s = await a();
|
|
21
|
+
if (!s) return {
|
|
22
|
+
data: [],
|
|
23
|
+
total: 0,
|
|
24
|
+
totalPages: 0
|
|
25
|
+
};
|
|
26
|
+
let c = ((Number.isInteger(e) && e > 0 ? e : 1) - 1) * n, l = c + n - 1, u = s.from("orders").select("\n *,\n order_items (*)\n ", { count: "exact" }).order("created_at", { ascending: !1 }).range(c, l);
|
|
27
|
+
t && t !== "all" && (u = u.eq("status", t)), o && (u = u.or(`id.eq.${o},user_id.eq.${o}`));
|
|
28
|
+
let { data: d, error: f, count: p } = await u;
|
|
29
|
+
if (f) throw console.error("Error fetching orders:", f), Error("Failed to fetch orders");
|
|
30
|
+
let m = d || [], h = Array.from(new Set(m.map((e) => e.user_id).filter(Boolean))), g = /* @__PURE__ */ new Map();
|
|
31
|
+
if (h.length > 0) {
|
|
32
|
+
let { data: e, error: t } = await s.from("profiles").select("*").in("id", h);
|
|
33
|
+
!t && e && e.forEach((e) => g.set(e.id, e));
|
|
14
34
|
}
|
|
15
35
|
return {
|
|
16
|
-
data:
|
|
36
|
+
data: m.map((e) => ({
|
|
17
37
|
...e,
|
|
18
|
-
customer: e.user_id ?
|
|
38
|
+
customer: e.user_id ? g.get(e.user_id) : null
|
|
19
39
|
})),
|
|
20
|
-
total:
|
|
21
|
-
totalPages:
|
|
40
|
+
total: p || 0,
|
|
41
|
+
totalPages: p ? Math.ceil(p / n) : 0
|
|
22
42
|
};
|
|
23
43
|
}
|
|
24
|
-
async function
|
|
25
|
-
let
|
|
26
|
-
if (
|
|
27
|
-
let
|
|
28
|
-
if (
|
|
29
|
-
|
|
44
|
+
async function s(e) {
|
|
45
|
+
let t = await a();
|
|
46
|
+
if (!t) return null;
|
|
47
|
+
let { data: n, error: r } = await t.from("orders").select("\n *,\n order_items (*)\n ").eq("id", e).single();
|
|
48
|
+
if (r || !n) return console.error(`Error fetching order ${e}:`, r), null;
|
|
49
|
+
let i = n, o = null;
|
|
50
|
+
if (i.user_id) {
|
|
51
|
+
let { data: e } = await t.from("profiles").select("*").eq("id", i.user_id).single();
|
|
30
52
|
o = e;
|
|
31
53
|
}
|
|
32
|
-
let s =
|
|
54
|
+
let s = i.order_items.map((e) => e.product_id).filter(Boolean), c = /* @__PURE__ */ new Map();
|
|
33
55
|
if (s.length > 0) {
|
|
34
|
-
let { data: e } = await
|
|
56
|
+
let { data: e } = await t.from("products").select("id, title, slug, product_media(media(file_path))").in("id", s);
|
|
35
57
|
e && e.forEach((e) => {
|
|
36
58
|
let t = null;
|
|
37
59
|
e.product_media && e.product_media.length > 0 && e.product_media[0].media && (t = e.product_media[0].media.file_path), c.set(e.id, {
|
|
@@ -41,15 +63,15 @@ async function r(t) {
|
|
|
41
63
|
});
|
|
42
64
|
});
|
|
43
65
|
}
|
|
44
|
-
let l =
|
|
66
|
+
let l = i.order_items.map((e) => ({
|
|
45
67
|
...e,
|
|
46
68
|
product: e.product_id ? c.get(e.product_id) : null
|
|
47
69
|
}));
|
|
48
70
|
return {
|
|
49
|
-
...
|
|
71
|
+
...i,
|
|
50
72
|
order_items: l,
|
|
51
73
|
customer: o
|
|
52
74
|
};
|
|
53
75
|
}
|
|
54
76
|
//#endregion
|
|
55
|
-
export {
|
|
77
|
+
export { s as getOrderDetails, o as getOrders };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use server";
|
|
1
|
+
"use server";const e=require("../../../server-actions/require-ecom-role.cjs");let t=require("@nextblock-cms/db/server");async function n(n,r){if(await e.getEcomActorRole()!==`ADMIN`)return[];let i=(0,t.getServiceRoleSupabaseClient)(),a=i.from(`orders`).select(`*, order_items(*)`);if(n&&(a=a.gte(`created_at`,n)),r){let e=new Date(r);e.setHours(23,59,59,999),a=a.lte(`created_at`,e.toISOString())}let{data:o,error:s}=await a.order(`created_at`,{ascending:!0});if(s)throw console.error(`Error fetching report data:`,s),Error(`Failed to fetch report data: ${s.message}`);if(!o||o.length===0)return[];let c=Array.from(new Set(o.map(e=>e.user_id).filter(e=>!!e))),l=new Map;if(c.length>0){let{data:e}=await i.from(`profiles`).select(`id, full_name`).in(`id`,c);e&&e.forEach(e=>l.set(e.id,e))}return o.map(e=>({...e,customer:e.user_id?l.get(e.user_id):null}))}exports.fetchOrderReportData=n;
|
|
@@ -1,2 +1,43 @@
|
|
|
1
1
|
export type ReportType = 'general_ledger' | 'tax_liability' | 'currency_summary';
|
|
2
|
-
export declare function fetchOrderReportData(startDate: string, endDate: string): Promise<
|
|
2
|
+
export declare function fetchOrderReportData(startDate: string, endDate: string): Promise<{
|
|
3
|
+
customer: any;
|
|
4
|
+
coupon_code: string | null;
|
|
5
|
+
coupon_id: string | null;
|
|
6
|
+
created_at: string | null;
|
|
7
|
+
currency: string;
|
|
8
|
+
customer_details: import('@nextblock-cms/db').Json | null;
|
|
9
|
+
discount_details: import('@nextblock-cms/db').Json | null;
|
|
10
|
+
discount_total: number;
|
|
11
|
+
exchange_rate_at_purchase: number;
|
|
12
|
+
freemius_last_event_type: string | null;
|
|
13
|
+
freemius_last_synced_at: string | null;
|
|
14
|
+
freemius_license_id: string | null;
|
|
15
|
+
freemius_plan_id: string | null;
|
|
16
|
+
freemius_product_id: string | null;
|
|
17
|
+
freemius_subscription_id: string | null;
|
|
18
|
+
freemius_trial_ends_at: string | null;
|
|
19
|
+
freemius_trial_id: string | null;
|
|
20
|
+
freemius_user_id: string | null;
|
|
21
|
+
id: string;
|
|
22
|
+
inventory_deducted_at: string | null;
|
|
23
|
+
invoice_number: string | null;
|
|
24
|
+
paid_at: string | null;
|
|
25
|
+
payment_intent_id: string | null;
|
|
26
|
+
provider: string | null;
|
|
27
|
+
shipping_total: number | null;
|
|
28
|
+
status: string;
|
|
29
|
+
stripe_session_id: string | null;
|
|
30
|
+
subtotal: number | null;
|
|
31
|
+
tax_details: import('@nextblock-cms/db').Json | null;
|
|
32
|
+
tax_total: number;
|
|
33
|
+
total: number;
|
|
34
|
+
user_id: string | null;
|
|
35
|
+
order_items: {
|
|
36
|
+
id: string;
|
|
37
|
+
order_id: string;
|
|
38
|
+
price_at_purchase: number;
|
|
39
|
+
product_id: string | null;
|
|
40
|
+
quantity: number;
|
|
41
|
+
variant_id: string | null;
|
|
42
|
+
}[];
|
|
43
|
+
}[]>;
|
|
@@ -1,24 +1,26 @@
|
|
|
1
1
|
"use server";
|
|
2
|
-
import {
|
|
2
|
+
import { getEcomActorRole as e } from "../../../server-actions/require-ecom-role.es.js";
|
|
3
|
+
import { getServiceRoleSupabaseClient as t } from "@nextblock-cms/db/server";
|
|
3
4
|
//#region src/lib/pages/cms/orders/export-actions.ts
|
|
4
|
-
async function
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
e
|
|
5
|
+
async function n(n, r) {
|
|
6
|
+
if (await e() !== "ADMIN") return [];
|
|
7
|
+
let i = t(), a = i.from("orders").select("*, order_items(*)");
|
|
8
|
+
if (n && (a = a.gte("created_at", n)), r) {
|
|
9
|
+
let e = new Date(r);
|
|
10
|
+
e.setHours(23, 59, 59, 999), a = a.lte("created_at", e.toISOString());
|
|
9
11
|
}
|
|
10
|
-
let { data:
|
|
11
|
-
if (
|
|
12
|
-
if (!
|
|
13
|
-
let
|
|
14
|
-
if (
|
|
15
|
-
let { data: e } = await
|
|
16
|
-
e && e.forEach((e) =>
|
|
12
|
+
let { data: o, error: s } = await a.order("created_at", { ascending: !0 });
|
|
13
|
+
if (s) throw console.error("Error fetching report data:", s), Error(`Failed to fetch report data: ${s.message}`);
|
|
14
|
+
if (!o || o.length === 0) return [];
|
|
15
|
+
let c = Array.from(new Set(o.map((e) => e.user_id).filter((e) => !!e))), l = /* @__PURE__ */ new Map();
|
|
16
|
+
if (c.length > 0) {
|
|
17
|
+
let { data: e } = await i.from("profiles").select("id, full_name").in("id", c);
|
|
18
|
+
e && e.forEach((e) => l.set(e.id, e));
|
|
17
19
|
}
|
|
18
|
-
return
|
|
20
|
+
return o.map((e) => ({
|
|
19
21
|
...e,
|
|
20
|
-
customer: e.user_id ?
|
|
22
|
+
customer: e.user_id ? l.get(e.user_id) : null
|
|
21
23
|
}));
|
|
22
24
|
}
|
|
23
25
|
//#endregion
|
|
24
|
-
export {
|
|
26
|
+
export { n as fetchOrderReportData };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use server";const e=require("../../../order-inventory.cjs"),t=require("../../../invoice-server.cjs");let n=require("@nextblock-cms/db/server"),r=require("@supabase/supabase-js");var i=[`pending`,`trial`,`paid`,`shipped`,`cancelled`,`refunded`];async function a(){let e=(0,n.createClient)(),{data:{user:t},error:r}=await e.auth.getUser();if(r||!t)return{error:`Unauthorized`};let{data:i,error:a}=await e.from(`profiles`).select(`role`).eq(`id`,t.id).single();return a||!i||![`ADMIN`,`WRITER`].includes(i.role)?{error:`Forbidden`}:{supabase:e,user:t}}
|
|
1
|
+
"use server";const e=require("../../../order-inventory.cjs"),t=require("../../../invoice-server.cjs");let n=require("@nextblock-cms/db/server"),r=require("@supabase/supabase-js");var i=[`pending`,`trial`,`paid`,`shipped`,`cancelled`,`refunded`];async function a(){let e=(0,n.createClient)(),{data:{user:t},error:r}=await e.auth.getUser();if(r||!t)return{error:`Unauthorized`};let{data:i,error:a}=await e.from(`profiles`).select(`role`).eq(`id`,t.id).single();return a||!i||![`ADMIN`,`WRITER`].includes(i.role)?{error:`Forbidden`}:{supabase:e,user:t,role:i.role}}var o=new Set([`paid`,`cancelled`,`refunded`]);function s(){let e=process.env.SUPABASE_SERVICE_ROLE_KEY||process.env.SUPABASE_SECRET_KEY,t=process.env.NEXT_PUBLIC_SUPABASE_URL||process.env.SUPABASE_URL;if(!e||!t)throw Error(`Server configuration error`);return(0,r.createClient)(t,e,{auth:{autoRefreshToken:!1,persistSession:!1}})}async function c(n,r){let c=await a();if(`error`in c)return{success:!1,error:c.error};if(!i.includes(r))return{success:!1,error:`Invalid order status`};if(o.has(r)&&c.role!==`ADMIN`)return{success:!1,error:`Forbidden`};let l;try{l=s()}catch(e){return{success:!1,error:e instanceof Error?e.message:`Server configuration error`}}let{data:u,error:d}=await l.from(`orders`).update({status:r}).eq(`id`,n).select();return d?(console.error(`Order status update error:`,d),{success:!1,error:d.message}):!u||u.length===0?{success:!1,error:`Order not found or update failed.`}:(r===`paid`&&(await t.assignInvoiceMetadata({orderId:n,client:l}),await e.applyOrderInventoryDeduction(l,n)),{success:!0})}exports.updateOrderStatus=c;
|