@nextblock-cms/ecom 0.22.2 → 0.22.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.
Files changed (87) hide show
  1. package/index.cjs +1 -1
  2. package/index.es.js +31 -31
  3. package/lib/components/Checkout.cjs +1 -1
  4. package/lib/components/Checkout.es.js +1 -0
  5. package/lib/components/SubscriptionSelector.cjs +1 -1
  6. package/lib/components/SubscriptionSelector.es.js +91 -92
  7. package/lib/coupon-server.cjs +1 -1
  8. package/lib/coupon-server.es.js +78 -61
  9. package/lib/coupons.cjs +1 -1
  10. package/lib/coupons.d.ts +9 -0
  11. package/lib/coupons.es.js +11 -2
  12. package/lib/currency-sync.cjs +1 -1
  13. package/lib/currency-sync.es.js +1 -1
  14. package/lib/freemius-coupons.cjs +1 -1
  15. package/lib/freemius-coupons.es.js +88 -92
  16. package/lib/freemius-license-claim.cjs +1 -1
  17. package/lib/freemius-license-claim.d.ts +13 -0
  18. package/lib/freemius-license-claim.es.js +44 -29
  19. package/lib/freemius-license.cjs +1 -1
  20. package/lib/freemius-license.es.js +19 -17
  21. package/lib/freemius-order-sync.cjs +1 -1
  22. package/lib/freemius-order-sync.d.ts +44 -0
  23. package/lib/freemius-order-sync.es.js +164 -77
  24. package/lib/freemius-pricing-server.cjs +1 -0
  25. package/lib/freemius-pricing-server.d.ts +65 -0
  26. package/lib/freemius-pricing-server.es.js +82 -0
  27. package/lib/freemius-pricing.cjs +1 -0
  28. package/lib/freemius-pricing.d.ts +53 -0
  29. package/lib/freemius-pricing.es.js +51 -0
  30. package/lib/invoice-server.cjs +1 -1
  31. package/lib/invoice-server.es.js +1 -0
  32. package/lib/pages/cms/coupons/CouponsPage.cjs +1 -1
  33. package/lib/pages/cms/coupons/CouponsPage.es.js +1 -0
  34. package/lib/pages/cms/coupons/EditCouponPage.cjs +1 -1
  35. package/lib/pages/cms/coupons/EditCouponPage.es.js +1 -0
  36. package/lib/pages/cms/coupons/actions.cjs +1 -1
  37. package/lib/pages/cms/coupons/actions.es.js +1 -0
  38. package/lib/pages/cms/orders/actions.cjs +1 -1
  39. package/lib/pages/cms/orders/actions.es.js +1 -0
  40. package/lib/pages/cms/orders/export-actions.cjs +1 -1
  41. package/lib/pages/cms/orders/export-actions.es.js +1 -0
  42. package/lib/pages/cms/orders/server-actions.cjs +1 -1
  43. package/lib/pages/cms/orders/server-actions.es.js +1 -0
  44. package/lib/pages/cms/products/ProductsPage.cjs +1 -1
  45. package/lib/pages/cms/products/ProductsPage.es.js +1 -0
  46. package/lib/pages/cms/products/[id]/edit/EditProductPage.cjs +1 -1
  47. package/lib/pages/cms/products/[id]/edit/EditProductPage.es.js +1 -0
  48. package/lib/pages/cms/products/actions.cjs +3 -3
  49. package/lib/pages/cms/products/actions.d.ts +11 -5
  50. package/lib/pages/cms/products/actions.es.js +45 -12
  51. package/lib/pages/cms/products/inventory/actions.cjs +1 -1
  52. package/lib/pages/cms/products/inventory/actions.es.js +1 -0
  53. package/lib/pages/cms/products/new/NewProductPage.cjs +1 -1
  54. package/lib/pages/cms/products/new/NewProductPage.es.js +1 -0
  55. package/lib/pages/cms/products/server-actions.cjs +1 -1
  56. package/lib/pages/cms/products/server-actions.es.js +1 -0
  57. package/lib/pages/cms/shipping/ShippingPage.cjs +1 -1
  58. package/lib/pages/cms/shipping/ShippingPage.es.js +1 -0
  59. package/lib/pages/cms/shipping/server-actions.cjs +1 -1
  60. package/lib/pages/cms/shipping/server-actions.es.js +1 -0
  61. package/lib/pages/cms/taxes/TaxesPage.cjs +1 -1
  62. package/lib/pages/cms/taxes/TaxesPage.es.js +1 -0
  63. package/lib/payment-config.cjs +1 -1
  64. package/lib/payment-config.es.js +1 -0
  65. package/lib/providers/freemius.cjs +1 -1
  66. package/lib/providers/freemius.es.js +200 -187
  67. package/lib/providers/stripe.cjs +1 -1
  68. package/lib/providers/stripe.es.js +151 -150
  69. package/lib/server-actions/coupon-actions.cjs +1 -1
  70. package/lib/server-actions/coupon-actions.es.js +1 -0
  71. package/lib/server-actions/customer-actions.d.ts +6 -1
  72. package/lib/server-actions/require-ecom-role.cjs +1 -1
  73. package/lib/server-actions/require-ecom-role.es.js +22 -10
  74. package/lib/server-actions/tax-actions.cjs +1 -1
  75. package/lib/server-actions/tax-actions.es.js +1 -0
  76. package/lib/shared-inventory.cjs +1 -1
  77. package/lib/shared-inventory.es.js +1 -1
  78. package/lib/stripe/checkout.cjs +1 -1
  79. package/lib/stripe/checkout.es.js +1 -0
  80. package/lib/stripe/order-sync.cjs +1 -1
  81. package/lib/stripe/order-sync.es.js +1 -0
  82. package/lib/stripe/webhooks.cjs +1 -1
  83. package/lib/stripe/webhooks.d.ts +10 -0
  84. package/lib/stripe/webhooks.es.js +23 -10
  85. package/package.json +4 -4
  86. package/server.cjs +1 -1
  87. package/server.es.js +31 -31
@@ -1,34 +1,36 @@
1
1
  import { parseFreemiusCheckoutCredentialsMap as e, readFreemiusEnvValue as t, resolveFreemiusCheckoutCredentials as n } from "./providers/freemius.es.js";
