@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.
- package/index.cjs +1 -1
- package/index.es.js +31 -31
- package/lib/components/Checkout.cjs +1 -1
- package/lib/components/Checkout.es.js +1 -0
- package/lib/components/SubscriptionSelector.cjs +1 -1
- package/lib/components/SubscriptionSelector.es.js +91 -92
- package/lib/coupon-server.cjs +1 -1
- package/lib/coupon-server.es.js +78 -61
- package/lib/coupons.cjs +1 -1
- package/lib/coupons.d.ts +9 -0
- package/lib/coupons.es.js +11 -2
- package/lib/currency-sync.cjs +1 -1
- package/lib/currency-sync.es.js +1 -1
- package/lib/freemius-coupons.cjs +1 -1
- package/lib/freemius-coupons.es.js +88 -92
- package/lib/freemius-license-claim.cjs +1 -1
- package/lib/freemius-license-claim.d.ts +13 -0
- package/lib/freemius-license-claim.es.js +44 -29
- package/lib/freemius-license.cjs +1 -1
- package/lib/freemius-license.es.js +19 -17
- package/lib/freemius-order-sync.cjs +1 -1
- package/lib/freemius-order-sync.d.ts +44 -0
- package/lib/freemius-order-sync.es.js +164 -77
- package/lib/freemius-pricing-server.cjs +1 -0
- package/lib/freemius-pricing-server.d.ts +65 -0
- package/lib/freemius-pricing-server.es.js +82 -0
- package/lib/freemius-pricing.cjs +1 -0
- package/lib/freemius-pricing.d.ts +53 -0
- package/lib/freemius-pricing.es.js +51 -0
- package/lib/invoice-server.cjs +1 -1
- package/lib/invoice-server.es.js +1 -0
- package/lib/pages/cms/coupons/CouponsPage.cjs +1 -1
- package/lib/pages/cms/coupons/CouponsPage.es.js +1 -0
- package/lib/pages/cms/coupons/EditCouponPage.cjs +1 -1
- package/lib/pages/cms/coupons/EditCouponPage.es.js +1 -0
- package/lib/pages/cms/coupons/actions.cjs +1 -1
- package/lib/pages/cms/coupons/actions.es.js +1 -0
- package/lib/pages/cms/orders/actions.cjs +1 -1
- package/lib/pages/cms/orders/actions.es.js +1 -0
- package/lib/pages/cms/orders/export-actions.cjs +1 -1
- package/lib/pages/cms/orders/export-actions.es.js +1 -0
- package/lib/pages/cms/orders/server-actions.cjs +1 -1
- package/lib/pages/cms/orders/server-actions.es.js +1 -0
- package/lib/pages/cms/products/ProductsPage.cjs +1 -1
- package/lib/pages/cms/products/ProductsPage.es.js +1 -0
- package/lib/pages/cms/products/[id]/edit/EditProductPage.cjs +1 -1
- package/lib/pages/cms/products/[id]/edit/EditProductPage.es.js +1 -0
- package/lib/pages/cms/products/actions.cjs +3 -3
- package/lib/pages/cms/products/actions.d.ts +11 -5
- package/lib/pages/cms/products/actions.es.js +45 -12
- package/lib/pages/cms/products/inventory/actions.cjs +1 -1
- package/lib/pages/cms/products/inventory/actions.es.js +1 -0
- package/lib/pages/cms/products/new/NewProductPage.cjs +1 -1
- package/lib/pages/cms/products/new/NewProductPage.es.js +1 -0
- package/lib/pages/cms/products/server-actions.cjs +1 -1
- package/lib/pages/cms/products/server-actions.es.js +1 -0
- package/lib/pages/cms/shipping/ShippingPage.cjs +1 -1
- package/lib/pages/cms/shipping/ShippingPage.es.js +1 -0
- package/lib/pages/cms/shipping/server-actions.cjs +1 -1
- package/lib/pages/cms/shipping/server-actions.es.js +1 -0
- package/lib/pages/cms/taxes/TaxesPage.cjs +1 -1
- package/lib/pages/cms/taxes/TaxesPage.es.js +1 -0
- package/lib/payment-config.cjs +1 -1
- package/lib/payment-config.es.js +1 -0
- package/lib/providers/freemius.cjs +1 -1
- package/lib/providers/freemius.es.js +200 -187
- package/lib/providers/stripe.cjs +1 -1
- package/lib/providers/stripe.es.js +151 -150
- package/lib/server-actions/coupon-actions.cjs +1 -1
- package/lib/server-actions/coupon-actions.es.js +1 -0
- package/lib/server-actions/customer-actions.d.ts +6 -1
- package/lib/server-actions/require-ecom-role.cjs +1 -1
- package/lib/server-actions/require-ecom-role.es.js +22 -10
- package/lib/server-actions/tax-actions.cjs +1 -1
- package/lib/server-actions/tax-actions.es.js +1 -0
- package/lib/shared-inventory.cjs +1 -1
- package/lib/shared-inventory.es.js +1 -1
- package/lib/stripe/checkout.cjs +1 -1
- package/lib/stripe/checkout.es.js +1 -0
- package/lib/stripe/order-sync.cjs +1 -1
- package/lib/stripe/order-sync.es.js +1 -0
- package/lib/stripe/webhooks.cjs +1 -1
- package/lib/stripe/webhooks.d.ts +10 -0
- package/lib/stripe/webhooks.es.js +23 -10
- package/package.json +4 -4
- package/server.cjs +1 -1
- package/server.es.js +31 -31
|
@@ -1,44 +1,45 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { computeFreemiusSaleDiscountPercent as e } from "./freemius-pricing.es.js";
|
|
2
|
+
import { hydrateFreemiusEnvFromDb as t } from "./payment-config.es.js";
|
|
3
|
+
import { readFreemiusEnvValue as n, resolveFreemiusCheckoutCredentials as r } from "./providers/freemius.es.js";
|
|
4
|
+
import { minorUnitAmountToMajor as i } from "@nextblock-cms/utils";
|
|
4
5
|
//#region src/lib/freemius-coupons.ts
|
|
5
|
-
function
|
|
6
|
+
function a(e) {
|
|
6
7
|
if (!e) return null;
|
|
7
8
|
let t = new Date(e);
|
|
8
9
|
return Number.isNaN(t.getTime()) ? e : t.toISOString().replace("T", " ").replace(/\.\d{3}Z$/, "");
|
|
9
10
|
}
|
|
10
|
-
function
|
|
11
|
+
function o(e) {
|
|
11
12
|
return e === "percent" ? "percentage" : "dollar";
|
|
12
13
|
}
|
|
13
|
-
function
|
|
14
|
-
return
|
|
14
|
+
function s(e) {
|
|
15
|
+
return r(e).apiKey || n("FREEMIUS_API_KEY");
|
|
15
16
|
}
|
|
16
|
-
async function
|
|
17
|
-
await
|
|
18
|
-
let n =
|
|
19
|
-
if (!n) throw Error(`Missing Freemius API bearer token for product ${
|
|
20
|
-
let r =
|
|
21
|
-
method:
|
|
17
|
+
async function c(e) {
|
|
18
|
+
await t();
|
|
19
|
+
let n = s(e.productId);
|
|
20
|
+
if (!n) throw Error(`Missing Freemius API bearer token for product ${e.productId}. Set FREEMIUS_API_KEY or FREEMIUS_CHECKOUT_PRODUCTS_JSON[${e.productId}].apiKey.`);
|
|
21
|
+
let r = e.couponId ? `/${e.couponId}` : "", i = await fetch(`https://api.freemius.com/v1/products/${e.productId}/coupons${r}.json`, {
|
|
22
|
+
method: e.method,
|
|
22
23
|
headers: {
|
|
23
24
|
Authorization: `Bearer ${n}`,
|
|
24
25
|
"Content-Type": "application/json",
|
|
25
26
|
Accept: "application/json"
|
|
26
27
|
},
|
|
27
|
-
body:
|
|
28
|
-
}), a = await i.text(),
|
|
29
|
-
if (!i.ok) throw Error(
|
|
30
|
-
return
|
|
28
|
+
body: e.method === "DELETE" ? void 0 : JSON.stringify(e.body || {})
|
|
29
|
+
}), a = await i.text(), o = a ? JSON.parse(a) : null;
|
|
30
|
+
if (!i.ok) throw Error(o?.message || o?.error?.message || `Freemius coupon request failed with ${i.status}`);
|
|
31
|
+
return o;
|
|
31
32
|
}
|
|
32
|
-
function
|
|
33
|
+
function l(e, t) {
|
|
33
34
|
return {
|
|
34
35
|
code: e.code,
|
|
35
|
-
discount: e.discount_type === "fixed" ?
|
|
36
|
-
discount_type:
|
|
36
|
+
discount: e.discount_type === "fixed" ? i(e.discount_amount, "USD") : e.discount_amount,
|
|
37
|
+
discount_type: o(e.discount_type),
|
|
37
38
|
plans: t.planIds.length > 0 ? t.planIds.join(",") : null,
|
|
38
39
|
licenses: null,
|
|
39
40
|
billing_cycles: null,
|
|
40
|
-
start_date:
|
|
41
|
-
end_date:
|
|
41
|
+
start_date: a(e.starts_at),
|
|
42
|
+
end_date: a(e.ends_at),
|
|
42
43
|
redemptions_limit: e.redemption_limit ?? null,
|
|
43
44
|
has_renewals_discount: !1,
|
|
44
45
|
has_addons_discount: !1,
|
|
@@ -47,7 +48,7 @@ function c(e, t) {
|
|
|
47
48
|
user_type: "all"
|
|
48
49
|
};
|
|
49
50
|
}
|
|
50
|
-
async function
|
|
51
|
+
async function u(e, t) {
|
|
51
52
|
if (t.provider_scope === "stripe") return [];
|
|
52
53
|
let { data: n, error: r } = await e.from("coupon_products").select("product_id").eq("coupon_id", t.id);
|
|
53
54
|
if (r) throw Error(r.message);
|
|
@@ -71,7 +72,7 @@ async function l(e, t) {
|
|
|
71
72
|
planIds: [...new Set(e.planIds)]
|
|
72
73
|
}));
|
|
73
74
|
}
|
|
74
|
-
async function
|
|
75
|
+
async function d(e, t) {
|
|
75
76
|
let { data: n } = await e.from("coupon_freemius_mappings").select("sync_status, sync_error").eq("coupon_id", t), r = n || [];
|
|
76
77
|
if (r.length === 0) {
|
|
77
78
|
await e.from("coupons").update({
|
|
@@ -86,21 +87,21 @@ async function u(e, t) {
|
|
|
86
87
|
freemius_sync_error: i?.sync_error ?? null
|
|
87
88
|
}).eq("id", t);
|
|
88
89
|
}
|
|
89
|
-
async function
|
|
90
|
+
async function f(e) {
|
|
90
91
|
let { data: t, error: n } = await e.client.from("coupons").select("id, code, name, internal_note, provider_scope, discount_type, discount_amount, is_active, starts_at, ends_at, redemption_limit").eq("id", e.couponId).single();
|
|
91
92
|
if (n || !t) return {
|
|
92
93
|
success: !1,
|
|
93
94
|
error: n?.message || "Coupon not found"
|
|
94
95
|
};
|
|
95
|
-
let r = t, i = await
|
|
96
|
+
let r = t, i = await u(e.client, r);
|
|
96
97
|
await e.client.from("coupons").update({
|
|
97
98
|
freemius_sync_status: i.length > 0 ? "pending" : "not_required",
|
|
98
99
|
freemius_sync_error: null
|
|
99
100
|
}).eq("id", r.id);
|
|
100
101
|
for (let t of i) {
|
|
101
|
-
let { data: n } = await e.client.from("coupon_freemius_mappings").select("id, freemius_coupon_id").eq("coupon_id", r.id).eq("freemius_product_id", t.freemiusProductId).maybeSingle(), i =
|
|
102
|
+
let { data: n } = await e.client.from("coupon_freemius_mappings").select("id, freemius_coupon_id").eq("coupon_id", r.id).eq("freemius_product_id", t.freemiusProductId).maybeSingle(), i = l(r, t);
|
|
102
103
|
try {
|
|
103
|
-
let a = await
|
|
104
|
+
let a = await c({
|
|
104
105
|
productId: t.freemiusProductId,
|
|
105
106
|
method: n?.freemius_coupon_id ? "PUT" : "POST",
|
|
106
107
|
couponId: n?.freemius_coupon_id ?? null,
|
|
@@ -132,111 +133,106 @@ async function d(e) {
|
|
|
132
133
|
}, { onConflict: "coupon_id,freemius_product_id" });
|
|
133
134
|
}
|
|
134
135
|
}
|
|
135
|
-
return await
|
|
136
|
+
return await d(e.client, r.id), {
|
|
136
137
|
success: !0,
|
|
137
138
|
targetCount: i.length
|
|
138
139
|
};
|
|
139
140
|
}
|
|
140
|
-
function
|
|
141
|
+
function p(e) {
|
|
141
142
|
let t = (e.sku || "").toUpperCase().replace(/[^A-Z0-9]/g, "");
|
|
142
143
|
return t ? `SALE${t}`.slice(0, 32) : `SALE${e.productId.replace(/[^A-Za-z0-9]/g, "").toUpperCase()}`.slice(0, 32);
|
|
143
144
|
}
|
|
144
|
-
function
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
return n <= 0 || n > 100 ? null : n;
|
|
148
|
-
}
|
|
149
|
-
async function m(e) {
|
|
150
|
-
let { data: t, error: n } = await e.client.from("products").select("id, sku, price, sale_price, sale_start_at, sale_end_at, payment_provider, freemius_product_id, freemius_plan_id").eq("id", e.productId).maybeSingle();
|
|
151
|
-
if (n || !t) return {
|
|
145
|
+
async function m(t) {
|
|
146
|
+
let { data: n, error: r } = await t.client.from("products").select("id, sku, price, sale_price, sale_start_at, sale_end_at, payment_provider, freemius_product_id, freemius_plan_id").eq("id", t.productId).maybeSingle();
|
|
147
|
+
if (r || !n) return {
|
|
152
148
|
success: !1,
|
|
153
|
-
error:
|
|
149
|
+
error: r?.message || "Product not found"
|
|
154
150
|
};
|
|
155
|
-
let
|
|
156
|
-
if (
|
|
151
|
+
let i = n, a = String(i.freemius_product_id || "").trim();
|
|
152
|
+
if (i.payment_provider !== "freemius" || !a) return {
|
|
157
153
|
success: !0,
|
|
158
154
|
skipped: !0,
|
|
159
155
|
reason: "not_a_freemius_product"
|
|
160
156
|
};
|
|
161
|
-
let
|
|
162
|
-
if (!
|
|
157
|
+
let o = e(i.price, i.sale_price), s = o !== null, { data: u } = await t.client.from("product_freemius_sale_coupons").select("id, freemius_coupon_id, freemius_coupon_code, discount_percent, is_active").eq("product_id", i.id).maybeSingle();
|
|
158
|
+
if (!s && !u?.freemius_coupon_id) return u?.id && await t.client.from("product_freemius_sale_coupons").update({
|
|
163
159
|
is_active: !1,
|
|
164
160
|
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
165
|
-
}).eq("id",
|
|
161
|
+
}).eq("id", u.id), {
|
|
166
162
|
success: !0,
|
|
167
163
|
active: !1
|
|
168
164
|
};
|
|
169
|
-
let
|
|
170
|
-
sku:
|
|
171
|
-
productId:
|
|
172
|
-
}),
|
|
173
|
-
id:
|
|
174
|
-
code:
|
|
175
|
-
name: `Scheduled sale ${
|
|
165
|
+
let d = u?.freemius_coupon_code || p({
|
|
166
|
+
sku: i.sku,
|
|
167
|
+
productId: i.id
|
|
168
|
+
}), f = i.freemius_plan_id ? [String(i.freemius_plan_id)] : [], m = o ?? u?.discount_percent ?? 1, h = l({
|
|
169
|
+
id: i.id,
|
|
170
|
+
code: d,
|
|
171
|
+
name: `Scheduled sale ${i.sku || i.id}`,
|
|
176
172
|
provider_scope: "freemius",
|
|
177
173
|
discount_type: "percent",
|
|
178
174
|
discount_amount: m,
|
|
179
|
-
is_active:
|
|
180
|
-
starts_at:
|
|
181
|
-
ends_at:
|
|
175
|
+
is_active: s,
|
|
176
|
+
starts_at: i.sale_start_at,
|
|
177
|
+
ends_at: i.sale_end_at,
|
|
182
178
|
redemption_limit: null
|
|
183
179
|
}, {
|
|
184
|
-
freemiusProductId:
|
|
185
|
-
productId:
|
|
186
|
-
planIds:
|
|
180
|
+
freemiusProductId: a,
|
|
181
|
+
productId: i.id,
|
|
182
|
+
planIds: f
|
|
187
183
|
});
|
|
188
184
|
try {
|
|
189
|
-
let
|
|
190
|
-
productId:
|
|
191
|
-
method:
|
|
192
|
-
couponId:
|
|
185
|
+
let e = await c({
|
|
186
|
+
productId: a,
|
|
187
|
+
method: u?.freemius_coupon_id ? "PUT" : "POST",
|
|
188
|
+
couponId: u?.freemius_coupon_id ?? null,
|
|
193
189
|
body: h
|
|
194
|
-
}), n =
|
|
195
|
-
return await
|
|
196
|
-
id:
|
|
197
|
-
product_id:
|
|
198
|
-
freemius_product_id:
|
|
199
|
-
freemius_plan_id:
|
|
200
|
-
freemius_coupon_id: n?.id ? String(n.id) :
|
|
201
|
-
freemius_coupon_code:
|
|
202
|
-
discount_percent:
|
|
203
|
-
starts_at:
|
|
204
|
-
ends_at:
|
|
205
|
-
is_active:
|
|
190
|
+
}), n = e?.coupon ?? e;
|
|
191
|
+
return await t.client.from("product_freemius_sale_coupons").upsert({
|
|
192
|
+
id: u?.id,
|
|
193
|
+
product_id: i.id,
|
|
194
|
+
freemius_product_id: a,
|
|
195
|
+
freemius_plan_id: i.freemius_plan_id ? String(i.freemius_plan_id) : null,
|
|
196
|
+
freemius_coupon_id: n?.id ? String(n.id) : u?.freemius_coupon_id ?? null,
|
|
197
|
+
freemius_coupon_code: d,
|
|
198
|
+
discount_percent: o,
|
|
199
|
+
starts_at: i.sale_start_at,
|
|
200
|
+
ends_at: i.sale_end_at,
|
|
201
|
+
is_active: s,
|
|
206
202
|
sync_status: "synced",
|
|
207
203
|
sync_error: null,
|
|
208
|
-
remote_payload:
|
|
204
|
+
remote_payload: e,
|
|
209
205
|
last_synced_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
210
206
|
}, { onConflict: "product_id" }), {
|
|
211
207
|
success: !0,
|
|
212
|
-
active:
|
|
213
|
-
code:
|
|
208
|
+
active: s,
|
|
209
|
+
code: d
|
|
214
210
|
};
|
|
215
|
-
} catch (
|
|
216
|
-
return await
|
|
217
|
-
id:
|
|
218
|
-
product_id:
|
|
219
|
-
freemius_product_id:
|
|
220
|
-
freemius_plan_id:
|
|
221
|
-
freemius_coupon_id:
|
|
222
|
-
freemius_coupon_code:
|
|
223
|
-
discount_percent:
|
|
224
|
-
starts_at:
|
|
225
|
-
ends_at:
|
|
226
|
-
is_active:
|
|
211
|
+
} catch (e) {
|
|
212
|
+
return await t.client.from("product_freemius_sale_coupons").upsert({
|
|
213
|
+
id: u?.id,
|
|
214
|
+
product_id: i.id,
|
|
215
|
+
freemius_product_id: a,
|
|
216
|
+
freemius_plan_id: i.freemius_plan_id ? String(i.freemius_plan_id) : null,
|
|
217
|
+
freemius_coupon_id: u?.freemius_coupon_id ?? null,
|
|
218
|
+
freemius_coupon_code: d,
|
|
219
|
+
discount_percent: o,
|
|
220
|
+
starts_at: i.sale_start_at,
|
|
221
|
+
ends_at: i.sale_end_at,
|
|
222
|
+
is_active: s,
|
|
227
223
|
sync_status: "failed",
|
|
228
|
-
sync_error:
|
|
224
|
+
sync_error: e.message || "Freemius sale coupon sync failed",
|
|
229
225
|
last_synced_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
230
226
|
}, { onConflict: "product_id" }), {
|
|
231
227
|
success: !1,
|
|
232
|
-
error:
|
|
228
|
+
error: e.message || "Freemius sale coupon sync failed"
|
|
233
229
|
};
|
|
234
230
|
}
|
|
235
231
|
}
|
|
236
232
|
async function h(e) {
|
|
237
233
|
let { data: t } = await e.client.from("coupon_freemius_mappings").select("id, freemius_product_id, freemius_coupon_id").eq("coupon_id", e.couponId).not("freemius_coupon_id", "is", null);
|
|
238
234
|
for (let n of t || []) try {
|
|
239
|
-
await
|
|
235
|
+
await c({
|
|
240
236
|
productId: n.freemius_product_id,
|
|
241
237
|
couponId: n.freemius_coupon_id,
|
|
242
238
|
method: "DELETE"
|
|
@@ -254,4 +250,4 @@ async function h(e) {
|
|
|
254
250
|
}
|
|
255
251
|
}
|
|
256
252
|
//#endregion
|
|
257
|
-
export { h as deleteCouponFromFreemius,
|
|
253
|
+
export { h as deleteCouponFromFreemius, f as syncCouponToFreemius, m as syncProductSaleCouponToFreemius };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const e=require("./payment-config.cjs"),t=require("./providers/freemius.cjs");let n=require("@freemius/sdk");var r=20,i=`This license cannot be claimed automatically. Use the key from your purchase email.`;function a(e){if(!e||typeof e!=`string`)return null;let t=e.trim(),n=/(?:z|[+-]\d{2}:?\d{2})$/i.test(t)?t:`${t.replace(` `,`T`)}Z`,r=Date.parse(n);return Number.isFinite(r)?r:null}function o(e,t){return e==null||t==null?!1:String(e).trim()===String(t).trim()}function s(e){return(e??``).trim().toLowerCase()}function c(e){let{license:t,request:n,user:r}=e,i=e.maxAgeMinutes??20,c=(e.now??new Date).getTime();if(!t||!o(t.id,n.licenseId))return{ok:!1,reason:`license_not_found`};let l=a(t.created);return l===null||c-l>i*6e4||l-c>3e5?{ok:!1,reason:`too_old`}:o(t.plugin_id,n.productId)?t.is_cancelled?{ok:!1,reason:`cancelled`}:!n.userId?.trim()||!o(t.user_id,n.userId)?{ok:!1,reason:`user_mismatch`}:!r||!s(n.email)||s(r.email)!==s(n.email)?{ok:!1,reason:`email_mismatch`}:typeof t.activated==`number`&&t.activated>0?{ok:!1,reason:`already_activated`}:t.secret_key?{ok:!0}:{ok:!1,reason:`no_key`}:{ok:!1,reason:`product_mismatch`}}async function l(r,
|
|
1
|
+
const e=require("./payment-config.cjs"),t=require("./providers/freemius.cjs");let n=require("@freemius/sdk");var r=20,i=`This license cannot be claimed automatically. Use the key from your purchase email.`;function a(e){if(!e||typeof e!=`string`)return null;let t=e.trim(),n=/(?:z|[+-]\d{2}:?\d{2})$/i.test(t)?t:`${t.replace(` `,`T`)}Z`,r=Date.parse(n);return Number.isFinite(r)?r:null}function o(e,t){return e==null||t==null?!1:String(e).trim()===String(t).trim()}function s(e){return(e??``).trim().toLowerCase()}function c(e){let{license:t,request:n,user:r}=e,i=e.maxAgeMinutes??20,c=(e.now??new Date).getTime();if(!t||!o(t.id,n.licenseId))return{ok:!1,reason:`license_not_found`};let l=a(t.created);return l===null||c-l>i*6e4||l-c>3e5?{ok:!1,reason:`too_old`}:o(t.plugin_id,n.productId)?t.is_cancelled?{ok:!1,reason:`cancelled`}:!n.userId?.trim()||!o(t.user_id,n.userId)?{ok:!1,reason:`user_mismatch`}:!r||!s(n.email)||s(r.email)!==s(n.email)?{ok:!1,reason:`email_mismatch`}:typeof t.activated==`number`&&t.activated>0?{ok:!1,reason:`already_activated`}:t.secret_key?{ok:!0}:{ok:!1,reason:`no_key`}:{ok:!1,reason:`product_mismatch`}}async function l(r,i){await e.hydrateFreemiusEnvFromDb();let a=t.resolveFreemiusCheckoutCredentials(r);if(!a.apiKey||!a.secretKey||!a.publicKey)return{ok:!1,reason:`not_configured`};let o=new n.Freemius({apiKey:a.apiKey,productId:Number(r),publicKey:a.publicKey,secretKey:a.secretKey});try{let e=await o.api.license.retrieve(i),t=null;return e?.user_id!==null&&e?.user_id!==void 0&&(t=await o.api.user.retrieve(String(e.user_id))),{license:e,ok:!0,user:t}}catch(e){return console.error(`[Freemius License] Freemius lookup failed:`,e),{ok:!1,reason:`upstream_error`}}}async function u(e,t){let n=await l(e.productId,e.licenseId);if(!n.ok)return n.reason===`not_configured`?{error:`The license service is not configured for this product.`,ok:!1,reason:`not_configured`,status:503}:{error:`Freemius could not be reached to verify the license.`,ok:!1,reason:`upstream_error`,status:502};let{license:r,user:a}=n,o=c({license:r,maxAgeMinutes:t?.maxAgeMinutes,now:t?.now,request:e,user:a});return o.ok?{expiration:r?.expiration??null,licenseKey:String(r?.secret_key),ok:!0,planId:r?.plan_id!==null&&r?.plan_id!==void 0?String(r.plan_id):null}:{error:i,ok:!1,reason:o.reason,status:403}}exports.FREEMIUS_LICENSE_CLAIM_MAX_AGE_MINUTES=r,exports.FREEMIUS_LICENSE_CLAIM_REFUSED_MESSAGE=i,exports.claimFreemiusLicenseKey=u,exports.evaluateFreemiusLicenseClaim=c,exports.parseFreemiusTimestamp=a,exports.retrieveFreemiusLicenseRecords=l;
|
|
@@ -83,6 +83,19 @@ export type FreemiusLicenseClaimResult = {
|
|
|
83
83
|
reason: FreemiusLicenseClaimRejection | 'not_configured' | 'upstream_error';
|
|
84
84
|
status: number;
|
|
85
85
|
};
|
|
86
|
+
export type FreemiusLicenseRecords = {
|
|
87
|
+
license: FreemiusLicenseRecordLike | null;
|
|
88
|
+
ok: true;
|
|
89
|
+
user: FreemiusUserRecordLike | null;
|
|
90
|
+
} | {
|
|
91
|
+
ok: false;
|
|
92
|
+
reason: 'not_configured' | 'upstream_error';
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* The license and its owner as Freemius reports them, read with the vendor credentials for
|
|
96
|
+
* `productId`. Credentials come from the CMS settings (hydrated here) or the environment.
|
|
97
|
+
*/
|
|
98
|
+
export declare function retrieveFreemiusLicenseRecords(productId: string, licenseId: string): Promise<FreemiusLicenseRecords>;
|
|
86
99
|
/**
|
|
87
100
|
* Look the license up with the vendor credentials for `productId` and return its key
|
|
88
101
|
* when `evaluateFreemiusLicenseClaim` accepts the claim.
|
|
@@ -44,49 +44,64 @@ function c(e) {
|
|
|
44
44
|
reason: "product_mismatch"
|
|
45
45
|
};
|
|
46
46
|
}
|
|
47
|
-
async function l(r,
|
|
47
|
+
async function l(r, i) {
|
|
48
48
|
await e();
|
|
49
|
-
let
|
|
50
|
-
if (!
|
|
51
|
-
error: "The license service is not configured for this product.",
|
|
49
|
+
let a = t(r);
|
|
50
|
+
if (!a.apiKey || !a.secretKey || !a.publicKey) return {
|
|
52
51
|
ok: !1,
|
|
53
|
-
reason: "not_configured"
|
|
54
|
-
status: 503
|
|
52
|
+
reason: "not_configured"
|
|
55
53
|
};
|
|
56
|
-
let
|
|
57
|
-
apiKey:
|
|
58
|
-
productId: Number(r
|
|
59
|
-
publicKey:
|
|
60
|
-
secretKey:
|
|
61
|
-
})
|
|
54
|
+
let o = new n({
|
|
55
|
+
apiKey: a.apiKey,
|
|
56
|
+
productId: Number(r),
|
|
57
|
+
publicKey: a.publicKey,
|
|
58
|
+
secretKey: a.secretKey
|
|
59
|
+
});
|
|
62
60
|
try {
|
|
63
|
-
|
|
61
|
+
let e = await o.api.license.retrieve(i), t = null;
|
|
62
|
+
return e?.user_id !== null && e?.user_id !== void 0 && (t = await o.api.user.retrieve(String(e.user_id))), {
|
|
63
|
+
license: e,
|
|
64
|
+
ok: !0,
|
|
65
|
+
user: t
|
|
66
|
+
};
|
|
64
67
|
} catch (e) {
|
|
65
|
-
return console.error("[Freemius License
|
|
66
|
-
error: "Freemius could not be reached to verify the license.",
|
|
68
|
+
return console.error("[Freemius License] Freemius lookup failed:", e), {
|
|
67
69
|
ok: !1,
|
|
68
|
-
reason: "upstream_error"
|
|
69
|
-
status: 502
|
|
70
|
+
reason: "upstream_error"
|
|
70
71
|
};
|
|
71
72
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
73
|
+
}
|
|
74
|
+
async function u(e, t) {
|
|
75
|
+
let n = await l(e.productId, e.licenseId);
|
|
76
|
+
if (!n.ok) return n.reason === "not_configured" ? {
|
|
77
|
+
error: "The license service is not configured for this product.",
|
|
78
|
+
ok: !1,
|
|
79
|
+
reason: "not_configured",
|
|
80
|
+
status: 503
|
|
81
|
+
} : {
|
|
82
|
+
error: "Freemius could not be reached to verify the license.",
|
|
83
|
+
ok: !1,
|
|
84
|
+
reason: "upstream_error",
|
|
85
|
+
status: 502
|
|
86
|
+
};
|
|
87
|
+
let { license: r, user: a } = n, o = c({
|
|
88
|
+
license: r,
|
|
89
|
+
maxAgeMinutes: t?.maxAgeMinutes,
|
|
90
|
+
now: t?.now,
|
|
91
|
+
request: e,
|
|
92
|
+
user: a
|
|
78
93
|
});
|
|
79
|
-
return
|
|
80
|
-
expiration:
|
|
81
|
-
licenseKey: String(
|
|
94
|
+
return o.ok ? {
|
|
95
|
+
expiration: r?.expiration ?? null,
|
|
96
|
+
licenseKey: String(r?.secret_key),
|
|
82
97
|
ok: !0,
|
|
83
|
-
planId:
|
|
98
|
+
planId: r?.plan_id !== null && r?.plan_id !== void 0 ? String(r.plan_id) : null
|
|
84
99
|
} : {
|
|
85
100
|
error: i,
|
|
86
101
|
ok: !1,
|
|
87
|
-
reason:
|
|
102
|
+
reason: o.reason,
|
|
88
103
|
status: 403
|
|
89
104
|
};
|
|
90
105
|
}
|
|
91
106
|
//#endregion
|
|
92
|
-
export { r as FREEMIUS_LICENSE_CLAIM_MAX_AGE_MINUTES, i as FREEMIUS_LICENSE_CLAIM_REFUSED_MESSAGE,
|
|
107
|
+
export { r as FREEMIUS_LICENSE_CLAIM_MAX_AGE_MINUTES, i as FREEMIUS_LICENSE_CLAIM_REFUSED_MESSAGE, u as claimFreemiusLicenseKey, c as evaluateFreemiusLicenseClaim, a as parseFreemiusTimestamp, l as retrieveFreemiusLicenseRecords };
|
package/lib/freemius-license.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const e=require("./payment-config.cjs"),t=require("./providers/freemius.cjs");let
|
|
1
|
+
const e=require("./payment-config.cjs"),t=require("./providers/freemius.cjs"),n=require("./freemius-license-claim.cjs");let r=require("@freemius/sdk");async function i(e,n){if(!e)return null;let i=t.resolveFreemiusCheckoutCredentials(e);return!i.apiKey||!i.secretKey||!i.publicKey?null:new r.Freemius({productId:Number(e),apiKey:i.apiKey,secretKey:i.secretKey,publicKey:i.publicKey}).api.license.retrieve(n)}async function a(t){let{data:r,error:a}=await t.client.from(`orders`).select(`id, provider, created_at, freemius_product_id, freemius_license_id, freemius_trial_ends_at`).eq(`id`,t.orderId).maybeSingle();if(a||!r||r.provider!==`freemius`||!r.freemius_license_id)return null;let o=null;try{await e.hydrateFreemiusEnvFromDb(),o=await i(r.freemius_product_id,String(r.freemius_license_id))}catch(e){console.error(`[Freemius License] Failed to retrieve license key:`,e)}let s=r.created_at?Date.parse(r.created_at):NaN,c=n.parseFreemiusTimestamp(o?.created),l=!!o&&String(o?.plugin_id??``)===String(r.freemius_product_id??``)&&Number.isFinite(s)&&c!==null&&c>=s-3e5;return o&&!l&&console.warn(`[Freemius License] Not revealing license ${r.freemius_license_id} on order ${r.id}: product or creation time does not match the order`),{licenseId:String(r.freemius_license_id),licenseKey:l?o?.secret_key??null:null,planId:o?.plan_id==null?null:String(o.plan_id),expiration:o?.expiration??null,trialEndsAt:r.freemius_trial_ends_at??null,isCancelled:!!o?.is_cancelled}}exports.getFreemiusOrderLicense=a;
|
|
@@ -1,34 +1,36 @@
|
|
|
1
1
|
import { hydrateFreemiusEnvFromDb as e } from "./payment-config.es.js";
|
|
2
2
|
import { resolveFreemiusCheckoutCredentials as t } from "./providers/freemius.es.js";
|
|
3
|
-
import {
|
|
3
|
+
import { parseFreemiusTimestamp as n } from "./freemius-license-claim.es.js";
|
|
4
|
+
import { Freemius as r } from "@freemius/sdk";
|
|
4
5
|
//#region src/lib/freemius-license.ts
|
|
5
|
-
async function
|
|
6
|
+
async function i(e, n) {
|
|
6
7
|
if (!e) return null;
|
|
7
8
|
let i = t(e);
|
|
8
|
-
return !i.apiKey || !i.secretKey || !i.publicKey ? null : new
|
|
9
|
+
return !i.apiKey || !i.secretKey || !i.publicKey ? null : new r({
|
|
9
10
|
productId: Number(e),
|
|
10
11
|
apiKey: i.apiKey,
|
|
11
12
|
secretKey: i.secretKey,
|
|
12
13
|
publicKey: i.publicKey
|
|
13
|
-
}).api.license.retrieve(
|
|
14
|
+
}).api.license.retrieve(n);
|
|
14
15
|
}
|
|
15
|
-
async function
|
|
16
|
-
let { data:
|
|
17
|
-
if (
|
|
18
|
-
let
|
|
16
|
+
async function a(t) {
|
|
17
|
+
let { data: r, error: a } = await t.client.from("orders").select("id, provider, created_at, freemius_product_id, freemius_license_id, freemius_trial_ends_at").eq("id", t.orderId).maybeSingle();
|
|
18
|
+
if (a || !r || r.provider !== "freemius" || !r.freemius_license_id) return null;
|
|
19
|
+
let o = null;
|
|
19
20
|
try {
|
|
20
|
-
await e(),
|
|
21
|
+
await e(), o = await i(r.freemius_product_id, String(r.freemius_license_id));
|
|
21
22
|
} catch (e) {
|
|
22
23
|
console.error("[Freemius License] Failed to retrieve license key:", e);
|
|
23
24
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
let s = r.created_at ? Date.parse(r.created_at) : NaN, c = n(o?.created), l = !!o && String(o?.plugin_id ?? "") === String(r.freemius_product_id ?? "") && Number.isFinite(s) && c !== null && c >= s - 3e5;
|
|
26
|
+
return o && !l && console.warn(`[Freemius License] Not revealing license ${r.freemius_license_id} on order ${r.id}: product or creation time does not match the order`), {
|
|
27
|
+
licenseId: String(r.freemius_license_id),
|
|
28
|
+
licenseKey: l ? o?.secret_key ?? null : null,
|
|
29
|
+
planId: o?.plan_id == null ? null : String(o.plan_id),
|
|
30
|
+
expiration: o?.expiration ?? null,
|
|
31
|
+
trialEndsAt: r.freemius_trial_ends_at ?? null,
|
|
32
|
+
isCancelled: !!o?.is_cancelled
|
|
31
33
|
};
|
|
32
34
|
}
|
|
33
35
|
//#endregion
|
|
34
|
-
export {
|
|
36
|
+
export { a as getFreemiusOrderLicense };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("./providers/freemius.cjs"),n=require("./order-inventory.cjs"),r=require("./invoice-server.cjs");let
|
|
1
|
+
const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("./providers/freemius.cjs"),n=require("./order-inventory.cjs"),r=require("./invoice-server.cjs"),i=require("./freemius-license-claim.cjs");require("server-only");let a=require("@supabase/supabase-js"),o=require("crypto");o=e.__toESM(o,1);let s=require("@freemius/sdk");function c(e){return e==null||e===``?null:String(e)}function l(e){let t=c(e);if(!t)return null;let n=t.includes(`T`)?t:t.replace(` `,`T`)+`Z`,r=new Date(n);return Number.isNaN(r.getTime())?t:r.toISOString()}function u(e){if(e==null||e===``)return null;let t=Number(e);return Number.isFinite(t)?t:null}function d(...e){return e.some(e=>{let t=u(e);return t!==null&&t>0})}function f(e){return e===`pending`||e===`trial`}function p(e){return[`license.cancelled`,`license.deleted`,`license.expired`,`subscription.cancelled`,`subscription.renewal.failed.last`,`trial.cancelled`,`trial.expired`].includes(e||``)}function m(e,t){return e===`license.extended`&&(t?.is_renewal===!0||t?.is_renewal===1||t?.is_renewal===`1`||t?.is_renewal===`true`)}function h(e){let t=e.objects?.payment??e.data?.payment??e.data;return e.type===`payment.created`&&d(t?.gross,t?.total_gross,t?.amount,t?.initial_amount)}function g(e){let t=e?.purchase??null,n=e?.trial??null;return{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),freemius_plan_id:c(t?.plan_id)??c(t?.planId),freemius_license_id:c(t?.license_id)??c(t?.licenseId)??c(n?.license_id)??c(n?.licenseId),freemius_subscription_id:c(t?.subscription_id)??c(t?.subscriptionId)??c(n?.subscription_id)??c(n?.subscriptionId),freemius_trial_id:c(n?.id),freemius_user_id:c(t?.user_id)??c(t?.userId)??c(n?.user_id)??c(n?.userId)??c(e?.user?.id),freemius_trial_ends_at:l(n?.trial_ends_at)??l(n?.trialEndsAt)??l(t?.trial_ends)??l(t?.trialEnds)}}function _(e,t=`pending`){let n=e?.purchase??null,r=e?.trial??null;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}function v(e){let{event:t,currentStatus:n,purchaseData:r}=e;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}function y(e,...t){let n=t.filter(e=>!!e);return e===`paid`||n.includes(`paid`)?`paid`:n.includes(`cancelled`)?`cancelled`:n.includes(`trial`)?`trial`:n.includes(`shipped`)?`shipped`:n.includes(`refunded`)?`refunded`:e}function b(e,t){let n={freemius_last_event_type:t,freemius_last_synced_at:new Date().toISOString()};for(let[t,r]of Object.entries(e))r!=null&&r!==``&&(n[t]=r);return n}function x(){let e=process.env.NEXT_PUBLIC_SUPABASE_URL||process.env.SUPABASE_URL,t=process.env.SUPABASE_SERVICE_ROLE_KEY||process.env.SUPABASE_SECRET_KEY;if(!e||!t)throw Error(`Missing Supabase Service Role environment variables`);return(0,a.createClient)(e,t,{auth:{autoRefreshToken:!1,persistSession:!1}})}async function S(e,t){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();if(r||!n)throw Error(r?.message||`Order not found`);return n}async function C(e,t){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();if(r)throw Error(r.message);return n??null}async function w(e){let t=b(e.metadata,e.eventType),i=e.order.status,a=i===`paid`&&e.nextStatus!==`paid`?`paid`:e.nextStatus;a!==i&&(t.status=a);let{error:o}=await e.client.from(`orders`).update(t).eq(`id`,e.order.id);if(o)throw Error(o.message);return a===`paid`&&i!==`paid`&&(await r.assignInvoiceMetadata({orderId:e.order.id,client:e.client}),await n.applyOrderInventoryDeduction(e.client,e.order.id)),a}var T=3e5,E=`This checkout could not be verified with Freemius. Your license key is in your purchase email.`;function D(e){let t=c(e.licenseId),n=c(e.order.freemius_product_id);if(!t)return{ok:!1,reason:`no_license_in_callback`};if(!n)return{ok:!1,reason:`missing_order_product`};let r=c(e.order.freemius_license_id);if(r&&r!==t)return{ok:!1,reason:`order_has_other_license`};if(e.linkedToOtherOrder)return{ok:!1,reason:`license_linked_to_other_order`};let a=i.evaluateFreemiusLicenseClaim({license:e.license,now:e.now,request:{email:e.callbackEmail??``,kind:`purchase`,licenseId:t,productId:n,userId:c(e.callbackUserId)??``},user:e.user});if(!a.ok)return a;let o=e.order.created_at?Date.parse(e.order.created_at):NaN,s=i.parseFreemiusTimestamp(e.license?.created);return!Number.isFinite(o)||s===null||s<o-T?{ok:!1,reason:`license_predates_order`}:{ok:!0}}async function O(e,t,n){let{data:r,error:i}=await e.from(`orders`).select(`id`).eq(`freemius_license_id`,t).neq(`id`,n).limit(1);if(i)throw Error(i.message);return Array.isArray(r)&&r.length>0}async function k(e){let t=e.client??x(),n=await S(t,e.orderId);if(n.provider!==`freemius`)return{success:!1,error:`Only Freemius orders can be synced from Freemius checkout`};let r=g(e.checkoutResponse),a=r.freemius_license_id??null,o=c(n.freemius_product_id),s=!!a&&c(n.freemius_license_id)===a,l=null;if(!s){let s;if(!a||!o)s=D({callbackEmail:null,callbackUserId:null,license:null,licenseId:a,linkedToOtherOrder:!1,order:n,user:null});else{let c=await(e.retrieveLicense??i.retrieveFreemiusLicenseRecords)(o,a);if(!c.ok)return console.error(`[Freemius Checkout Sync] Could not verify license ${a} for order ${n.id}: ${c.reason}`),{success:!1,error:E};s=D({callbackEmail:e.checkoutResponse?.user?.email??null,callbackUserId:r.freemius_user_id??null,license:c.license,licenseId:a,linkedToOtherOrder:await O(t,a,n.id),now:e.now,order:n,user:c.user}),l=c.license}if(!s.ok)return console.warn(`[Freemius Checkout Sync] Refused license ${a??`(none)`} for order ${n.id}: ${s.reason}`),{success:!1,error:E}}let u={...r,freemius_product_id:o,freemius_plan_id:c(l?.plan_id)??c(n.freemius_plan_id)??null,freemius_license_id:a,freemius_user_id:c(l?.user_id)??c(n.freemius_user_id)??null},d=null;if(a)try{d=await M(o,a)}catch(e){console.error(`[Freemius Checkout Sync] Failed to retrieve purchase data:`,e)}let f={type:`checkout.purchaseCompleted`,data:{license_id:r.freemius_license_id},objects:{}},p={...u,...j(f,d),freemius_product_id:o},m=_(e.checkoutResponse,n.status),h=d?v({event:f,currentStatus:n.status,purchaseData:d}):null,b=m===`paid`?n.status:m,C=await w({client:t,order:n,nextStatus:y(n.status,b,h),metadata:p,eventType:`checkout.purchaseCompleted`});return{success:!0,orderId:n.id,status:C,metadata:p}}function A(e){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)}function j(e,t){let n=e.objects?.license,r=e.objects?.subscription,i=e.objects?.trial,a=e.objects?.payment??e.data?.payment;return{freemius_product_id:c(t?.productId)??c(t?.product_id)??c(n?.plugin_id)??c(r?.plugin_id)??c(a?.plugin_id),freemius_plan_id:c(t?.planId)??c(t?.plan_id)??c(n?.plan_id),freemius_license_id:c(t?.licenseId)??c(t?.license_id)??A(e),freemius_subscription_id:c(t?.subscriptionId)??c(t?.subscription_id)??c(r?.id)??c(e.data?.subscription_id),freemius_trial_id:c(i?.id)??c(e.data?.trial_id),freemius_user_id:c(t?.userId)??c(t?.user_id)??c(e.objects?.user?.id),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)}}async function M(e,n){if(!e)return null;let r=t.resolveFreemiusCheckoutCredentials(e);return!r.apiKey||!r.secretKey||!r.publicKey?null:new s.Freemius({productId:Number(e),apiKey:r.apiKey,secretKey:r.secretKey,publicKey:r.publicKey}).purchase.retrievePurchaseData(n)}async function N(e){let t=e.client??x(),n=A(e.event);if(!n)return{success:!0,ignored:!0,reason:`missing_license_id`,type:e.event.type};let r=await C(t,n);if(!r)return{success:!0,ignored:!0,reason:`unknown_license_id`,licenseId:n,type:e.event.type};let i=null;try{i=await M(r.freemius_product_id??e.event.objects?.license?.plugin_id,n)}catch(e){console.error(`[Freemius Sync] Failed to retrieve purchase data:`,e)}let a=j(e.event,i),o=await w({client:t,order:r,nextStatus:v({event:e.event,currentStatus:r.status,purchaseData:i}),metadata:a,eventType:e.event.type??null});return{success:!0,orderId:r.id,status:o,type:e.event.type}}function P(){let e=[t.readFreemiusEnvValue(`FREEMIUS_SECRET_KEY`),t.readFreemiusEnvValue(`FREEMIUS_ECOMMERCE_SANDBOX_SECRET_KEY`),...Object.values(t.parseFreemiusCheckoutCredentialsMap()??{}).map(e=>e.secretKey??null)];return Array.from(new Set(e.filter(e=>!!e)))}function F(e,t){return t?P().some(n=>{let r=o.default.createHmac(`sha256`,n).update(e).digest(`hex`);try{return o.default.timingSafeEqual(Buffer.from(r,`hex`),Buffer.from(t,`hex`))}catch{return!1}}):!1}exports.FREEMIUS_CHECKOUT_SYNC_REFUSED_MESSAGE=E,exports.evaluateFreemiusCheckoutLicense=D,exports.extractFreemiusCheckoutMetadata=g,exports.getFreemiusWebhookSecretCandidates=P,exports.resolveFreemiusStatusFromCheckoutResponse=_,exports.resolveFreemiusStatusFromWebhookEvent=v,exports.syncFreemiusCheckoutOrder=k,exports.syncFreemiusOrderFromWebhookEvent=N,exports.verifyFreemiusWebhookSignature=F;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { SupabaseClient } from '@supabase/supabase-js';
|
|
2
|
+
import { FreemiusLicenseClaimRejection, FreemiusLicenseRecordLike, FreemiusLicenseRecords, FreemiusUserRecordLike } from './freemius-license-claim';
|
|
2
3
|
export type FreemiusTrackedOrderStatus = 'pending' | 'trial' | 'paid' | 'shipped' | 'cancelled' | 'refunded';
|
|
3
4
|
type SupabaseLikeClient = SupabaseClient<any>;
|
|
4
5
|
type FreemiusCheckoutPurchase = {
|
|
@@ -53,6 +54,19 @@ export type FreemiusCheckoutResponse = {
|
|
|
53
54
|
email?: string | null;
|
|
54
55
|
} | null;
|
|
55
56
|
};
|
|
57
|
+
type FreemiusOrderRow = {
|
|
58
|
+
id: string;
|
|
59
|
+
status: FreemiusTrackedOrderStatus | string;
|
|
60
|
+
provider: string | null;
|
|
61
|
+
created_at?: string | null;
|
|
62
|
+
freemius_product_id?: string | null;
|
|
63
|
+
freemius_plan_id?: string | null;
|
|
64
|
+
freemius_license_id?: string | null;
|
|
65
|
+
freemius_subscription_id?: string | null;
|
|
66
|
+
freemius_trial_id?: string | null;
|
|
67
|
+
freemius_user_id?: string | null;
|
|
68
|
+
freemius_trial_ends_at?: string | null;
|
|
69
|
+
};
|
|
56
70
|
type FreemiusOrderMetadata = {
|
|
57
71
|
freemius_product_id?: string | null;
|
|
58
72
|
freemius_plan_id?: string | null;
|
|
@@ -75,10 +89,40 @@ export declare function resolveFreemiusStatusFromWebhookEvent(input: {
|
|
|
75
89
|
currentStatus: string;
|
|
76
90
|
purchaseData?: Record<string, any> | null;
|
|
77
91
|
}): FreemiusTrackedOrderStatus;
|
|
92
|
+
/** What a refused checkout sync tells the browser, whatever the reason (logged server-side). */
|
|
93
|
+
export declare const FREEMIUS_CHECKOUT_SYNC_REFUSED_MESSAGE = "This checkout could not be verified with Freemius. Your license key is in your purchase email.";
|
|
94
|
+
export type FreemiusCheckoutLicenseRejection = FreemiusLicenseClaimRejection | 'license_linked_to_other_order' | 'license_predates_order' | 'missing_order_product' | 'no_license_in_callback' | 'order_has_other_license';
|
|
95
|
+
/**
|
|
96
|
+
* Pure decision for the browser checkout sync: may this order be linked to the license the
|
|
97
|
+
* checkout callback named? The callback is attacker-controlled (anyone holding an order can
|
|
98
|
+
* POST it with any license id), and the success page and the order history reveal the linked
|
|
99
|
+
* license's key, so a link is accepted only when Freemius itself says the license is the
|
|
100
|
+
* buyer's fresh, never-activated license for THIS order's product (the vendor claim rules),
|
|
101
|
+
* it was created after the order, and no other order holds it. Product and plan ids always
|
|
102
|
+
* come from the order or from Freemius, never from the callback.
|
|
103
|
+
*/
|
|
104
|
+
export declare function evaluateFreemiusCheckoutLicense(input: {
|
|
105
|
+
callbackEmail: string | null | undefined;
|
|
106
|
+
callbackUserId: string | null | undefined;
|
|
107
|
+
license: FreemiusLicenseRecordLike | null | undefined;
|
|
108
|
+
licenseId: string | null | undefined;
|
|
109
|
+
linkedToOtherOrder: boolean;
|
|
110
|
+
now?: Date;
|
|
111
|
+
order: Pick<FreemiusOrderRow, 'created_at' | 'freemius_license_id' | 'freemius_product_id'>;
|
|
112
|
+
user: FreemiusUserRecordLike | null | undefined;
|
|
113
|
+
}): {
|
|
114
|
+
ok: true;
|
|
115
|
+
} | {
|
|
116
|
+
ok: false;
|
|
117
|
+
reason: FreemiusCheckoutLicenseRejection;
|
|
118
|
+
};
|
|
78
119
|
export declare function syncFreemiusCheckoutOrder(input: {
|
|
79
120
|
orderId: string;
|
|
80
121
|
checkoutResponse?: FreemiusCheckoutResponse | null;
|
|
81
122
|
client?: SupabaseLikeClient;
|
|
123
|
+
/** Freemius lookup; injectable for tests. */
|
|
124
|
+
retrieveLicense?: (productId: string, licenseId: string) => Promise<FreemiusLicenseRecords>;
|
|
125
|
+
now?: Date;
|
|
82
126
|
}): Promise<{
|
|
83
127
|
success: boolean;
|
|
84
128
|
error: string;
|