@nextblock-cms/ecom 0.10.2 → 0.10.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/CustomerProfileForm.cjs.js +1 -1
- package/lib/components/CustomerProfileForm.es.js +162 -159
- package/lib/freemius-coupons.cjs.js +1 -1
- package/lib/freemius-coupons.es.js +51 -49
- package/lib/pages/cms/payments/PaymentsClient.cjs.js +1 -1
- package/lib/pages/cms/payments/PaymentsClient.d.ts +4 -1
- package/lib/pages/cms/payments/PaymentsClient.es.js +255 -127
- package/lib/pages/cms/payments/PaymentsPage.cjs.js +1 -1
- package/lib/pages/cms/payments/PaymentsPage.es.js +20 -16
- package/lib/pages/cms/payments/actions.cjs.js +1 -1
- package/lib/pages/cms/payments/actions.d.ts +1 -0
- package/lib/pages/cms/payments/actions.es.js +41 -13
- package/lib/pages/cms/payments/queries.cjs.js +1 -1
- package/lib/pages/cms/payments/queries.es.js +13 -24
- package/lib/payment-config.cjs.js +1 -0
- package/lib/payment-config.d.ts +57 -0
- package/lib/payment-config.es.js +137 -0
- package/lib/providers/freemius.cjs.js +2 -2
- package/lib/providers/freemius.es.js +108 -107
- package/lib/providers/stripe.cjs.js +1 -1
- package/lib/providers/stripe.es.js +167 -167
- package/lib/stripe/checkout.cjs.js +1 -1
- package/lib/stripe/checkout.es.js +14 -14
- package/lib/stripe/client.cjs.js +1 -1
- package/lib/stripe/client.d.ts +1 -1
- package/lib/stripe/client.es.js +8 -5
- package/lib/stripe/order-sync.cjs.js +1 -1
- package/lib/stripe/order-sync.es.js +50 -50
- package/lib/stripe/webhooks.cjs.js +1 -1
- package/lib/stripe/webhooks.es.js +12 -10
- package/package.json +4 -4
- package/server.cjs.js +1 -1
- package/server.d.ts +1 -0
- package/server.es.js +188 -178
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const s=require("@nextblock-cms/db/server"),E="payment_secret",f="payment_public",K=6e4;let u=null;function a(e){return e&&typeof e=="object"&&!Array.isArray(e)?e:{}}async function S(e=!1){const r=Date.now();if(!e&&u&&r-u.at<K)return{secret:u.secret,public:u.public};let i={},t={};try{const n=s.getServiceRoleSupabaseClient(),[{data:o},{data:c}]=await Promise.all([n.from("site_settings").select("value").eq("key",E).maybeSingle(),n.from("site_settings").select("value").eq("key",f).maybeSingle()]);i=a(o?.value),t=a(c?.value)}catch{}return u={at:r,secret:i,public:t},{secret:i,public:t}}function d(){u=null}function l(e,r){return s.tryDecryptWithEnvKey(e[r])}async function h(){const{secret:e}=await S();return s.resolveConfigValue(l(e,"stripeSecretKey"),"STRIPE_SECRET_KEY")}async function _(){const{secret:e}=await S();return s.resolveConfigValue(l(e,"stripeWebhookSecret"),"STRIPE_WEBHOOK_SECRET")}async function v(){const{public:e}=await S(),r=a(e.stripe).publishableKey;return s.resolveConfigValue(typeof r=="string"?r:null,"NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY")}async function C(){try{const{secret:e,public:r}=await S(),i=a(r.freemius),t=(n,o)=>{o&&o.trim()&&(process.env[n]=o.trim())};t("FREEMIUS_SECRET_KEY",l(e,"freemiusSecretKey")),t("FREEMIUS_API_KEY",l(e,"freemiusApiKey")),t("FREEMIUS_ECOMMERCE_SANDBOX_SECRET_KEY",l(e,"freemiusSandboxSecretKey")),t("FREEMIUS_PUBLIC_KEY",typeof i.publicKey=="string"?i.publicKey:null),t("FREEMIUS_PRODUCT_ID",typeof i.productId=="string"?i.productId:null),t("FREEMIUS_DEVELOPER_ID",typeof i.developerId=="string"?i.developerId:null)}catch(e){console.warn("[payment-config] Could not hydrate Freemius credentials from the database:",e)}}async function P(){const{secret:e,public:r}=await S(),i=a(r.freemius),t=a(r.stripe),n=[];s.resolveConfigValue(l(e,"stripeSecretKey"),"STRIPE_SECRET_KEY")||n.push("Secret key"),s.resolveConfigValue(typeof t.publishableKey=="string"?t.publishableKey:null,"NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY")||n.push("Publishable key"),s.resolveConfigValue(l(e,"stripeWebhookSecret"),"STRIPE_WEBHOOK_SECRET")||n.push("Webhook secret");const o=[];return s.resolveConfigValue(typeof i.publicKey=="string"?i.publicKey:null,"FREEMIUS_PUBLIC_KEY")||o.push("Public key"),s.resolveConfigValue(l(e,"freemiusSecretKey"),"FREEMIUS_SECRET_KEY")||o.push("Secret key"),{stripe:{hasKeys:n.length===0,missing:n},freemius:{hasKeys:o.length===0,missing:o}}}async function I(){const e=s.createClient(),[{data:r},{data:i}]=await Promise.all([e.from("site_settings").select("value").eq("key",E).maybeSingle(),e.from("site_settings").select("value").eq("key",f).maybeSingle()]),t=a(r?.value),n=a(i?.value),o=a(n.stripe),c=a(n.freemius),p=y=>typeof y=="string"?y:"";return{stripe:{publishableKey:p(o.publishableKey),hasSecretKey:s.getSecretEnvelopeStatus(t.stripeSecretKey).hasStoredValue,hasWebhookSecret:s.getSecretEnvelopeStatus(t.stripeWebhookSecret).hasStoredValue},freemius:{developerId:p(c.developerId),publicKey:p(c.publicKey),productId:p(c.productId),hasSecretKey:s.getSecretEnvelopeStatus(t.freemiusSecretKey).hasStoredValue,hasApiKey:s.getSecretEnvelopeStatus(t.freemiusApiKey).hasStoredValue},envFallbackActive:!!(!t.stripeSecretKey&&process.env.STRIPE_SECRET_KEY)}}async function R(e){const r=s.createClient(),i={stripe:{publishableKey:e.stripe.publishableKey.trim()},freemius:{developerId:e.freemius.developerId.trim(),publicKey:e.freemius.publicKey.trim(),productId:e.freemius.productId.trim(),sandboxEnabled:!1}},{error:t}=await r.from("site_settings").upsert({key:f,value:i});if(t)throw console.error("Error saving payment_public settings:",t.message),new Error("Failed to save payment settings.");const n=[["stripeSecretKey",e.stripe.secretKey],["stripeWebhookSecret",e.stripe.webhookSecret],["freemiusSecretKey",e.freemius.secretKey],["freemiusApiKey",e.freemius.apiKey]];if(n.some(([,c])=>c&&c.trim())){if(s.isSandboxEnvironment())throw new Error("The sandbox cannot store live payment credentials.");const{data:c}=await r.from("site_settings").select("value").eq("key",E).maybeSingle(),y={...a(c?.value)};for(const[g,b]of n)b&&b.trim()&&(y[g]=s.encryptWithEnvKey(b.trim()));const{error:m}=await r.from("site_settings").upsert({key:E,value:y});if(m)throw console.error("Error saving payment_secret settings:",m.message),new Error("Failed to save payment credentials.")}d()}exports.clearPaymentConfigCache=d;exports.getPaymentConfigStatus=P;exports.getPaymentCredentialsView=I;exports.hydrateFreemiusEnvFromDb=C;exports.resolveStripePublishableKey=v;exports.resolveStripeSecretKey=h;exports.resolveStripeWebhookSecret=_;exports.savePaymentCredentials=R;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/** Drop the in-memory cache after a save so the next read reflects new credentials. */
|
|
2
|
+
export declare function clearPaymentConfigCache(): void;
|
|
3
|
+
export declare function resolveStripeSecretKey(): Promise<string | null>;
|
|
4
|
+
export declare function resolveStripeWebhookSecret(): Promise<string | null>;
|
|
5
|
+
export declare function resolveStripePublishableKey(): Promise<string | null>;
|
|
6
|
+
/**
|
|
7
|
+
* Make the synchronous FREEMIUS_* env reads DB-aware by writing any configured DB
|
|
8
|
+
* values into process.env (DB wins). Idempotent and cheap (cached); call at async
|
|
9
|
+
* entry points before the package's sync credential helpers run.
|
|
10
|
+
*/
|
|
11
|
+
export declare function hydrateFreemiusEnvFromDb(): Promise<void>;
|
|
12
|
+
export interface PaymentProviderStatus {
|
|
13
|
+
hasKeys: boolean;
|
|
14
|
+
missing: string[];
|
|
15
|
+
}
|
|
16
|
+
export interface PaymentConfigStatus {
|
|
17
|
+
stripe: PaymentProviderStatus;
|
|
18
|
+
freemius: PaymentProviderStatus;
|
|
19
|
+
}
|
|
20
|
+
export declare function getPaymentConfigStatus(): Promise<PaymentConfigStatus>;
|
|
21
|
+
export interface PaymentCredentialsView {
|
|
22
|
+
stripe: {
|
|
23
|
+
publishableKey: string;
|
|
24
|
+
hasSecretKey: boolean;
|
|
25
|
+
hasWebhookSecret: boolean;
|
|
26
|
+
};
|
|
27
|
+
freemius: {
|
|
28
|
+
developerId: string;
|
|
29
|
+
publicKey: string;
|
|
30
|
+
productId: string;
|
|
31
|
+
hasSecretKey: boolean;
|
|
32
|
+
hasApiKey: boolean;
|
|
33
|
+
};
|
|
34
|
+
envFallbackActive: boolean;
|
|
35
|
+
}
|
|
36
|
+
export declare function getPaymentCredentialsView(): Promise<PaymentCredentialsView>;
|
|
37
|
+
export interface SavePaymentCredentialsInput {
|
|
38
|
+
stripe: {
|
|
39
|
+
publishableKey: string;
|
|
40
|
+
secretKey?: string;
|
|
41
|
+
webhookSecret?: string;
|
|
42
|
+
};
|
|
43
|
+
freemius: {
|
|
44
|
+
developerId: string;
|
|
45
|
+
publicKey: string;
|
|
46
|
+
productId: string;
|
|
47
|
+
secretKey?: string;
|
|
48
|
+
apiKey?: string;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Persist payment credentials. Public fields always overwrite; secret fields are
|
|
53
|
+
* encrypted and only written when a new value is supplied (blank keeps the stored
|
|
54
|
+
* secret). Refuses to store real secrets in the sandbox. Caller enforces ADMIN; RLS
|
|
55
|
+
* double-enforces.
|
|
56
|
+
*/
|
|
57
|
+
export declare function savePaymentCredentials(input: SavePaymentCredentialsInput): Promise<void>;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { resolveConfigValue as o, createClient as d, getSecretEnvelopeStatus as b, isSandboxEnvironment as h, encryptWithEnvKey as g, getServiceRoleSupabaseClient as I, tryDecryptWithEnvKey as P } from "@nextblock-cms/db/server";
|
|
2
|
+
const f = "payment_secret", S = "payment_public", v = 6e4;
|
|
3
|
+
let u = null;
|
|
4
|
+
function n(e) {
|
|
5
|
+
return e && typeof e == "object" && !Array.isArray(e) ? e : {};
|
|
6
|
+
}
|
|
7
|
+
async function E(e = !1) {
|
|
8
|
+
const r = Date.now();
|
|
9
|
+
if (!e && u && r - u.at < v)
|
|
10
|
+
return { secret: u.secret, public: u.public };
|
|
11
|
+
let s = {}, t = {};
|
|
12
|
+
try {
|
|
13
|
+
const i = I(), [{ data: c }, { data: a }] = await Promise.all([
|
|
14
|
+
i.from("site_settings").select("value").eq("key", f).maybeSingle(),
|
|
15
|
+
i.from("site_settings").select("value").eq("key", S).maybeSingle()
|
|
16
|
+
]);
|
|
17
|
+
s = n(c?.value), t = n(a?.value);
|
|
18
|
+
} catch {
|
|
19
|
+
}
|
|
20
|
+
return u = { at: r, secret: s, public: t }, { secret: s, public: t };
|
|
21
|
+
}
|
|
22
|
+
function R() {
|
|
23
|
+
u = null;
|
|
24
|
+
}
|
|
25
|
+
function l(e, r) {
|
|
26
|
+
return P(e[r]);
|
|
27
|
+
}
|
|
28
|
+
async function w() {
|
|
29
|
+
const { secret: e } = await E();
|
|
30
|
+
return o(l(e, "stripeSecretKey"), "STRIPE_SECRET_KEY");
|
|
31
|
+
}
|
|
32
|
+
async function T() {
|
|
33
|
+
const { secret: e } = await E();
|
|
34
|
+
return o(l(e, "stripeWebhookSecret"), "STRIPE_WEBHOOK_SECRET");
|
|
35
|
+
}
|
|
36
|
+
async function k() {
|
|
37
|
+
const { public: e } = await E(), r = n(e.stripe).publishableKey;
|
|
38
|
+
return o(
|
|
39
|
+
typeof r == "string" ? r : null,
|
|
40
|
+
"NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY"
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
async function U() {
|
|
44
|
+
try {
|
|
45
|
+
const { secret: e, public: r } = await E(), s = n(r.freemius), t = (i, c) => {
|
|
46
|
+
c && c.trim() && (process.env[i] = c.trim());
|
|
47
|
+
};
|
|
48
|
+
t("FREEMIUS_SECRET_KEY", l(e, "freemiusSecretKey")), t("FREEMIUS_API_KEY", l(e, "freemiusApiKey")), t("FREEMIUS_ECOMMERCE_SANDBOX_SECRET_KEY", l(e, "freemiusSandboxSecretKey")), t(
|
|
49
|
+
"FREEMIUS_PUBLIC_KEY",
|
|
50
|
+
typeof s.publicKey == "string" ? s.publicKey : null
|
|
51
|
+
), t(
|
|
52
|
+
"FREEMIUS_PRODUCT_ID",
|
|
53
|
+
typeof s.productId == "string" ? s.productId : null
|
|
54
|
+
), t(
|
|
55
|
+
"FREEMIUS_DEVELOPER_ID",
|
|
56
|
+
typeof s.developerId == "string" ? s.developerId : null
|
|
57
|
+
);
|
|
58
|
+
} catch (e) {
|
|
59
|
+
console.warn("[payment-config] Could not hydrate Freemius credentials from the database:", e);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async function F() {
|
|
63
|
+
const { secret: e, public: r } = await E(), s = n(r.freemius), t = n(r.stripe), i = [];
|
|
64
|
+
o(l(e, "stripeSecretKey"), "STRIPE_SECRET_KEY") || i.push("Secret key"), o(
|
|
65
|
+
typeof t.publishableKey == "string" ? t.publishableKey : null,
|
|
66
|
+
"NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY"
|
|
67
|
+
) || i.push("Publishable key"), o(l(e, "stripeWebhookSecret"), "STRIPE_WEBHOOK_SECRET") || i.push("Webhook secret");
|
|
68
|
+
const c = [];
|
|
69
|
+
return o(
|
|
70
|
+
typeof s.publicKey == "string" ? s.publicKey : null,
|
|
71
|
+
"FREEMIUS_PUBLIC_KEY"
|
|
72
|
+
) || c.push("Public key"), o(l(e, "freemiusSecretKey"), "FREEMIUS_SECRET_KEY") || c.push("Secret key"), {
|
|
73
|
+
stripe: { hasKeys: i.length === 0, missing: i },
|
|
74
|
+
freemius: { hasKeys: c.length === 0, missing: c }
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
async function M() {
|
|
78
|
+
const e = d(), [{ data: r }, { data: s }] = await Promise.all([
|
|
79
|
+
e.from("site_settings").select("value").eq("key", f).maybeSingle(),
|
|
80
|
+
e.from("site_settings").select("value").eq("key", S).maybeSingle()
|
|
81
|
+
]), t = n(r?.value), i = n(s?.value), c = n(i.stripe), a = n(i.freemius), p = (y) => typeof y == "string" ? y : "";
|
|
82
|
+
return {
|
|
83
|
+
stripe: {
|
|
84
|
+
publishableKey: p(c.publishableKey),
|
|
85
|
+
hasSecretKey: b(t.stripeSecretKey).hasStoredValue,
|
|
86
|
+
hasWebhookSecret: b(t.stripeWebhookSecret).hasStoredValue
|
|
87
|
+
},
|
|
88
|
+
freemius: {
|
|
89
|
+
developerId: p(a.developerId),
|
|
90
|
+
publicKey: p(a.publicKey),
|
|
91
|
+
productId: p(a.productId),
|
|
92
|
+
hasSecretKey: b(t.freemiusSecretKey).hasStoredValue,
|
|
93
|
+
hasApiKey: b(t.freemiusApiKey).hasStoredValue
|
|
94
|
+
},
|
|
95
|
+
envFallbackActive: !!(!t.stripeSecretKey && process.env.STRIPE_SECRET_KEY)
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
async function Y(e) {
|
|
99
|
+
const r = d(), s = {
|
|
100
|
+
stripe: { publishableKey: e.stripe.publishableKey.trim() },
|
|
101
|
+
freemius: {
|
|
102
|
+
developerId: e.freemius.developerId.trim(),
|
|
103
|
+
publicKey: e.freemius.publicKey.trim(),
|
|
104
|
+
productId: e.freemius.productId.trim(),
|
|
105
|
+
sandboxEnabled: !1
|
|
106
|
+
}
|
|
107
|
+
}, { error: t } = await r.from("site_settings").upsert({ key: S, value: s });
|
|
108
|
+
if (t)
|
|
109
|
+
throw console.error("Error saving payment_public settings:", t.message), new Error("Failed to save payment settings.");
|
|
110
|
+
const i = [
|
|
111
|
+
["stripeSecretKey", e.stripe.secretKey],
|
|
112
|
+
["stripeWebhookSecret", e.stripe.webhookSecret],
|
|
113
|
+
["freemiusSecretKey", e.freemius.secretKey],
|
|
114
|
+
["freemiusApiKey", e.freemius.apiKey]
|
|
115
|
+
];
|
|
116
|
+
if (i.some(([, a]) => a && a.trim())) {
|
|
117
|
+
if (h())
|
|
118
|
+
throw new Error("The sandbox cannot store live payment credentials.");
|
|
119
|
+
const { data: a } = await r.from("site_settings").select("value").eq("key", f).maybeSingle(), y = { ...n(a?.value) };
|
|
120
|
+
for (const [_, m] of i)
|
|
121
|
+
m && m.trim() && (y[_] = g(m.trim()));
|
|
122
|
+
const { error: K } = await r.from("site_settings").upsert({ key: f, value: y });
|
|
123
|
+
if (K)
|
|
124
|
+
throw console.error("Error saving payment_secret settings:", K.message), new Error("Failed to save payment credentials.");
|
|
125
|
+
}
|
|
126
|
+
R();
|
|
127
|
+
}
|
|
128
|
+
export {
|
|
129
|
+
R as clearPaymentConfigCache,
|
|
130
|
+
F as getPaymentConfigStatus,
|
|
131
|
+
M as getPaymentCredentialsView,
|
|
132
|
+
U as hydrateFreemiusEnvFromDb,
|
|
133
|
+
k as resolveStripePublishableKey,
|
|
134
|
+
w as resolveStripeSecretKey,
|
|
135
|
+
T as resolveStripeWebhookSecret,
|
|
136
|
+
Y as savePaymentCredentials
|
|
137
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const V=require("@supabase/supabase-js"),Z=require("crypto"),oe=require("@freemius/sdk"),ce=require("../customer.cjs.js"),Q=require("../coupon-server.cjs.js"),G=require("../customer-addresses.cjs.js"),j=require("../currency.cjs.js");function i(e){const r=process.env[e];if(!r)return null;const n=r.trim();return n.startsWith('"')&&n.endsWith('"')||n.startsWith("'")&&n.endsWith("'")?n.slice(1,-1).trim():n}function
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const V=require("@supabase/supabase-js"),Z=require("crypto"),oe=require("@freemius/sdk"),ce=require("../payment-config.cjs.js"),ae=require("../customer.cjs.js"),Q=require("../coupon-server.cjs.js"),G=require("../customer-addresses.cjs.js"),j=require("../currency.cjs.js");function i(e){const r=process.env[e];if(!r)return null;const n=r.trim();return n.startsWith('"')&&n.endsWith('"')||n.startsWith("'")&&n.endsWith("'")?n.slice(1,-1).trim():n}function ue(e){const r=e?.trim();if(!r)return{firstName:null,lastName:null};const[n,...s]=r.split(/\s+/);return{firstName:n||null,lastName:s.length>0?s.join(" "):null}}function ee(e){if(e==null||e==="")return 0;const r=Number(e);return!Number.isFinite(r)||r<=0?0:Math.round(r)}function le(e){return e===!0||e===1||e==="1"||e==="true"}function de(e,r){const n=e?.[r];return Array.isArray(n)?n:n&&typeof n=="object"?[n]:[]}function H(e,r){for(const n of r){const s=de(e,n);if(s.length>0)return s}return Array.isArray(e)?e:[]}function k(e){if(e==null||e==="")return null;const r=Number(e);return Number.isFinite(r)?r:null}function pe(e){return e===null?0:e>5e3?(console.warn(`[Freemius Sync] Suspiciously high price detected: ${e}. Assuming it is already in cents.`),Math.round(e)):Math.round(e*100)}function _e(e){const r=Number(e);return!Number.isFinite(r)||r<1?1:Math.round(r)}function re(){const e=i("FREEMIUS_CHECKOUT_PRODUCTS_JSON");if(!e)return null;try{const r=JSON.parse(e);return r&&typeof r=="object"?r:null}catch(r){return console.error("[Freemius Checkout] Failed to parse FREEMIUS_CHECKOUT_PRODUCTS_JSON:",r),null}}function ne(e){const r=re(),n=String(e),s=r?.[n],c=i("FREEMIUS_PRODUCT_ID"),d=i("FREEMIUS_ECOMMERCE_SANDBOX_PUBLIC_KEY"),f=i("FREEMIUS_ECOMMERCE_SANDBOX_SECRET_KEY");return s?.publicKey?{publicKey:s.publicKey,secretKey:s.secretKey??null,apiKey:s.apiKey??null,source:"product-map"}:process.env.FREEMIUS_SANDBOX_ENABLED==="true"&&c&&c===n&&d?{publicKey:d,secretKey:f,apiKey:i("FREEMIUS_API_KEY"),source:"single-product-sandbox-env"}:c&&c===n&&i("FREEMIUS_PUBLIC_KEY")?{publicKey:i("FREEMIUS_PUBLIC_KEY"),secretKey:i("FREEMIUS_SECRET_KEY"),apiKey:i("FREEMIUS_API_KEY"),source:"single-product-env"}:{publicKey:i("FREEMIUS_PUBLIC_KEY"),secretKey:i("FREEMIUS_SECRET_KEY"),apiKey:i("FREEMIUS_API_KEY"),source:"legacy-env"}}async function me(e){if(!e.apiKey)throw new Error("Missing Freemius API key for SDK sandbox generation.");return new oe.Freemius({productId:Number(e.productId),apiKey:e.apiKey,secretKey:e.secretKey,publicKey:e.publicKey}).checkout.getSandboxParams()}class fe{getProviderName(){return"Freemius"}async createCheckoutSession({items:r,customerEmail:n,customerPhone:s,userId:c,billingAddress:d,shippingAddress:f,currencyCode:m,couponCode:b,couponContextItems:E}){const a=process.env.NEXT_PUBLIC_SUPABASE_URL||process.env.SUPABASE_URL,u=process.env.SUPABASE_SERVICE_ROLE_KEY||process.env.SUPABASE_SECRET_KEY;if(!a||!u)return{error:"Missing Supabase credentials for checkout (Service Key required).",url:null};const p=V.createClient(a,u);if(!r||r.length===0)return{error:"Cart is empty",url:null};if(r.length!==1)return{error:"Freemius items must be checked out one at a time.",url:null};const{data:g,error:K}=await p.from("currencies").select("code, symbol, exchange_rate, is_default, is_active, auto_sync_product_prices, auto_update_exchange_rate, exchange_rate_source, exchange_rate_updated_at, rounding_mode, rounding_increment, rounding_charm_amount").eq("is_active",!0).order("code",{ascending:!0}),$=g??[];if(K||$.length===0)return{error:"Failed to resolve store currencies",url:null};const X=j.getDefaultCurrency($),U=$.find(l=>l.code===(m||"").toUpperCase())??X,S=r[0],{data:o,error:M}=await p.from("products").select("id, title, price, prices, sale_price, sale_prices, sale_start_at, sale_end_at, scheduled_price, scheduled_prices, scheduled_price_at, freemius_plan_id, freemius_product_id, trial_period_days, trial_requires_payment_method").eq("id",S.product_id).single();if(M||!o)return{error:"Product not found",url:null};const R=o.freemius_plan_id,P=o.freemius_product_id;if(!R||!P)return{error:"Product is not configured for Freemius checkout (missing Plan ID or Product ID)",url:null};const D=j.resolveEffectivePriceForCurrency({prices:o.prices||{},salePrices:o.sale_prices||{},fallbackPrice:o.price,fallbackSalePrice:o.sale_price,saleStartAt:o.sale_start_at,saleEndAt:o.sale_end_at,scheduledPrice:o.scheduled_price,scheduledPrices:o.scheduled_prices||{},scheduledPriceAt:o.scheduled_price_at,currencyCode:U.code,currencies:$}),q=D.sale_price??D.price,N=j.isSaleWindowActive({saleStartAt:o.sale_start_at,saleEndAt:o.sale_end_at}),y=q*S.quantity;let t=null,_=0;if(b){const l=await Q.getCouponQuote({client:p,code:b,items:E&&E.length>0?E:r,currencyCode:U.code});if(!l.success)return{error:l.error,errorKey:l.errorKey,errorStatus:400,url:null};t=l.quote,_=Math.min(y,t.lineDiscounts.filter(v=>v.product_id===o.id).reduce((v,L)=>v+L.discount,0))}const F=ee(o.trial_period_days),w=F>0?S.trial_preference?S.trial_preference:o.trial_requires_payment_method?"paid":"free":null,O="pending",h=ue(d?.recipient_name??null),{data:Y,error:J}=await p.from("orders").insert({status:O,total:Math.max(0,y-_),currency:U.code,exchange_rate_at_purchase:U.exchange_rate,subtotal:y,discount_total:_,coupon_id:t?.couponId??null,coupon_code:t?.code??null,discount_details:t?{code:t.code,discount_type:t.discountType,discount_amount:t.discountAmount,provider:"freemius",provider_discounts:t.providerDiscounts,line_discounts:t.lineDiscounts,final_amount_owned_by:"freemius"}:null,provider:"freemius",freemius_product_id:String(P),freemius_plan_id:String(R),user_id:c||null,customer_details:ae.normalizeOrderCustomerDetails({email:n,phone:s,name:d?.recipient_name,billing:d,shipping:f})}).select("id").single();if(J||!Y)return console.error("Failed to create pending order:",J),{error:"Failed to initiate order",url:null};const{error:W}=await p.from("order_items").insert([{order_id:Y.id,product_id:o.id,quantity:S.quantity,price_at_purchase:q}]);if(W&&console.error("Failed to insert order items:",W),c)try{await G.upsertDefaultUserAddresses({userId:c,billingAddress:d,shippingAddress:f,client:p}),await G.fillMissingUserProfileCheckoutDetails({userId:c,fullName:d?.recipient_name??f?.recipient_name??null,phone:s,client:p})}catch(l){console.error("Failed to sync checkout profile defaults before checkout:",l)}t&&await Q.recordCouponRedemption({client:p,quote:t,orderId:Y.id,provider:"freemius",discountTotal:_,userId:c,customerEmail:n,metadata:{currency:U.code,subtotal:y,final_amount_owned_by:"freemius"}}),await ce.hydrateFreemiusEnvFromDb();const B=process.env.FREEMIUS_SANDBOX_ENABLED==="true",A=ne(P),I=A.publicKey,x=A.secretKey,z=A.apiKey;if(!I||B&&!x)return{error:"Missing FREEMIUS credentials (PUBLIC_KEY or SECRET_KEY) in environment variables.",url:null};if(B&&A.source==="legacy-env"){const l=i("FREEMIUS_PRODUCT_ID"),v=!!i("FREEMIUS_ECOMMERCE_SANDBOX_PUBLIC_KEY"),L=!!i("FREEMIUS_ECOMMERCE_SANDBOX_SECRET_KEY");console.warn(`[Freemius Checkout] Sandbox is enabled for product ${P}, but no product-scoped checkout credentials were selected. Falling back to legacy FREEMIUS_PUBLIC_KEY/FREEMIUS_SECRET_KEY may open live checkout instead of sandbox.`,{configuredProductId:l,productIdsMatch:l===String(P),hasSandboxOverridePublicKey:v,hasSandboxOverrideSecretKey:L,hasCheckoutProductsJson:!!i("FREEMIUS_CHECKOUT_PRODUCTS_JSON")})}let T=!1;if(B&&x&&I)try{T=await me({productId:P,publicKey:I,secretKey:x,apiKey:z})}catch(l){console.warn("Freemius Checkout - SDK sandbox generation failed. Falling back to manual token generation.",l,{credentialSource:A.source,hasApiKey:!!z});const v=Math.floor(Date.now()/1e3).toString(),L=`${v}${P}${x}${I}checkout`,ie=Z.createHash("md5").update(L).digest("hex");T={ctx:v,token:ie}}const C=new URL(`https://checkout.freemius.com/app/${P}/plan/${R}/`);if(B&&x&&I?(C.searchParams.append("sandbox",T.token),C.searchParams.append("s_ctx_ts",T.ctx)):B&&C.searchParams.append("sandbox","true"),n&&C.searchParams.append("user_email",n),h.firstName&&C.searchParams.append("user_firstname",h.firstName),h.lastName&&C.searchParams.append("user_lastname",h.lastName),C.searchParams.append("currency",U.code.toLowerCase()),S.billing_cycle&&C.searchParams.append("billing_cycle",S.billing_cycle),w&&C.searchParams.append("trial",w),t)C.searchParams.append("coupon",t.code);else if(N){const{data:l}=await p.from("product_freemius_sale_coupons").select("freemius_coupon_code, is_active, starts_at, ends_at, sync_status").eq("product_id",o.id).maybeSingle();l?.is_active&&l.sync_status==="synced"&&l.freemius_coupon_code&&j.isSaleWindowActive({saleStartAt:l.starts_at,saleEndAt:l.ends_at})&&C.searchParams.append("coupon",l.freemius_coupon_code)}return{url:C.toString(),customProps:{provider:"freemius",plugin_id:P,plan_id:R,public_key:I,user_email:n,user_firstname:h.firstName,user_lastname:h.lastName,credential_source:A.source,sandbox:T,billing_cycle:S.billing_cycle,trial:w,trial_period_days:F,trial_requires_payment_method:o.trial_requires_payment_method,initial_order_status:O,coupon:t?.code??null,order_id:Y.id}}}}async function te(e,r,n,s){const d=new Date().toUTCString().replace("GMT","+0000"),f=`GET
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
${d}
|
|
5
|
-
${e}`,m=Z.createHmac("sha256",s).update(f).digest("hex"),b=Buffer.from(m).toString("base64").replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,""),E=`FS ${r}:${n}:${b}`,a=await fetch(`https://api.freemius.com${e}`,{headers:{Authorization:E,Date:d,Accept:"application/json"}});if(!a.ok){const u=await a.text();throw console.error(`[Freemius API] [ERROR] ${e} returned ${a.status}: ${u}`),new Error(`Freemius API failed on ${e}: ${a.status} - ${u}`)}return a.json()}async function
|
|
5
|
+
${e}`,m=Z.createHmac("sha256",s).update(f).digest("hex"),b=Buffer.from(m).toString("base64").replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,""),E=`FS ${r}:${n}:${b}`,a=await fetch(`https://api.freemius.com${e}`,{headers:{Authorization:E,Date:d,Accept:"application/json"}});if(!a.ok){const u=await a.text();throw console.error(`[Freemius API] [ERROR] ${e} returned ${a.status}: ${u}`),new Error(`Freemius API failed on ${e}: ${a.status} - ${u}`)}return a.json()}async function Ee(){const e=i("FREEMIUS_DEVELOPER_ID"),r=i("FREEMIUS_PUBLIC_KEY"),n=i("FREEMIUS_SECRET_KEY"),s=process.env.NEXT_PUBLIC_SUPABASE_URL||process.env.SUPABASE_URL,c=process.env.SUPABASE_SERVICE_ROLE_KEY||process.env.SUPABASE_SECRET_KEY;if(!e||!r||!n||!s||!c)throw new Error("Missing necessary environment variables for Freemius Sync.");const d=V.createClient(s,c,{auth:{autoRefreshToken:!1,persistSession:!1}}),f=m=>te(m,e,r,n);try{console.log(`[Freemius Sync] Fetching all plugins for developer ${e}...`);const m=await f(`/v1/developers/${e}/plugins.json`),b=H(m,["plugins","plugin"]);console.log(`[Freemius Sync] Found ${b.length} plugins. Syncing plans...`);let E=0;const{data:a}=await d.from("languages").select("id").eq("code","en").single(),u=a?.id;if(!u)throw new Error("English language not found in database. Cannot sync products.");for(const p of b){const g=p.id?.toString();if(!g){console.warn("[Freemius Sync] Skipping plugin without an id:",p);continue}const K=await se(d,e,g,p.title||p.name||`Freemius Product ${g}`,f,u);E+=K}return{success:!0,count:E}}catch(m){throw console.error("[Freemius Sync] Global Error:",m),m}}async function ge(e){const r=i("FREEMIUS_DEVELOPER_ID"),n=i("FREEMIUS_PUBLIC_KEY"),s=i("FREEMIUS_SECRET_KEY"),c=process.env.NEXT_PUBLIC_SUPABASE_URL||process.env.SUPABASE_URL,d=process.env.SUPABASE_SERVICE_ROLE_KEY||process.env.SUPABASE_SECRET_KEY;if(!r||!n||!s||!c||!d)throw new Error("Missing environment variables for Freemius Sync.");const f=V.createClient(c,d),m=g=>te(g,r,n,s),{data:b}=await f.from("languages").select("id").eq("code","en").single(),E=b?.id;if(!E)throw new Error("English language not found in database. Cannot sync products.");const a=await m(`/v1/developers/${r}/plugins/${e}.json`),u=a.plugin??a;return{success:!0,count:await se(f,r,e,u.title||u.name||`Freemius Product ${e}`,m,E)}}async function se(e,r,n,s,c,d){console.log(`[Freemius Sync] Fetching plans for plugin: ${s} (${n})...`);let f=0;try{const m=`/v1/developers/${r}/plugins/${n}/plans.json`,b=await c(m),E=H(b,["plans","plan"]);console.log(`[Freemius Sync] Received ${E.length} plans for plugin ${n}.`);for(const a of E){const u=a.id?.toString();if(!u){console.warn("[Freemius Sync] Skipping plan without an id:",a);continue}const p=a.name||a.title||u,g=a.title||p;console.log(`[Freemius Sync] Processing plan: ${g} (${u})...`);let K=a;if(K.trial_period===void 0||K.is_require_subscription===void 0)try{const t=`/v1/developers/${r}/plugins/${n}/plans/${u}.json`,_=await c(t),F=_.plan??_;K={...K,...F}}catch(t){console.warn(`[Freemius Sync] Could not fetch trial details for plan ${u}:`,t instanceof Error?t.message:t)}const $=ee(K.trial_period),X=$>0&&le(K.is_require_subscription);let U=0,S=[];try{const t=`/v1/developers/${r}/plugins/${n}/plans/${u}/pricing.json`,_=await c(t);if(S=H(_,["pricing","prices","pricings"]),S.length>0){const F=S[0],w=k(F.annual_price)??k(F.monthly_price)??k(F.lifetime_price);U=pe(w)}console.log(`[Freemius Sync] Plan: ${g} -> Resolved Price (cents): ${U}`)}catch(t){console.warn(`[Freemius Sync] Could not fetch pricing for plan ${u}:`,t instanceof Error?t.message:t)}const o=`${s}-${g}`.toLowerCase().replace(/[^\w\s-]/g,"").replace(/[\s_]+/g,"-").replace(/^-+|-+$/g,""),M={title:`${s} - ${g}`,slug:o,short_description:a.description||"",price:U,product_type:"digital",payment_provider:"freemius",freemius_plan_id:u,freemius_product_id:n,trial_period_days:$,trial_requires_payment_method:X,status:"active",stock:999,sku:`FM-${n}-${u}`,language_id:d},{data:R,error:P}=await e.from("products").upsert(M,{onConflict:"language_id, sku"}).select();if(P||!R||R.length===0){console.error(`[Freemius Sync] Error upserting product ${M.sku}:`,P);continue}const D=R[0].id,{data:q,error:N}=await e.from("freemius_plans").select("id").eq("product_id",D).eq("name",p).maybeSingle();N&&console.warn(`[Freemius Sync] Could not check existing local plan for ${M.sku}:`,N.message||N);let y="";if(q){y=q.id;const{error:t}=await e.from("freemius_plans").update({title:g,updated_at:new Date().toISOString()}).eq("id",y);t&&console.warn(`[Freemius Sync] Could not update local plan ${y}:`,t.message||t)}else{const{data:t,error:_}=await e.from("freemius_plans").insert({product_id:D,name:p,title:g}).select("id").single();_&&console.warn(`[Freemius Sync] Could not insert local plan for ${M.sku}:`,_.message||_),t&&(y=t.id)}if(y&&S.length>0)for(const t of S){const _=_e(t.licenses??t.license_quota??t.quota),{data:F,error:w}=await e.from("freemius_pricing").select("id").eq("plan_id",y).eq("license_quota",_).maybeSingle();w&&console.warn(`[Freemius Sync] Could not check pricing for plan ${y}, quota ${_}:`,w.message||w);const O={api_monthly_price:k(t.monthly_price),api_annual_price:k(t.annual_price),api_lifetime_price:k(t.lifetime_price),updated_at:new Date().toISOString()};if(F){const{error:h}=await e.from("freemius_pricing").update(O).eq("id",F.id);h&&console.warn(`[Freemius Sync] Could not update pricing ${F.id}:`,h.message||h)}else{const{error:h}=await e.from("freemius_pricing").insert({plan_id:y,license_quota:_,...O});h&&console.warn(`[Freemius Sync] Could not insert pricing for plan ${y}, quota ${_}:`,h.message||h)}}console.log(`[Freemius Sync] Successfully fully synced product ${M.sku}.`),f++}}catch(m){console.error(`[Freemius Sync] Failed sync for plugin ${n}:`,m.message)}return f}exports.FreemiusProvider=fe;exports.parseFreemiusCheckoutCredentialsMap=re;exports.readFreemiusEnvValue=i;exports.resolveFreemiusCheckoutCredentials=ne;exports.syncFreemiusProductsToSupabase=Ee;exports.syncSingleFreemiusProduct=ge;
|