2
2
  import { applyOrderInventoryDeduction as r } from "./order-inventory.es.js";
3
3
  import { assignInvoiceMetadata as i } from "./invoice-server.es.js";
4
- import { createClient as a } from "@supabase/supabase-js";
5
- import o from "crypto";
6
- import { Freemius as s } from "@freemius/sdk";
4
+ import { evaluateFreemiusLicenseClaim as a, parseFreemiusTimestamp as o, retrieveFreemiusLicenseRecords as s } from "./freemius-license-claim.es.js";
5
+ import "server-only";
6
+ import { createClient as c } from "@supabase/supabase-js";
7
+ import l from "crypto";
8
+ import { Freemius as u } from "@freemius/sdk";
7
9
  //#region src/lib/freemius-order-sync.ts
8
- function c(e) {
10
+ function d(e) {
9
11
  return e == null || e === "" ? null : String(e);
10
12
  }
11
- function l(e) {
12
- let t = c(e);
13
+ function f(e) {
14
+ let t = d(e);
13
15
  if (!t) return null;
14
16
  let n = t.includes("T") ? t : t.replace(" ", "T") + "Z", r = new Date(n);
15
17
  return Number.isNaN(r.getTime()) ? t : r.toISOString();
16
18
  }
17
- function u(e) {
19
+ function p(e) {
18
20
  if (e == null || e === "") return null;
19
21
  let t = Number(e);
20
22
  return Number.isFinite(t) ? t : null;
21
23
  }
22
- function d(...e) {
24
+ function m(...e) {
23
25
  return e.some((e) => {
24
- let t = u(e);
26
+ let t = p(e);
25
27
  return t !== null && t > 0;
26
28
  });
27
29
  }
28
- function f(e) {
30
+ function h(e) {
29
31
  return e === "pending" || e === "trial";
30
32
  }
31
- function p(e) {
33
+ function g(e) {
32
34
  return [
33
35
  "license.cancelled",
34
36
  "license.deleted",
@@ -39,38 +41,38 @@ function p(e) {
39
41
  "trial.expired"
40
42
  ].includes(e || "");
41
43
  }
42
- function m(e, t) {
44
+ function _(e, t) {
43
45
  return e === "license.extended" && (t?.is_renewal === !0 || t?.is_renewal === 1 || t?.is_renewal === "1" || t?.is_renewal === "true");
44
46
  }
45
- function h(e) {
47
+ function v(e) {
46
48
  let t = e.objects?.payment ?? e.data?.payment ?? e.data;
47
- return e.type === "payment.created" && d(t?.gross, t?.total_gross, t?.amount, t?.initial_amount);
49
+ return e.type === "payment.created" && m(t?.gross, t?.total_gross, t?.amount, t?.initial_amount);
48
50
  }
49
- function g(e) {
51
+ function y(e) {
50
52
  let t = e?.purchase ?? null, n = e?.trial ?? null;
51
53
  return {
52
- freemius_product_id: c(t?.plugin_id) ?? c(t?.product_id) ?? c(t?.productId) ?? c(n?.plugin_id) ?? c(n?.product_id) ?? c(n?.productId),
53
- freemius_plan_id: c(t?.plan_id) ?? c(t?.planId),
54
- freemius_license_id: c(t?.license_id) ?? c(t?.licenseId) ?? c(n?.license_id) ?? c(n?.licenseId),
55
- freemius_subscription_id: c(t?.subscription_id) ?? c(t?.subscriptionId) ?? c(n?.subscription_id) ?? c(n?.subscriptionId),
56
- freemius_trial_id: c(n?.id),
57
- freemius_user_id: c(t?.user_id) ?? c(t?.userId) ?? c(n?.user_id) ?? c(n?.userId) ?? c(e?.user?.id),
58
- freemius_trial_ends_at: l(n?.trial_ends_at) ?? l(n?.trialEndsAt) ?? l(t?.trial_ends) ?? l(t?.trialEnds)
54
+ freemius_product_id: d(t?.plugin_id) ?? d(t?.product_id) ?? d(t?.productId) ?? d(n?.plugin_id) ?? d(n?.product_id) ?? d(n?.productId),
55
+ freemius_plan_id: d(t?.plan_id) ?? d(t?.planId),
56
+ freemius_license_id: d(t?.license_id) ?? d(t?.licenseId) ?? d(n?.license_id) ?? d(n?.licenseId),
57
+ freemius_subscription_id: d(t?.subscription_id) ?? d(t?.subscriptionId) ?? d(n?.subscription_id) ?? d(n?.subscriptionId),
58
+ freemius_trial_id: d(n?.id),
59
+ freemius_user_id: d(t?.user_id) ?? d(t?.userId) ?? d(n?.user_id) ?? d(n?.userId) ?? d(e?.user?.id),
60
+ freemius_trial_ends_at: f(n?.trial_ends_at) ?? f(n?.trialEndsAt) ?? f(t?.trial_ends) ?? f(t?.trialEnds)
59
61
  };
60
62
  }
61
- function _(e, t = "pending") {
63
+ function b(e, t = "pending") {
62
64
  let n = e?.purchase ?? null, r = e?.trial ?? null;
63
- return t === "paid" ? "paid" : r && (r.canceled_at || r.canceledAt) && f(t) ? "cancelled" : r ? "trial" : n ? (n.canceled_at || n.canceledAt) && f(t) ? "cancelled" : d(n.initial_amount, n.initialAmount, n.gross, n.total_gross, n.totalGross) ? "paid" : n.trial_ends || n.trialEnds || n.subscription_id || n.subscriptionId ? "trial" : t : t;
65
+ return t === "paid" ? "paid" : r && (r.canceled_at || r.canceledAt) && h(t) ? "cancelled" : r ? "trial" : n ? (n.canceled_at || n.canceledAt) && h(t) ? "cancelled" : m(n.initial_amount, n.initialAmount, n.gross, n.total_gross, n.totalGross) ? "paid" : n.trial_ends || n.trialEnds || n.subscription_id || n.subscriptionId ? "trial" : t : t;
64
66
  }
65
- function v(e) {
67
+ function x(e) {
66
68
  let { event: t, currentStatus: n, purchaseData: r } = e;
67
- return n === "paid" && p(t.type) ? "paid" : p(t.type) ? f(n) ? "cancelled" : n : h(t) || m(t.type, t.data) || r && d(r.initialAmount, r.initial_amount, r.gross, r.totalGross, r.total_gross) ? "paid" : r?.canceled === !0 && f(n) ? "cancelled" : n === "pending" && (r?.trialEndsAt || r?.trial_ends_at || r?.trialEnds || r?.trial_ends) || (r?.subscriptionId || r?.subscription_id || t.type === "subscription.created" || t.type === "license.created") && n === "pending" ? "trial" : n;
69
+ return n === "paid" && g(t.type) ? "paid" : g(t.type) ? h(n) ? "cancelled" : n : v(t) || _(t.type, t.data) || r && m(r.initialAmount, r.initial_amount, r.gross, r.totalGross, r.total_gross) ? "paid" : r?.canceled === !0 && h(n) ? "cancelled" : n === "pending" && (r?.trialEndsAt || r?.trial_ends_at || r?.trialEnds || r?.trial_ends) || (r?.subscriptionId || r?.subscription_id || t.type === "subscription.created" || t.type === "license.created") && n === "pending" ? "trial" : n;
68
70
  }
69
- function y(e, ...t) {
71
+ function S(e, ...t) {
70
72
  let n = t.filter((e) => !!e);
71
73
  return e === "paid" || n.includes("paid") ? "paid" : n.includes("cancelled") ? "cancelled" : n.includes("trial") ? "trial" : n.includes("shipped") ? "shipped" : n.includes("refunded") ? "refunded" : e;
72
74
  }
73
- function b(e, t) {
75
+ function C(e, t) {
74
76
  let n = {
75
77
  freemius_last_event_type: t,
76
78
  freemius_last_synced_at: (/* @__PURE__ */ new Date()).toISOString()
@@ -78,26 +80,26 @@ function b(e, t) {
78
80
  for (let [t, r] of Object.entries(e)) r != null && r !== "" && (n[t] = r);
79
81
  return n;
80
82
  }
81
- function x() {
83
+ function w() {
82
84
  let e = process.env.NEXT_PUBLIC_SUPABASE_URL || process.env.SUPABASE_URL, t = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.SUPABASE_SECRET_KEY;
83
85
  if (!e || !t) throw Error("Missing Supabase Service Role environment variables");
84
- return a(e, t, { auth: {
86
+ return c(e, t, { auth: {
85
87
  autoRefreshToken: !1,
86
88
  persistSession: !1
87
89
  } });
88
90
  }
89
- async function S(e, t) {
90
- let { data: n, error: r } = await e.from("orders").select("id, status, provider, freemius_product_id, freemius_plan_id, freemius_license_id, freemius_subscription_id, freemius_trial_id, freemius_user_id, freemius_trial_ends_at").eq("id", t).single();
91
+ async function T(e, t) {
92
+ let { data: n, error: r } = await e.from("orders").select("id, status, provider, created_at, freemius_product_id, freemius_plan_id, freemius_license_id, freemius_subscription_id, freemius_trial_id, freemius_user_id, freemius_trial_ends_at").eq("id", t).single();
91
93
  if (r || !n) throw Error(r?.message || "Order not found");
92
94
  return n;
93
95
  }
94
- async function C(e, t) {
95
- let { data: n, error: r } = await e.from("orders").select("id, status, provider, freemius_product_id, freemius_plan_id, freemius_license_id, freemius_subscription_id, freemius_trial_id, freemius_user_id, freemius_trial_ends_at").eq("freemius_license_id", t).order("created_at", { ascending: !1 }).limit(1).maybeSingle();
96
+ async function E(e, t) {
97
+ let { data: n, error: r } = await e.from("orders").select("id, status, provider, created_at, freemius_product_id, freemius_plan_id, freemius_license_id, freemius_subscription_id, freemius_trial_id, freemius_user_id, freemius_trial_ends_at").eq("freemius_license_id", t).order("created_at", { ascending: !1 }).limit(1).maybeSingle();
96
98
  if (r) throw Error(r.message);
97
99
  return n ?? null;
98
100
  }
99
- async function w(e) {
100
- let t = b(e.metadata, e.eventType), n = e.order.status, a = n === "paid" && e.nextStatus !== "paid" ? "paid" : e.nextStatus;
101
+ async function D(e) {
102
+ let t = C(e.metadata, e.eventType), n = e.order.status, a = n === "paid" && e.nextStatus !== "paid" ? "paid" : e.nextStatus;
101
103
  a !== n && (t.status = a);
102
104
  let { error: o } = await e.client.from("orders").update(t).eq("id", e.order.id);
103
105
  if (o) throw Error(o.message);
@@ -106,77 +108,162 @@ async function w(e) {
106
108
  client: e.client
107
109
  }), await r(e.client, e.order.id)), a;
108
110
  }
109
- async function T(e) {
110
- let t = e.client ?? x(), n = await S(t, e.orderId);
111
+ var O = 3e5, k = "This checkout could not be verified with Freemius. Your license key is in your purchase email.";
112
+ function A(e) {
113
+ let t = d(e.licenseId), n = d(e.order.freemius_product_id);
114
+ if (!t) return {
115
+ ok: !1,
116
+ reason: "no_license_in_callback"
117
+ };
118
+ if (!n) return {
119
+ ok: !1,
120
+ reason: "missing_order_product"
121
+ };
122
+ let r = d(e.order.freemius_license_id);
123
+ if (r && r !== t) return {
124
+ ok: !1,
125
+ reason: "order_has_other_license"
126
+ };
127
+ if (e.linkedToOtherOrder) return {
128
+ ok: !1,
129
+ reason: "license_linked_to_other_order"
130
+ };
131
+ let i = a({
132
+ license: e.license,
133
+ now: e.now,
134
+ request: {
135
+ email: e.callbackEmail ?? "",
136
+ kind: "purchase",
137
+ licenseId: t,
138
+ productId: n,
139
+ userId: d(e.callbackUserId) ?? ""
140
+ },
141
+ user: e.user
142
+ });
143
+ if (!i.ok) return i;
144
+ let s = e.order.created_at ? Date.parse(e.order.created_at) : NaN, c = o(e.license?.created);
145
+ return !Number.isFinite(s) || c === null || c < s - O ? {
146
+ ok: !1,
147
+ reason: "license_predates_order"
148
+ } : { ok: !0 };
149
+ }
150
+ async function j(e, t, n) {
151
+ let { data: r, error: i } = await e.from("orders").select("id").eq("freemius_license_id", t).neq("id", n).limit(1);
152
+ if (i) throw Error(i.message);
153
+ return Array.isArray(r) && r.length > 0;
154
+ }
155
+ async function M(e) {
156
+ let t = e.client ?? w(), n = await T(t, e.orderId);
111
157
  if (n.provider !== "freemius") return {
112
158
  success: !1,
113
159
  error: "Only Freemius orders can be synced from Freemius checkout"
114
160
  };
115
- let r = g(e.checkoutResponse), i = null;
116
- if (r.freemius_license_id) try {
117
- i = await O(r.freemius_product_id ?? n.freemius_product_id, r.freemius_license_id);
161
+ let r = y(e.checkoutResponse), i = r.freemius_license_id ?? null, a = d(n.freemius_product_id), o = !!i && d(n.freemius_license_id) === i, c = null;
162
+ if (!o) {
163
+ let o;
164
+ if (!i || !a) o = A({
165
+ callbackEmail: null,
166
+ callbackUserId: null,
167
+ license: null,
168
+ licenseId: i,
169
+ linkedToOtherOrder: !1,
170
+ order: n,
171
+ user: null
172
+ });
173
+ else {
174
+ let l = await (e.retrieveLicense ?? s)(a, i);
175
+ if (!l.ok) return console.error(`[Freemius Checkout Sync] Could not verify license ${i} for order ${n.id}: ${l.reason}`), {
176
+ success: !1,
177
+ error: k
178
+ };
179
+ o = A({
180
+ callbackEmail: e.checkoutResponse?.user?.email ?? null,
181
+ callbackUserId: r.freemius_user_id ?? null,
182
+ license: l.license,
183
+ licenseId: i,
184
+ linkedToOtherOrder: await j(t, i, n.id),
185
+ now: e.now,
186
+ order: n,
187
+ user: l.user
188
+ }), c = l.license;
189
+ }
190
+ if (!o.ok) return console.warn(`[Freemius Checkout Sync] Refused license ${i ?? "(none)"} for order ${n.id}: ${o.reason}`), {
191
+ success: !1,
192
+ error: k
193
+ };
194
+ }
195
+ let l = {
196
+ ...r,
197
+ freemius_product_id: a,
198
+ freemius_plan_id: d(c?.plan_id) ?? d(n.freemius_plan_id) ?? null,
199
+ freemius_license_id: i,
200
+ freemius_user_id: d(c?.user_id) ?? d(n.freemius_user_id) ?? null
201
+ }, u = null;
202
+ if (i) try {
203
+ u = await F(a, i);
118
204
  } catch (e) {
119
205
  console.error("[Freemius Checkout Sync] Failed to retrieve purchase data:", e);
120
206
  }
121
- let a = {
207
+ let f = {
122
208
  type: "checkout.purchaseCompleted",
123
209
  data: { license_id: r.freemius_license_id },
124
210
  objects: {}
125
- }, o = {
126
- ...r,
127
- ...D(a, i)
128
- }, s = _(e.checkoutResponse, n.status), c = i ? v({
129
- event: a,
211
+ }, p = {
212
+ ...l,
213
+ ...P(f, u),
214
+ freemius_product_id: a
215
+ }, m = b(e.checkoutResponse, n.status), h = u ? x({
216
+ event: f,
130
217
  currentStatus: n.status,
131
- purchaseData: i
132
- }) : null, l = s === "paid" ? n.status : s, u = await w({
218
+ purchaseData: u
219
+ }) : null, g = m === "paid" ? n.status : m, _ = await D({
133
220
  client: t,
134
221
  order: n,
135
- nextStatus: y(n.status, l, c),
136
- metadata: o,
222
+ nextStatus: S(n.status, g, h),
223
+ metadata: p,
137
224
  eventType: "checkout.purchaseCompleted"
138
225
  });
139
226
  return {
140
227
  success: !0,
141
228
  orderId: n.id,
142
- status: u,
143
- metadata: o
229
+ status: _,
230
+ metadata: p
144
231
  };
145
232
  }
146
- function E(e) {
147
- return c(e.data?.license_id) ?? c(e.objects?.license?.id) ?? c(e.objects?.subscription?.license_id) ?? c(e.objects?.payment?.license_id) ?? c(e.data?.payment?.license_id);
233
+ function N(e) {
234
+ return d(e.data?.license_id) ?? d(e.objects?.license?.id) ?? d(e.objects?.subscription?.license_id) ?? d(e.objects?.payment?.license_id) ?? d(e.data?.payment?.license_id);
148
235
  }
149
- function D(e, t) {
236
+ function P(e, t) {
150
237
  let n = e.objects?.license, r = e.objects?.subscription, i = e.objects?.trial, a = e.objects?.payment ?? e.data?.payment;
151
238
  return {
152
- freemius_product_id: c(t?.productId) ?? c(t?.product_id) ?? c(n?.plugin_id) ?? c(r?.plugin_id) ?? c(a?.plugin_id),
153
- freemius_plan_id: c(t?.planId) ?? c(t?.plan_id) ?? c(n?.plan_id),
154
- freemius_license_id: c(t?.licenseId) ?? c(t?.license_id) ?? E(e),
155
- freemius_subscription_id: c(t?.subscriptionId) ?? c(t?.subscription_id) ?? c(r?.id) ?? c(e.data?.subscription_id),
156
- freemius_trial_id: c(i?.id) ?? c(e.data?.trial_id),
157
- freemius_user_id: c(t?.userId) ?? c(t?.user_id) ?? c(e.objects?.user?.id),
158
- freemius_trial_ends_at: l(i?.trial_ends_at) ?? l(i?.trial_ends) ?? l(n?.trial_ends) ?? l(t?.trialEndsAt) ?? l(t?.trial_ends_at)
239
+ freemius_product_id: d(t?.productId) ?? d(t?.product_id) ?? d(n?.plugin_id) ?? d(r?.plugin_id) ?? d(a?.plugin_id),
240
+ freemius_plan_id: d(t?.planId) ?? d(t?.plan_id) ?? d(n?.plan_id),
241
+ freemius_license_id: d(t?.licenseId) ?? d(t?.license_id) ?? N(e),
242
+ freemius_subscription_id: d(t?.subscriptionId) ?? d(t?.subscription_id) ?? d(r?.id) ?? d(e.data?.subscription_id),
243
+ freemius_trial_id: d(i?.id) ?? d(e.data?.trial_id),
244
+ freemius_user_id: d(t?.userId) ?? d(t?.user_id) ?? d(e.objects?.user?.id),
245
+ freemius_trial_ends_at: f(i?.trial_ends_at) ?? f(i?.trial_ends) ?? f(n?.trial_ends) ?? f(t?.trialEndsAt) ?? f(t?.trial_ends_at)
159
246
  };
160
247
  }
161
- async function O(e, t) {
248
+ async function F(e, t) {
162
249
  if (!e) return null;
163
250
  let r = n(e);
164
- return !r.apiKey || !r.secretKey || !r.publicKey ? null : new s({
251
+ return !r.apiKey || !r.secretKey || !r.publicKey ? null : new u({
165
252
  productId: Number(e),
166
253
  apiKey: r.apiKey,
167
254
  secretKey: r.secretKey,
168
255
  publicKey: r.publicKey
169
256
  }).purchase.retrievePurchaseData(t);
170
257
  }
171
- async function k(e) {
172
- let t = e.client ?? x(), n = E(e.event);
258
+ async function I(e) {
259
+ let t = e.client ?? w(), n = N(e.event);
173
260
  if (!n) return {
174
261
  success: !0,
175
262
  ignored: !0,
176
263
  reason: "missing_license_id",
177
264
  type: e.event.type
178
265
  };
179
- let r = await C(t, n);
266
+ let r = await E(t, n);
180
267
  if (!r) return {
181
268
  success: !0,
182
269
  ignored: !0,
@@ -186,14 +273,14 @@ async function k(e) {
186
273
  };
187
274
  let i = null;
188
275
  try {
189
- i = await O(r.freemius_product_id ?? e.event.objects?.license?.plugin_id, n);
276
+ i = await F(r.freemius_product_id ?? e.event.objects?.license?.plugin_id, n);
190
277
  } catch (e) {
191
278
  console.error("[Freemius Sync] Failed to retrieve purchase data:", e);
192
279
  }
193
- let a = D(e.event, i), o = await w({
280
+ let a = P(e.event, i), o = await D({
194
281
  client: t,
195
282
  order: r,
196
- nextStatus: v({
283
+ nextStatus: x({
197
284
  event: e.event,
198
285
  currentStatus: r.status,
199
286
  purchaseData: i
@@ -208,7 +295,7 @@ async function k(e) {
208
295
  type: e.event.type
209
296
  };
210
297
  }
211
- function A() {
298
+ function L() {
212
299
  let n = [
213
300
  t("FREEMIUS_SECRET_KEY"),
214
301
  t("FREEMIUS_ECOMMERCE_SANDBOX_SECRET_KEY"),
@@ -216,15 +303,15 @@ function A() {
216
303
  ];
217
304
  return Array.from(new Set(n.filter((e) => !!e)));
218
305
  }
219
- function j(e, t) {
220
- return t ? A().some((n) => {
221
- let r = o.createHmac("sha256", n).update(e).digest("hex");
306
+ function R(e, t) {
307
+ return t ? L().some((n) => {
308
+ let r = l.createHmac("sha256", n).update(e).digest("hex");
222
309
  try {
223
- return o.timingSafeEqual(Buffer.from(r, "hex"), Buffer.from(t, "hex"));
310
+ return l.timingSafeEqual(Buffer.from(r, "hex"), Buffer.from(t, "hex"));
224
311
  } catch {
225
312
  return !1;
226
313
  }
227
314
  }) : !1;
228
315
  }
229
316
  //#endregion
230
- export { g as extractFreemiusCheckoutMetadata, A as getFreemiusWebhookSecretCandidates, _ as resolveFreemiusStatusFromCheckoutResponse, v as resolveFreemiusStatusFromWebhookEvent, T as syncFreemiusCheckoutOrder, k as syncFreemiusOrderFromWebhookEvent, j as verifyFreemiusWebhookSignature };
317
+ export { k as FREEMIUS_CHECKOUT_SYNC_REFUSED_MESSAGE, A as evaluateFreemiusCheckoutLicense, y as extractFreemiusCheckoutMetadata, L as getFreemiusWebhookSecretCandidates, b as resolveFreemiusStatusFromCheckoutResponse, x as resolveFreemiusStatusFromWebhookEvent, M as syncFreemiusCheckoutOrder, I as syncFreemiusOrderFromWebhookEvent, R as verifyFreemiusWebhookSignature };
@@ -0,0 +1 @@
1
+ const e=require("./currency.cjs"),t=require("./freemius-pricing.cjs");let n=require("@nextblock-cms/utils");require("server-only");var r=`id, translation_group_id, price, prices, sale_price, sale_prices, sale_start_at, sale_end_at, scheduled_price, scheduled_prices, scheduled_price_at`;async function i(e,t){let n=[t.id];if(t.translation_group_id){let{data:r,error:i}=await e.from(`products`).select(`id`).eq(`translation_group_id`,t.translation_group_id);if(i)throw Error(i.message);n=[...new Set([t.id,...(r||[]).map(e=>e.id)])]}let{data:r,error:i}=await e.from(`freemius_plans`).select(`id`).in(`product_id`,n);if(i)throw Error(i.message);if(!r||r.length===0)return null;let{data:a,error:o}=await e.from(`freemius_pricing`).select(`id, license_quota, api_monthly_price, api_annual_price, api_lifetime_price`).in(`plan_id`,r.map(e=>e.id)).eq(`is_active`,!0).order(`license_quota`,{ascending:!0,nullsFirst:!1}).order(`id`,{ascending:!0}).limit(1);if(o)throw Error(o.message);return a?.[0]??null}async function a(r){let{product:a,currencies:o}=r,s=r.now??new Date,c=r.billingCycle,l=c!=null&&c!==``;if(l&&!t.isFreemiusBillingCycle(c))return{ok:!1,reason:`invalid_billing_cycle`};let u=r.tierCache?.get(a.id);u||(u=i(r.client,a),r.tierCache?.set(a.id,u));let d=await u;if(!d){let t=e.resolveEffectivePriceForCurrency({prices:e.normalizePriceMap(a.prices),salePrices:e.normalizeSalePriceMap(a.sale_prices),fallbackPrice:a.price,fallbackSalePrice:a.sale_price,saleStartAt:a.sale_start_at,saleEndAt:a.sale_end_at,scheduledPrice:a.scheduled_price,scheduledPrices:e.normalizePriceMap(a.scheduled_prices),scheduledPriceAt:a.scheduled_price_at,currencyCode:r.currencyCode,currencies:o,now:s});return{ok:!0,source:`product`,billingCycle:l?c:null,price:t.price,unitAmount:t.sale_price??t.price}}let f={monthly:t.toFreemiusCyclePrice(d.api_monthly_price),annual:t.toFreemiusCyclePrice(d.api_annual_price),lifetime:t.toFreemiusCyclePrice(d.api_lifetime_price)},p=t.resolveFreemiusBillingCycle(c,f);if(!p.ok)return p;let m=e.getDefaultCurrency(o),h=e.convertMinorUnitAmount({amount:(0,n.majorUnitAmountToMinor)(f[p.cycle]??0,m.code),fromCurrencyCode:m.code,toCurrencyCode:r.currencyCode,currencies:o,applyRounding:!0}),g=e.isSaleWindowActive({saleStartAt:a.sale_start_at,saleEndAt:a.sale_end_at,now:s})?t.computeFreemiusSaleDiscountPercent(a.price,a.sale_price):null;return{ok:!0,source:`pricing`,billingCycle:p.cycle,price:h,unitAmount:g===null?h:h-Math.round(h*g/100)}}exports.FREEMIUS_PRICED_PRODUCT_COLUMNS=r,exports.resolveFreemiusUnitAmount=a;
@@ -0,0 +1,65 @@
1
+ import { SupabaseClient } from '@supabase/supabase-js';
2
+ import { CurrencyRecord } from './currency';
3
+ import { BillingCycle } from './types';
4
+ type SupabaseLikeClient = SupabaseClient<any>;
5
+ /** The `products` columns the resolver reads; select all of them. */
6
+ export declare const FREEMIUS_PRICED_PRODUCT_COLUMNS = "id, translation_group_id, price, prices, sale_price, sale_prices, sale_start_at, sale_end_at, scheduled_price, scheduled_prices, scheduled_price_at";
7
+ export type FreemiusPricedProduct = {
8
+ id: string;
9
+ translation_group_id?: string | null;
10
+ price: number | null;
11
+ prices?: Record<string, unknown> | null;
12
+ sale_price?: number | null;
13
+ sale_prices?: Record<string, unknown> | null;
14
+ sale_start_at?: string | null;
15
+ sale_end_at?: string | null;
16
+ scheduled_price?: number | null;
17
+ scheduled_prices?: Record<string, unknown> | null;
18
+ scheduled_price_at?: string | null;
19
+ };
20
+ type FreemiusPricingRow = {
21
+ id: string;
22
+ license_quota: number | null;
23
+ api_monthly_price: number | string | null;
24
+ api_annual_price: number | string | null;
25
+ api_lifetime_price: number | string | null;
26
+ };
27
+ /**
28
+ * Tier lookups keyed by product id, shared across one quote. The public coupon action prices a
29
+ * client-supplied line list, so without it repeating one product N times costs 3N queries.
30
+ */
31
+ export type FreemiusPricingTierCache = Map<string, Promise<FreemiusPricingRow | null>>;
32
+ export type FreemiusUnitAmountResolution = {
33
+ ok: true;
34
+ /** `pricing`: the plan's Freemius price for the cycle. `product`: no pricing rows, so `products.price`. */
35
+ source: 'pricing' | 'product';
36
+ /** The cycle to send to Freemius. Null only on the `product` fallback when none was asked for. */
37
+ billingCycle: BillingCycle | null;
38
+ /** Regular unit price, in the selected currency's minor units. */
39
+ price: number;
40
+ /** What the order records per unit: `price` less an active sale. */
41
+ unitAmount: number;
42
+ } | {
43
+ ok: false;
44
+ reason: 'invalid_billing_cycle' | 'billing_cycle_not_offered';
45
+ };
46
+ /**
47
+ * The unit amount a Freemius purchase is recorded and discounted at.
48
+ *
49
+ * Freemius charges its own plan price for the cycle the checkout opens on, so that is what the
50
+ * order records: the `api_*` price (what Freemius charges, never a display override) for the
51
+ * validated cycle, converted from the store's default currency into the selected one the way
52
+ * the product page converts it. An active sale comes off as the percent the Freemius sale coupon
53
+ * carries. `products.price` is used only when the product has no pricing rows at all; it is the
54
+ * sync's cycle-blind annual-first price, so pricing it for a monthly purchase over-records it.
55
+ */
56
+ export declare function resolveFreemiusUnitAmount(input: {
57
+ client: SupabaseLikeClient;
58
+ product: FreemiusPricedProduct;
59
+ billingCycle?: unknown;
60
+ currencyCode: string;
61
+ currencies: CurrencyRecord[];
62
+ tierCache?: FreemiusPricingTierCache;
63
+ now?: Date;
64
+ }): Promise<FreemiusUnitAmountResolution>;
65
+ export {};
@@ -0,0 +1,82 @@
1
+ import { convertMinorUnitAmount as e, getDefaultCurrency as t, isSaleWindowActive as n, normalizePriceMap as r, normalizeSalePriceMap as i, resolveEffectivePriceForCurrency as a } from "./currency.es.js";
2
+ import { computeFreemiusSaleDiscountPercent as o, isFreemiusBillingCycle as s, resolveFreemiusBillingCycle as c, toFreemiusCyclePrice as l } from "./freemius-pricing.es.js";
3
+ import { majorUnitAmountToMinor as u } from "@nextblock-cms/utils";
4
+ import "server-only";
5
+ //#region src/lib/freemius-pricing-server.ts
6
+ var d = "id, translation_group_id, price, prices, sale_price, sale_prices, sale_start_at, sale_end_at, scheduled_price, scheduled_prices, scheduled_price_at";
7
+ async function f(e, t) {
8
+ let n = [t.id];
9
+ if (t.translation_group_id) {
10
+ let { data: r, error: i } = await e.from("products").select("id").eq("translation_group_id", t.translation_group_id);
11
+ if (i) throw Error(i.message);
12
+ n = [.../* @__PURE__ */ new Set([t.id, ...(r || []).map((e) => e.id)])];
13
+ }
14
+ let { data: r, error: i } = await e.from("freemius_plans").select("id").in("product_id", n);
15
+ if (i) throw Error(i.message);
16
+ if (!r || r.length === 0) return null;
17
+ let { data: a, error: o } = await e.from("freemius_pricing").select("id, license_quota, api_monthly_price, api_annual_price, api_lifetime_price").in("plan_id", r.map((e) => e.id)).eq("is_active", !0).order("license_quota", {
18
+ ascending: !0,
19
+ nullsFirst: !1
20
+ }).order("id", { ascending: !0 }).limit(1);
21
+ if (o) throw Error(o.message);
22
+ return a?.[0] ?? null;
23
+ }
24
+ async function p(d) {
25
+ let { product: p, currencies: m } = d, h = d.now ?? /* @__PURE__ */ new Date(), g = d.billingCycle, _ = g != null && g !== "";
26
+ if (_ && !s(g)) return {
27
+ ok: !1,
28
+ reason: "invalid_billing_cycle"
29
+ };
30
+ let v = d.tierCache?.get(p.id);
31
+ v || (v = f(d.client, p), d.tierCache?.set(p.id, v));
32
+ let y = await v;
33
+ if (!y) {
34
+ let e = a({
35
+ prices: r(p.prices),
36
+ salePrices: i(p.sale_prices),
37
+ fallbackPrice: p.price,
38
+ fallbackSalePrice: p.sale_price,
39
+ saleStartAt: p.sale_start_at,
40
+ saleEndAt: p.sale_end_at,
41
+ scheduledPrice: p.scheduled_price,
42
+ scheduledPrices: r(p.scheduled_prices),
43
+ scheduledPriceAt: p.scheduled_price_at,
44
+ currencyCode: d.currencyCode,
45
+ currencies: m,
46
+ now: h
47
+ });
48
+ return {
49
+ ok: !0,
50
+ source: "product",
51
+ billingCycle: _ ? g : null,
52
+ price: e.price,
53
+ unitAmount: e.sale_price ?? e.price
54
+ };
55
+ }
56
+ let b = {
57
+ monthly: l(y.api_monthly_price),
58
+ annual: l(y.api_annual_price),
59
+ lifetime: l(y.api_lifetime_price)
60
+ }, x = c(g, b);
61
+ if (!x.ok) return x;
62
+ let S = t(m), C = e({
63
+ amount: u(b[x.cycle] ?? 0, S.code),
64
+ fromCurrencyCode: S.code,
65
+ toCurrencyCode: d.currencyCode,
66
+ currencies: m,
67
+ applyRounding: !0
68
+ }), w = n({
69
+ saleStartAt: p.sale_start_at,
70
+ saleEndAt: p.sale_end_at,
71
+ now: h
72
+ }) ? o(p.price, p.sale_price) : null;
73
+ return {
74
+ ok: !0,
75
+ source: "pricing",
76
+ billingCycle: x.cycle,
77
+ price: C,
78
+ unitAmount: w === null ? C : C - Math.round(C * w / 100)
79
+ };
80
+ }
81
+ //#endregion
82
+ export { d as FREEMIUS_PRICED_PRODUCT_COLUMNS, p as resolveFreemiusUnitAmount };
@@ -0,0 +1 @@
1
+ var e=[`annual`,`monthly`,`lifetime`];function t(t){return typeof t==`string`&&e.includes(t)}function n(e){if(e==null||e===``)return null;let t=Number(e);return Number.isFinite(t)&&t>=0?t:null}function r(e,t){let n=typeof e.license_quota==`number`?e.license_quota:null,r=typeof t.license_quota==`number`?t.license_quota:null;return n===r?e.id<t.id?-1:+(e.id>t.id):n===null?1:r===null?-1:n-r}function i(e){let t=e.filter(e=>e.is_active!==!1);return t.sort(r),t[0]??null}function a(n,r){if(n==null||n===``){let t=e.find(e=>r[e]!==null);return t?{ok:!0,cycle:t}:{ok:!1,reason:`billing_cycle_not_offered`}}return t(n)?r[n]===null?{ok:!1,reason:`billing_cycle_not_offered`}:{ok:!0,cycle:n}:{ok:!1,reason:`invalid_billing_cycle`}}function o(e,t){if(typeof e!=`number`||e<=0||typeof t!=`number`||t<0||t>=e)return null;let n=Math.round((1-t/e)*100);return n<=0||n>100?null:n}exports.FREEMIUS_BILLING_CYCLES=e,exports.compareFreemiusPricingTiers=r,exports.computeFreemiusSaleDiscountPercent=o,exports.isFreemiusBillingCycle=t,exports.pickFreemiusPricingTier=i,exports.resolveFreemiusBillingCycle=a,exports.toFreemiusCyclePrice=n;
@@ -0,0 +1,53 @@
1
+ import { BillingCycle } from './types';
2
+ /**
3
+ * Pure Freemius pricing rules shared by the product page (client) and the checkout and coupon
4
+ * quote (server, see freemius-pricing-server.ts). Both sides must pick the same tier and the
5
+ * same cycle, or the price the shopper sees, the coupon base and the recorded order drift apart.
6
+ */
7
+ /**
8
+ * The cycles a Freemius checkout accepts, in the order a missing cycle defaults through:
9
+ * Freemius opens on the annual cycle when the plan sells one, and the product page pre-selects
10
+ * the same way.
11
+ */
12
+ export declare const FREEMIUS_BILLING_CYCLES: readonly BillingCycle[];
13
+ export declare function isFreemiusBillingCycle(value: unknown): value is BillingCycle;
14
+ /** Major-unit price per cycle for one pricing tier; null means the plan does not sell that cycle. */
15
+ export type FreemiusCyclePrices = Record<BillingCycle, number | null>;
16
+ export declare function toFreemiusCyclePrice(value: unknown): number | null;
17
+ type FreemiusPricingTierLike = {
18
+ id: string;
19
+ license_quota: number | null;
20
+ is_active?: boolean | null;
21
+ };
22
+ /**
23
+ * `ORDER BY license_quota ASC NULLS LAST, id ASC`, the order the server query uses. Ids are
24
+ * compared as plain strings (not localeCompare) so lowercase uuids sort as Postgres sorts them.
25
+ */
26
+ export declare function compareFreemiusPricingTiers(a: FreemiusPricingTierLike, b: FreemiusPricingTierLike): number;
27
+ /**
28
+ * The tier a Freemius product is sold at: the active tier with the lowest license quota. The
29
+ * checkout does not pick a license count, so Freemius charges its entry tier, and this is the
30
+ * tier the product page shows and the order records.
31
+ */
32
+ export declare function pickFreemiusPricingTier<T extends FreemiusPricingTierLike>(tiers: readonly T[]): T | null;
33
+ export type FreemiusBillingCycleResolution = {
34
+ ok: true;
35
+ cycle: BillingCycle;
36
+ } | {
37
+ ok: false;
38
+ reason: 'invalid_billing_cycle' | 'billing_cycle_not_offered';
39
+ };
40
+ /**
41
+ * Validates the cycle a cart line asks for against the cycles the tier sells. No cycle (a grid
42
+ * "add to cart" sends none) resolves to the first one sold, in FREEMIUS_BILLING_CYCLES order.
43
+ * Anything that is not a cycle name, or a cycle without a price, is refused: the value is
44
+ * appended to the Freemius checkout as sent, so it is never passed through unchecked.
45
+ */
46
+ export declare function resolveFreemiusBillingCycle(requested: unknown, prices: FreemiusCyclePrices): FreemiusBillingCycleResolution;
47
+ /**
48
+ * The whole-percent discount a product sale becomes on Freemius. The sale coupon pushed to
49
+ * Freemius (syncProductSaleCouponToFreemius) carries this percent, and Freemius applies it to
50
+ * whichever cycle is bought, so the checkout records the sale with the same percent.
51
+ */
52
+ export declare function computeFreemiusSaleDiscountPercent(price?: number | null, salePrice?: number | null): number | null;
53
+ export {};
@@ -0,0 +1,51 @@
1
+ //#region src/lib/freemius-pricing.ts
2
+ var e = [
3
+ "annual",
4
+ "monthly",
5
+ "lifetime"
6
+ ];
7
+ function t(t) {
8
+ return typeof t == "string" && e.includes(t);
9
+ }
10
+ function n(e) {
11
+ if (e == null || e === "") return null;
12
+ let t = Number(e);
13
+ return Number.isFinite(t) && t >= 0 ? t : null;
14
+ }
15
+ function r(e, t) {
16
+ let n = typeof e.license_quota == "number" ? e.license_quota : null, r = typeof t.license_quota == "number" ? t.license_quota : null;
17
+ return n === r ? e.id < t.id ? -1 : +(e.id > t.id) : n === null ? 1 : r === null ? -1 : n - r;
18
+ }
19
+ function i(e) {
20
+ let t = e.filter((e) => e.is_active !== !1);
21
+ return t.sort(r), t[0] ?? null;
22
+ }
23
+ function a(n, r) {
24
+ if (n == null || n === "") {
25
+ let t = e.find((e) => r[e] !== null);
26
+ return t ? {
27
+ ok: !0,
28
+ cycle: t
29
+ } : {
30
+ ok: !1,
31
+ reason: "billing_cycle_not_offered"
32
+ };
33
+ }
34
+ return t(n) ? r[n] === null ? {
35
+ ok: !1,
36
+ reason: "billing_cycle_not_offered"
37
+ } : {
38
+ ok: !0,
39
+ cycle: n
40
+ } : {
41
+ ok: !1,
42
+ reason: "invalid_billing_cycle"
43
+ };
44
+ }
45
+ function o(e, t) {
46
+ if (typeof e != "number" || e <= 0 || typeof t != "number" || t < 0 || t >= e) return null;
47
+ let n = Math.round((1 - t / e) * 100);
48
+ return n <= 0 || n > 100 ? null : n;
49
+ }
50
+ //#endregion
51
+ export { e as FREEMIUS_BILLING_CYCLES, r as compareFreemiusPricingTiers, o as computeFreemiusSaleDiscountPercent, t as isFreemiusBillingCycle, i as pickFreemiusPricingTier, a as resolveFreemiusBillingCycle, n as toFreemiusCyclePrice };
@@ -1,4 +1,4 @@
1
- const e=require("./customer.cjs"),t=require("./invoice.cjs"),n=require("./order-tax-details.cjs");let r=require("@nextblock-cms/db/server");function i(e){return e?e.startsWith(`http`)||e.startsWith(`/`)?e:process.env.NEXT_PUBLIC_R2_BASE_URL?`${process.env.NEXT_PUBLIC_R2_BASE_URL}/${e}`:process.env.NEXT_PUBLIC_SUPABASE_URL?`${process.env.NEXT_PUBLIC_SUPABASE_URL}/storage/v1/object/public/media/${e}`:e:null}function a(e){return e??(0,r.getServiceRoleSupabaseClient)()}function o(e){if(!e)return null;let t=e.media?.object_key??e.media?.file_path??null;return{id:e.id,name:e.name??null,url:i(t),width:e.media?.width??null,height:e.media?.height??null}}var s=`active_logo_id`,c=`
1
+ const e=require("./customer.cjs"),t=require("./invoice.cjs"),n=require("./order-tax-details.cjs");require("server-only");let r=require("@nextblock-cms/db/server");function i(e){return e?e.startsWith(`http`)||e.startsWith(`/`)?e:process.env.NEXT_PUBLIC_R2_BASE_URL?`${process.env.NEXT_PUBLIC_R2_BASE_URL}/${e}`:process.env.NEXT_PUBLIC_SUPABASE_URL?`${process.env.NEXT_PUBLIC_SUPABASE_URL}/storage/v1/object/public/media/${e}`:e:null}function a(e){return e??(0,r.getServiceRoleSupabaseClient)()}function o(e){if(!e)return null;let t=e.media?.object_key??e.media?.file_path??null;return{id:e.id,name:e.name??null,url:i(t),width:e.media?.width??null,height:e.media?.height??null}}var s=`active_logo_id`,c=`
2
2
  id,
3
3
  name,
4
4
  media:media_id (