@nextblock-cms/ecom 0.10.1 → 0.10.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 (34) hide show
  1. package/lib/components/CustomerProfileForm.cjs.js +1 -1
  2. package/lib/components/CustomerProfileForm.es.js +162 -159
  3. package/lib/freemius-coupons.cjs.js +1 -1
  4. package/lib/freemius-coupons.es.js +51 -49
  5. package/lib/pages/cms/payments/PaymentsClient.cjs.js +1 -1
  6. package/lib/pages/cms/payments/PaymentsClient.d.ts +4 -1
  7. package/lib/pages/cms/payments/PaymentsClient.es.js +255 -127
  8. package/lib/pages/cms/payments/PaymentsPage.cjs.js +1 -1
  9. package/lib/pages/cms/payments/PaymentsPage.es.js +20 -16
  10. package/lib/pages/cms/payments/actions.cjs.js +1 -1
  11. package/lib/pages/cms/payments/actions.d.ts +1 -0
  12. package/lib/pages/cms/payments/actions.es.js +41 -13
  13. package/lib/pages/cms/payments/queries.cjs.js +1 -1
  14. package/lib/pages/cms/payments/queries.es.js +13 -24
  15. package/lib/payment-config.cjs.js +1 -0
  16. package/lib/payment-config.d.ts +57 -0
  17. package/lib/payment-config.es.js +137 -0
  18. package/lib/providers/freemius.cjs.js +2 -2
  19. package/lib/providers/freemius.es.js +108 -107
  20. package/lib/providers/stripe.cjs.js +1 -1
  21. package/lib/providers/stripe.es.js +167 -167
  22. package/lib/stripe/checkout.cjs.js +1 -1
  23. package/lib/stripe/checkout.es.js +14 -14
  24. package/lib/stripe/client.cjs.js +1 -1
  25. package/lib/stripe/client.d.ts +1 -1
  26. package/lib/stripe/client.es.js +8 -5
  27. package/lib/stripe/order-sync.cjs.js +1 -1
  28. package/lib/stripe/order-sync.es.js +50 -50
  29. package/lib/stripe/webhooks.cjs.js +1 -1
  30. package/lib/stripe/webhooks.es.js +12 -10
  31. package/package.json +4 -4
  32. package/server.cjs.js +1 -1
  33. package/server.d.ts +1 -0
  34. package/server.es.js +188 -178
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const w=require("@nextblock-cms/utils"),f=require("./providers/freemius.cjs.js");function y(e){if(!e)return null;const t=new Date(e);return Number.isNaN(t.getTime())?e:t.toISOString().replace("T"," ").replace(/\.\d{3}Z$/,"")}function h(e){return e==="percent"?"percentage":"dollar"}function C(e){return f.resolveFreemiusCheckoutCredentials(e).apiKey||f.readFreemiusEnvValue("FREEMIUS_API_KEY")}async function l(e){const t=C(e.productId);if(!t)throw new Error(`Missing Freemius API bearer token for product ${e.productId}. Set FREEMIUS_API_KEY or FREEMIUS_CHECKOUT_PRODUCTS_JSON[${e.productId}].apiKey.`);const s=e.couponId?`/${e.couponId}`:"",o=await fetch(`https://api.freemius.com/v1/products/${e.productId}/coupons${s}.json`,{method:e.method,headers:{Authorization:`Bearer ${t}`,"Content-Type":"application/json",Accept:"application/json"},body:e.method==="DELETE"?void 0:JSON.stringify(e.body||{})}),n=await o.text(),r=n?JSON.parse(n):null;if(!o.ok)throw new Error(r?.message||r?.error?.message||`Freemius coupon request failed with ${o.status}`);return r}function g(e,t){return{code:e.code,discount:e.discount_type==="fixed"?w.minorUnitAmountToMajor(e.discount_amount,"USD"):e.discount_amount,discount_type:h(e.discount_type),plans:t.planIds.length>0?t.planIds.join(","):null,licenses:null,billing_cycles:null,start_date:y(e.starts_at),end_date:y(e.ends_at),redemptions_limit:e.redemption_limit??null,has_renewals_discount:!1,has_addons_discount:!1,is_one_per_user:!1,is_active:e.is_active,user_type:"all"}}async function E(e,t){if(t.provider_scope==="stripe")return[];const{data:s,error:o}=await e.from("coupon_products").select("product_id").eq("coupon_id",t.id);if(o)throw new Error(o.message);const n=(s||[]).map(c=>c.product_id);let r=e.from("products").select("id, freemius_product_id, freemius_plan_id").eq("payment_provider","freemius").not("freemius_product_id","is",null);n.length>0&&(r=r.in("id",n));const{data:u,error:i}=await r;if(i)throw new Error(i.message);const d=new Map;for(const c of u||[]){const a=String(c.freemius_product_id||"").trim();if(!a)continue;const p=d.get(a)??{freemiusProductId:a,productId:c.id,planIds:[]};n.length>0&&c.freemius_plan_id&&p.planIds.push(String(c.freemius_plan_id)),d.set(a,p)}return[...d.values()].map(c=>({...c,planIds:[...new Set(c.planIds)]}))}async function P(e,t){const{data:s}=await e.from("coupon_freemius_mappings").select("sync_status, sync_error").eq("coupon_id",t),o=s||[];if(o.length===0){await e.from("coupons").update({freemius_sync_status:"not_required",freemius_sync_error:null}).eq("id",t);return}const n=o.find(r=>r.sync_status==="failed");await e.from("coupons").update({freemius_sync_status:n?"failed":"synced",freemius_sync_error:n?.sync_error??null}).eq("id",t)}async function q(e){const{data:t,error:s}=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();if(s||!t)return{success:!1,error:s?.message||"Coupon not found"};const o=t,n=await E(e.client,o);await e.client.from("coupons").update({freemius_sync_status:n.length>0?"pending":"not_required",freemius_sync_error:null}).eq("id",o.id);for(const r of n){const{data:u}=await e.client.from("coupon_freemius_mappings").select("id, freemius_coupon_id").eq("coupon_id",o.id).eq("freemius_product_id",r.freemiusProductId).maybeSingle(),i=g(o,r);try{const d=await l({productId:r.freemiusProductId,method:u?.freemius_coupon_id?"PUT":"POST",couponId:u?.freemius_coupon_id??null,body:i}),c=d?.coupon??d;await e.client.from("coupon_freemius_mappings").upsert({id:u?.id,coupon_id:o.id,product_id:r.productId,freemius_product_id:r.freemiusProductId,freemius_coupon_id:c?.id?String(c.id):u?.freemius_coupon_id??null,freemius_coupon_code:o.code,sync_status:"synced",sync_error:null,remote_payload:d,last_synced_at:new Date().toISOString()},{onConflict:"coupon_id,freemius_product_id"})}catch(d){await e.client.from("coupon_freemius_mappings").upsert({id:u?.id,coupon_id:o.id,product_id:r.productId,freemius_product_id:r.freemiusProductId,freemius_coupon_id:u?.freemius_coupon_id??null,freemius_coupon_code:o.code,sync_status:"failed",sync_error:d.message||"Freemius sync failed",last_synced_at:new Date().toISOString()},{onConflict:"coupon_id,freemius_product_id"})}}return await P(e.client,o.id),{success:!0,targetCount:n.length}}function F(e){const t=(e.sku||"").toUpperCase().replace(/[^A-Z0-9]/g,"");return t?`SALE${t}`.slice(0,32):`SALE${e.productId.replace(/[^A-Za-z0-9]/g,"").toUpperCase()}`.slice(0,32)}function v(e,t){if(typeof e!="number"||e<=0||typeof t!="number"||t<0||t>=e)return null;const s=Math.round((1-t/e)*100);return s<=0||s>100?null:s}async function T(e){const{data:t,error:s}=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();if(s||!t)return{success:!1,error:s?.message||"Product not found"};const o=t,n=String(o.freemius_product_id||"").trim();if(o.payment_provider!=="freemius"||!n)return{success:!0,skipped:!0,reason:"not_a_freemius_product"};const r=v(o.price,o.sale_price),u=r!==null,{data:i}=await e.client.from("product_freemius_sale_coupons").select("id, freemius_coupon_id, freemius_coupon_code, discount_percent, is_active").eq("product_id",o.id).maybeSingle();if(!u&&!i?.freemius_coupon_id)return i?.id&&await e.client.from("product_freemius_sale_coupons").update({is_active:!1,updated_at:new Date().toISOString()}).eq("id",i.id),{success:!0,active:!1};const d=i?.freemius_coupon_code||F({sku:o.sku,productId:o.id}),c=o.freemius_plan_id?[String(o.freemius_plan_id)]:[],a=r??i?.discount_percent??1,p={id:o.id,code:d,name:`Scheduled sale ${o.sku||o.id}`,discount_type:"percent",discount_amount:a,is_active:u,starts_at:o.sale_start_at,ends_at:o.sale_end_at,redemption_limit:null},S={productId:o.id,planIds:c},I=g(p,S);try{const _=await l({productId:n,method:i?.freemius_coupon_id?"PUT":"POST",couponId:i?.freemius_coupon_id??null,body:I}),m=_?.coupon??_;return await e.client.from("product_freemius_sale_coupons").upsert({id:i?.id,product_id:o.id,freemius_product_id:n,freemius_plan_id:o.freemius_plan_id?String(o.freemius_plan_id):null,freemius_coupon_id:m?.id?String(m.id):i?.freemius_coupon_id??null,freemius_coupon_code:d,discount_percent:r,starts_at:o.sale_start_at,ends_at:o.sale_end_at,is_active:u,sync_status:"synced",sync_error:null,remote_payload:_,last_synced_at:new Date().toISOString()},{onConflict:"product_id"}),{success:!0,active:u,code:d}}catch(_){return await e.client.from("product_freemius_sale_coupons").upsert({id:i?.id,product_id:o.id,freemius_product_id:n,freemius_plan_id:o.freemius_plan_id?String(o.freemius_plan_id):null,freemius_coupon_id:i?.freemius_coupon_id??null,freemius_coupon_code:d,discount_percent:r,starts_at:o.sale_start_at,ends_at:o.sale_end_at,is_active:u,sync_status:"failed",sync_error:_.message||"Freemius sale coupon sync failed",last_synced_at:new Date().toISOString()},{onConflict:"product_id"}),{success:!1,error:_.message||"Freemius sale coupon sync failed"}}}async function b(e){const{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);for(const s of t||[])try{await l({productId:s.freemius_product_id,couponId:s.freemius_coupon_id,method:"DELETE"}),await e.client.from("coupon_freemius_mappings").update({sync_status:"deleted",sync_error:null,updated_at:new Date().toISOString()}).eq("id",s.id)}catch(o){await e.client.from("coupon_freemius_mappings").update({sync_status:"failed",sync_error:o.message||"Freemius delete failed",updated_at:new Date().toISOString()}).eq("id",s.id)}}exports.deleteCouponFromFreemius=b;exports.syncCouponToFreemius=q;exports.syncProductSaleCouponToFreemius=T;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const I=require("@nextblock-cms/utils"),f=require("./providers/freemius.cjs.js"),h=require("./payment-config.cjs.js");function y(e){if(!e)return null;const o=new Date(e);return Number.isNaN(o.getTime())?e:o.toISOString().replace("T"," ").replace(/\.\d{3}Z$/,"")}function C(e){return e==="percent"?"percentage":"dollar"}function E(e){return f.resolveFreemiusCheckoutCredentials(e).apiKey||f.readFreemiusEnvValue("FREEMIUS_API_KEY")}async function l(e){await h.hydrateFreemiusEnvFromDb();const o=E(e.productId);if(!o)throw new Error(`Missing Freemius API bearer token for product ${e.productId}. Set FREEMIUS_API_KEY or FREEMIUS_CHECKOUT_PRODUCTS_JSON[${e.productId}].apiKey.`);const s=e.couponId?`/${e.couponId}`:"",t=await fetch(`https://api.freemius.com/v1/products/${e.productId}/coupons${s}.json`,{method:e.method,headers:{Authorization:`Bearer ${o}`,"Content-Type":"application/json",Accept:"application/json"},body:e.method==="DELETE"?void 0:JSON.stringify(e.body||{})}),n=await t.text(),r=n?JSON.parse(n):null;if(!t.ok)throw new Error(r?.message||r?.error?.message||`Freemius coupon request failed with ${t.status}`);return r}function g(e,o){return{code:e.code,discount:e.discount_type==="fixed"?I.minorUnitAmountToMajor(e.discount_amount,"USD"):e.discount_amount,discount_type:C(e.discount_type),plans:o.planIds.length>0?o.planIds.join(","):null,licenses:null,billing_cycles:null,start_date:y(e.starts_at),end_date:y(e.ends_at),redemptions_limit:e.redemption_limit??null,has_renewals_discount:!1,has_addons_discount:!1,is_one_per_user:!1,is_active:e.is_active,user_type:"all"}}async function F(e,o){if(o.provider_scope==="stripe")return[];const{data:s,error:t}=await e.from("coupon_products").select("product_id").eq("coupon_id",o.id);if(t)throw new Error(t.message);const n=(s||[]).map(c=>c.product_id);let r=e.from("products").select("id, freemius_product_id, freemius_plan_id").eq("payment_provider","freemius").not("freemius_product_id","is",null);n.length>0&&(r=r.in("id",n));const{data:u,error:i}=await r;if(i)throw new Error(i.message);const d=new Map;for(const c of u||[]){const a=String(c.freemius_product_id||"").trim();if(!a)continue;const p=d.get(a)??{freemiusProductId:a,productId:c.id,planIds:[]};n.length>0&&c.freemius_plan_id&&p.planIds.push(String(c.freemius_plan_id)),d.set(a,p)}return[...d.values()].map(c=>({...c,planIds:[...new Set(c.planIds)]}))}async function P(e,o){const{data:s}=await e.from("coupon_freemius_mappings").select("sync_status, sync_error").eq("coupon_id",o),t=s||[];if(t.length===0){await e.from("coupons").update({freemius_sync_status:"not_required",freemius_sync_error:null}).eq("id",o);return}const n=t.find(r=>r.sync_status==="failed");await e.from("coupons").update({freemius_sync_status:n?"failed":"synced",freemius_sync_error:n?.sync_error??null}).eq("id",o)}async function q(e){const{data:o,error:s}=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();if(s||!o)return{success:!1,error:s?.message||"Coupon not found"};const t=o,n=await F(e.client,t);await e.client.from("coupons").update({freemius_sync_status:n.length>0?"pending":"not_required",freemius_sync_error:null}).eq("id",t.id);for(const r of n){const{data:u}=await e.client.from("coupon_freemius_mappings").select("id, freemius_coupon_id").eq("coupon_id",t.id).eq("freemius_product_id",r.freemiusProductId).maybeSingle(),i=g(t,r);try{const d=await l({productId:r.freemiusProductId,method:u?.freemius_coupon_id?"PUT":"POST",couponId:u?.freemius_coupon_id??null,body:i}),c=d?.coupon??d;await e.client.from("coupon_freemius_mappings").upsert({id:u?.id,coupon_id:t.id,product_id:r.productId,freemius_product_id:r.freemiusProductId,freemius_coupon_id:c?.id?String(c.id):u?.freemius_coupon_id??null,freemius_coupon_code:t.code,sync_status:"synced",sync_error:null,remote_payload:d,last_synced_at:new Date().toISOString()},{onConflict:"coupon_id,freemius_product_id"})}catch(d){await e.client.from("coupon_freemius_mappings").upsert({id:u?.id,coupon_id:t.id,product_id:r.productId,freemius_product_id:r.freemiusProductId,freemius_coupon_id:u?.freemius_coupon_id??null,freemius_coupon_code:t.code,sync_status:"failed",sync_error:d.message||"Freemius sync failed",last_synced_at:new Date().toISOString()},{onConflict:"coupon_id,freemius_product_id"})}}return await P(e.client,t.id),{success:!0,targetCount:n.length}}function v(e){const o=(e.sku||"").toUpperCase().replace(/[^A-Z0-9]/g,"");return o?`SALE${o}`.slice(0,32):`SALE${e.productId.replace(/[^A-Za-z0-9]/g,"").toUpperCase()}`.slice(0,32)}function T(e,o){if(typeof e!="number"||e<=0||typeof o!="number"||o<0||o>=e)return null;const s=Math.round((1-o/e)*100);return s<=0||s>100?null:s}async function b(e){const{data:o,error:s}=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();if(s||!o)return{success:!1,error:s?.message||"Product not found"};const t=o,n=String(t.freemius_product_id||"").trim();if(t.payment_provider!=="freemius"||!n)return{success:!0,skipped:!0,reason:"not_a_freemius_product"};const r=T(t.price,t.sale_price),u=r!==null,{data:i}=await e.client.from("product_freemius_sale_coupons").select("id, freemius_coupon_id, freemius_coupon_code, discount_percent, is_active").eq("product_id",t.id).maybeSingle();if(!u&&!i?.freemius_coupon_id)return i?.id&&await e.client.from("product_freemius_sale_coupons").update({is_active:!1,updated_at:new Date().toISOString()}).eq("id",i.id),{success:!0,active:!1};const d=i?.freemius_coupon_code||v({sku:t.sku,productId:t.id}),c=t.freemius_plan_id?[String(t.freemius_plan_id)]:[],a=r??i?.discount_percent??1,p={id:t.id,code:d,name:`Scheduled sale ${t.sku||t.id}`,discount_type:"percent",discount_amount:a,is_active:u,starts_at:t.sale_start_at,ends_at:t.sale_end_at,redemption_limit:null},S={productId:t.id,planIds:c},w=g(p,S);try{const _=await l({productId:n,method:i?.freemius_coupon_id?"PUT":"POST",couponId:i?.freemius_coupon_id??null,body:w}),m=_?.coupon??_;return await e.client.from("product_freemius_sale_coupons").upsert({id:i?.id,product_id:t.id,freemius_product_id:n,freemius_plan_id:t.freemius_plan_id?String(t.freemius_plan_id):null,freemius_coupon_id:m?.id?String(m.id):i?.freemius_coupon_id??null,freemius_coupon_code:d,discount_percent:r,starts_at:t.sale_start_at,ends_at:t.sale_end_at,is_active:u,sync_status:"synced",sync_error:null,remote_payload:_,last_synced_at:new Date().toISOString()},{onConflict:"product_id"}),{success:!0,active:u,code:d}}catch(_){return await e.client.from("product_freemius_sale_coupons").upsert({id:i?.id,product_id:t.id,freemius_product_id:n,freemius_plan_id:t.freemius_plan_id?String(t.freemius_plan_id):null,freemius_coupon_id:i?.freemius_coupon_id??null,freemius_coupon_code:d,discount_percent:r,starts_at:t.sale_start_at,ends_at:t.sale_end_at,is_active:u,sync_status:"failed",sync_error:_.message||"Freemius sale coupon sync failed",last_synced_at:new Date().toISOString()},{onConflict:"product_id"}),{success:!1,error:_.message||"Freemius sale coupon sync failed"}}}async function D(e){const{data:o}=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);for(const s of o||[])try{await l({productId:s.freemius_product_id,couponId:s.freemius_coupon_id,method:"DELETE"}),await e.client.from("coupon_freemius_mappings").update({sync_status:"deleted",sync_error:null,updated_at:new Date().toISOString()}).eq("id",s.id)}catch(t){await e.client.from("coupon_freemius_mappings").update({sync_status:"failed",sync_error:t.message||"Freemius delete failed",updated_at:new Date().toISOString()}).eq("id",s.id)}}exports.deleteCouponFromFreemius=D;exports.syncCouponToFreemius=q;exports.syncProductSaleCouponToFreemius=b;
@@ -1,19 +1,21 @@
1
- import { minorUnitAmountToMajor as w } from "@nextblock-cms/utils";
1
+ import { minorUnitAmountToMajor as I } from "@nextblock-cms/utils";
2
2
  import { resolveFreemiusCheckoutCredentials as S, readFreemiusEnvValue as h } from "./providers/freemius.es.js";
3
+ import { hydrateFreemiusEnvFromDb as C } from "./payment-config.es.js";
3
4
  function f(e) {
4
5
  if (!e)
5
6
  return null;
6
7
  const o = new Date(e);
7
8
  return Number.isNaN(o.getTime()) ? e : o.toISOString().replace("T", " ").replace(/\.\d{3}Z$/, "");
8
9
  }
9
- function C(e) {
10
+ function E(e) {
10
11
  return e === "percent" ? "percentage" : "dollar";
11
12
  }
12
- function E(e) {
13
+ function P(e) {
13
14
  return S(e).apiKey || h("FREEMIUS_API_KEY");
14
15
  }
15
16
  async function l(e) {
16
- const o = E(e.productId);
17
+ await C();
18
+ const o = P(e.productId);
17
19
  if (!o)
18
20
  throw new Error(
19
21
  `Missing Freemius API bearer token for product ${e.productId}. Set FREEMIUS_API_KEY or FREEMIUS_CHECKOUT_PRODUCTS_JSON[${e.productId}].apiKey.`
@@ -29,7 +31,7 @@ async function l(e) {
29
31
  },
30
32
  body: e.method === "DELETE" ? void 0 : JSON.stringify(e.body || {})
31
33
  }
32
- ), n = await t.text(), r = n ? JSON.parse(n) : null;
34
+ ), i = await t.text(), r = i ? JSON.parse(i) : null;
33
35
  if (!t.ok)
34
36
  throw new Error(
35
37
  r?.message || r?.error?.message || `Freemius coupon request failed with ${t.status}`
@@ -39,8 +41,8 @@ async function l(e) {
39
41
  function y(e, o) {
40
42
  return {
41
43
  code: e.code,
42
- discount: e.discount_type === "fixed" ? w(e.discount_amount, "USD") : e.discount_amount,
43
- discount_type: C(e.discount_type),
44
+ discount: e.discount_type === "fixed" ? I(e.discount_amount, "USD") : e.discount_amount,
45
+ discount_type: E(e.discount_type),
44
46
  plans: o.planIds.length > 0 ? o.planIds.join(",") : null,
45
47
  licenses: null,
46
48
  billing_cycles: null,
@@ -54,18 +56,18 @@ function y(e, o) {
54
56
  user_type: "all"
55
57
  };
56
58
  }
57
- async function P(e, o) {
59
+ async function q(e, o) {
58
60
  if (o.provider_scope === "stripe")
59
61
  return [];
60
62
  const { data: s, error: t } = await e.from("coupon_products").select("product_id").eq("coupon_id", o.id);
61
63
  if (t)
62
64
  throw new Error(t.message);
63
- const n = (s || []).map((c) => c.product_id);
65
+ const i = (s || []).map((c) => c.product_id);
64
66
  let r = e.from("products").select("id, freemius_product_id, freemius_plan_id").eq("payment_provider", "freemius").not("freemius_product_id", "is", null);
65
- n.length > 0 && (r = r.in("id", n));
66
- const { data: u, error: i } = await r;
67
- if (i)
68
- throw new Error(i.message);
67
+ i.length > 0 && (r = r.in("id", i));
68
+ const { data: u, error: n } = await r;
69
+ if (n)
70
+ throw new Error(n.message);
69
71
  const d = /* @__PURE__ */ new Map();
70
72
  for (const c of u || []) {
71
73
  const a = String(c.freemius_product_id || "").trim();
@@ -76,14 +78,14 @@ async function P(e, o) {
76
78
  productId: c.id,
77
79
  planIds: []
78
80
  };
79
- n.length > 0 && c.freemius_plan_id && p.planIds.push(String(c.freemius_plan_id)), d.set(a, p);
81
+ i.length > 0 && c.freemius_plan_id && p.planIds.push(String(c.freemius_plan_id)), d.set(a, p);
80
82
  }
81
83
  return [...d.values()].map((c) => ({
82
84
  ...c,
83
85
  planIds: [...new Set(c.planIds)]
84
86
  }));
85
87
  }
86
- async function q(e, o) {
88
+ async function F(e, o) {
87
89
  const { data: s } = await e.from("coupon_freemius_mappings").select("sync_status, sync_error").eq("coupon_id", o), t = s || [];
88
90
  if (t.length === 0) {
89
91
  await e.from("coupons").update({
@@ -92,13 +94,13 @@ async function q(e, o) {
92
94
  }).eq("id", o);
93
95
  return;
94
96
  }
95
- const n = t.find((r) => r.sync_status === "failed");
97
+ const i = t.find((r) => r.sync_status === "failed");
96
98
  await e.from("coupons").update({
97
- freemius_sync_status: n ? "failed" : "synced",
98
- freemius_sync_error: n?.sync_error ?? null
99
+ freemius_sync_status: i ? "failed" : "synced",
100
+ freemius_sync_error: i?.sync_error ?? null
99
101
  }).eq("id", o);
100
102
  }
101
- async function O(e) {
103
+ async function A(e) {
102
104
  const { data: o, error: s } = await e.client.from("coupons").select(
103
105
  "id, code, name, internal_note, provider_scope, discount_type, discount_amount, is_active, starts_at, ends_at, redemption_limit"
104
106
  ).eq("id", e.couponId).single();
@@ -107,19 +109,19 @@ async function O(e) {
107
109
  success: !1,
108
110
  error: s?.message || "Coupon not found"
109
111
  };
110
- const t = o, n = await P(e.client, t);
112
+ const t = o, i = await q(e.client, t);
111
113
  await e.client.from("coupons").update({
112
- freemius_sync_status: n.length > 0 ? "pending" : "not_required",
114
+ freemius_sync_status: i.length > 0 ? "pending" : "not_required",
113
115
  freemius_sync_error: null
114
116
  }).eq("id", t.id);
115
- for (const r of n) {
116
- const { data: u } = await e.client.from("coupon_freemius_mappings").select("id, freemius_coupon_id").eq("coupon_id", t.id).eq("freemius_product_id", r.freemiusProductId).maybeSingle(), i = y(t, r);
117
+ for (const r of i) {
118
+ const { data: u } = await e.client.from("coupon_freemius_mappings").select("id, freemius_coupon_id").eq("coupon_id", t.id).eq("freemius_product_id", r.freemiusProductId).maybeSingle(), n = y(t, r);
117
119
  try {
118
120
  const d = await l({
119
121
  productId: r.freemiusProductId,
120
122
  method: u?.freemius_coupon_id ? "PUT" : "POST",
121
123
  couponId: u?.freemius_coupon_id ?? null,
122
- body: i
124
+ body: n
123
125
  }), c = d?.coupon ?? d;
124
126
  await e.client.from("coupon_freemius_mappings").upsert(
125
127
  {
@@ -153,34 +155,34 @@ async function O(e) {
153
155
  );
154
156
  }
155
157
  }
156
- return await q(e.client, t.id), {
158
+ return await F(e.client, t.id), {
157
159
  success: !0,
158
- targetCount: n.length
160
+ targetCount: i.length
159
161
  };
160
162
  }
161
163
  function v(e) {
162
164
  const o = (e.sku || "").toUpperCase().replace(/[^A-Z0-9]/g, "");
163
165
  return o ? `SALE${o}`.slice(0, 32) : `SALE${e.productId.replace(/[^A-Za-z0-9]/g, "").toUpperCase()}`.slice(0, 32);
164
166
  }
165
- function F(e, o) {
167
+ function D(e, o) {
166
168
  if (typeof e != "number" || e <= 0 || typeof o != "number" || o < 0 || o >= e)
167
169
  return null;
168
170
  const s = Math.round((1 - o / e) * 100);
169
171
  return s <= 0 || s > 100 ? null : s;
170
172
  }
171
- async function b(e) {
173
+ async function U(e) {
172
174
  const { data: o, error: s } = await e.client.from("products").select(
173
175
  "id, sku, price, sale_price, sale_start_at, sale_end_at, payment_provider, freemius_product_id, freemius_plan_id"
174
176
  ).eq("id", e.productId).maybeSingle();
175
177
  if (s || !o)
176
178
  return { success: !1, error: s?.message || "Product not found" };
177
- const t = o, n = String(t.freemius_product_id || "").trim();
178
- if (t.payment_provider !== "freemius" || !n)
179
+ const t = o, i = String(t.freemius_product_id || "").trim();
180
+ if (t.payment_provider !== "freemius" || !i)
179
181
  return { success: !0, skipped: !0, reason: "not_a_freemius_product" };
180
- const r = F(t.price, t.sale_price), u = r !== null, { data: i } = await e.client.from("product_freemius_sale_coupons").select("id, freemius_coupon_id, freemius_coupon_code, discount_percent, is_active").eq("product_id", t.id).maybeSingle();
181
- if (!u && !i?.freemius_coupon_id)
182
- return i?.id && await e.client.from("product_freemius_sale_coupons").update({ is_active: !1, updated_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", i.id), { success: !0, active: !1 };
183
- const d = i?.freemius_coupon_code || v({ sku: t.sku, productId: t.id }), c = t.freemius_plan_id ? [String(t.freemius_plan_id)] : [], a = r ?? i?.discount_percent ?? 1, p = {
182
+ const r = D(t.price, t.sale_price), u = r !== null, { data: n } = await e.client.from("product_freemius_sale_coupons").select("id, freemius_coupon_id, freemius_coupon_code, discount_percent, is_active").eq("product_id", t.id).maybeSingle();
183
+ if (!u && !n?.freemius_coupon_id)
184
+ return n?.id && await e.client.from("product_freemius_sale_coupons").update({ is_active: !1, updated_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", n.id), { success: !0, active: !1 };
185
+ const d = n?.freemius_coupon_code || v({ sku: t.sku, productId: t.id }), c = t.freemius_plan_id ? [String(t.freemius_plan_id)] : [], a = r ?? n?.discount_percent ?? 1, p = {
184
186
  id: t.id,
185
187
  code: d,
186
188
  name: `Scheduled sale ${t.sku || t.id}`,
@@ -193,21 +195,21 @@ async function b(e) {
193
195
  }, g = {
194
196
  productId: t.id,
195
197
  planIds: c
196
- }, I = y(p, g);
198
+ }, w = y(p, g);
197
199
  try {
198
200
  const _ = await l({
199
- productId: n,
200
- method: i?.freemius_coupon_id ? "PUT" : "POST",
201
- couponId: i?.freemius_coupon_id ?? null,
202
- body: I
201
+ productId: i,
202
+ method: n?.freemius_coupon_id ? "PUT" : "POST",
203
+ couponId: n?.freemius_coupon_id ?? null,
204
+ body: w
203
205
  }), m = _?.coupon ?? _;
204
206
  return await e.client.from("product_freemius_sale_coupons").upsert(
205
207
  {
206
- id: i?.id,
208
+ id: n?.id,
207
209
  product_id: t.id,
208
- freemius_product_id: n,
210
+ freemius_product_id: i,
209
211
  freemius_plan_id: t.freemius_plan_id ? String(t.freemius_plan_id) : null,
210
- freemius_coupon_id: m?.id ? String(m.id) : i?.freemius_coupon_id ?? null,
212
+ freemius_coupon_id: m?.id ? String(m.id) : n?.freemius_coupon_id ?? null,
211
213
  freemius_coupon_code: d,
212
214
  discount_percent: r,
213
215
  starts_at: t.sale_start_at,
@@ -223,11 +225,11 @@ async function b(e) {
223
225
  } catch (_) {
224
226
  return await e.client.from("product_freemius_sale_coupons").upsert(
225
227
  {
226
- id: i?.id,
228
+ id: n?.id,
227
229
  product_id: t.id,
228
- freemius_product_id: n,
230
+ freemius_product_id: i,
229
231
  freemius_plan_id: t.freemius_plan_id ? String(t.freemius_plan_id) : null,
230
- freemius_coupon_id: i?.freemius_coupon_id ?? null,
232
+ freemius_coupon_id: n?.freemius_coupon_id ?? null,
231
233
  freemius_coupon_code: d,
232
234
  discount_percent: r,
233
235
  starts_at: t.sale_start_at,
@@ -244,7 +246,7 @@ async function b(e) {
244
246
  };
245
247
  }
246
248
  }
247
- async function A(e) {
249
+ async function $(e) {
248
250
  const { data: o } = 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);
249
251
  for (const s of o || [])
250
252
  try {
@@ -266,7 +268,7 @@ async function A(e) {
266
268
  }
267
269
  }
268
270
  export {
269
- A as deleteCouponFromFreemius,
270
- O as syncCouponToFreemius,
271
- b as syncProductSaleCouponToFreemius
271
+ $ as deleteCouponFromFreemius,
272
+ A as syncCouponToFreemius,
273
+ U as syncProductSaleCouponToFreemius
272
274
  };
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),x=require("react"),t=require("@nextblock-cms/ui"),m=require("lucide-react"),p=require("react-dom");function j({initialEnabledProviders:r,configStatus:i,saveAction:n}){const[s,a]=x.useState(r),d=i?.stripe?.hasKeys,l=i?.freemius?.hasKeys;return e.jsxs("form",{action:n,className:"space-y-6 max-w-3xl p-8",children:[e.jsx("input",{type:"hidden",name:"stripe_enabled",value:s.stripe?"true":"false"}),e.jsx("input",{type:"hidden",name:"freemius_enabled",value:s.freemius?"true":"false"}),e.jsxs("div",{children:[e.jsx("h1",{className:"text-3xl font-bold tracking-tight",children:"Payment Settings"}),e.jsx("p",{className:"text-sm text-muted-foreground",children:"Enable the payment providers your store needs. Physical products use Stripe and digital products use Freemius."})]}),e.jsxs(t.Card,{children:[e.jsxs(t.CardHeader,{children:[e.jsx(t.CardTitle,{children:"Payment Providers"}),e.jsx(t.CardDescription,{children:"You can run both providers at the same time. Each product picks its provider from its product type."})]}),e.jsxs(t.CardContent,{className:"space-y-6",children:[e.jsx(u,{id:"stripe-enabled",label:"Stripe for Physical Products",description:"Use Stripe Checkout for physical merchandise and other shippable goods.",checked:s.stripe,disabled:!d,onCheckedChange:c=>a(o=>({...o,stripe:c})),ready:d,children:d?e.jsxs("div",{className:"mt-2 text-sm text-green-600 flex items-center gap-2",children:[e.jsx(m.CheckCircle2,{className:"w-4 h-4"}),e.jsx("span",{children:"Ready to process physical product checkout"})]}):e.jsx(h,{provider:"Stripe",missingKeys:i.stripe.missing,docsUrl:"https://dashboard.stripe.com/apikeys",docsLabel:"Stripe Dashboard -> Developers -> API Keys"})}),e.jsx(u,{id:"freemius-enabled",label:"Freemius for Digital Products",description:"Use Freemius for software licenses, SaaS plans, and other digital products.",checked:s.freemius,disabled:!l,onCheckedChange:c=>a(o=>({...o,freemius:c})),ready:l,children:l?e.jsxs("div",{className:"mt-2 text-sm text-green-600 flex items-center gap-2",children:[e.jsx(m.CheckCircle2,{className:"w-4 h-4"}),e.jsx("span",{children:"Ready to process digital product checkout"})]}):e.jsx(h,{provider:"Freemius",missingKeys:i.freemius.missing,docsUrl:"https://dashboard.freemius.com/",docsLabel:"Freemius Dashboard -> Developers -> Credentials"})}),e.jsx("div",{className:"flex justify-end pt-4",children:e.jsx(b,{})})]})]})]})}function b(){const{pending:r}=p.useFormStatus();return e.jsx(t.Button,{type:"submit",disabled:r,children:r?"Saving...":"Save Changes"})}function u({id:r,label:i,description:n,checked:s,disabled:a,ready:d,onCheckedChange:l,children:c}){return e.jsx("div",{className:"rounded-md border p-4",children:e.jsxs("div",{className:"flex items-start gap-3",children:[e.jsx(t.Checkbox,{id:r,checked:s,disabled:a,onCheckedChange:o=>l(!!o),className:"mt-1"}),e.jsxs("div",{className:"grid gap-1.5 leading-none w-full",children:[e.jsxs("div",{className:"flex items-center justify-between gap-3 flex-wrap",children:[e.jsx(t.Label,{htmlFor:r,className:"font-semibold text-base cursor-pointer",children:i}),e.jsx("span",{className:`text-xs font-medium ${s?"text-foreground":"text-muted-foreground"}`,children:s?"Enabled":"Disabled"})]}),e.jsx("p",{className:"text-sm text-muted-foreground",children:n}),!d&&e.jsx("p",{className:"text-xs text-amber-700",children:"This provider cannot be enabled until all required environment variables are present."}),c]})]})})}function h({provider:r,missingKeys:i,docsUrl:n,docsLabel:s}){return e.jsxs("div",{className:"mt-3 text-sm p-4 rounded-md border border-destructive/20 bg-destructive/5 text-foreground",children:[e.jsxs("div",{className:"flex items-center gap-2 font-semibold text-destructive mb-2",children:[e.jsx(m.AlertCircle,{className:"w-4 h-4"}),e.jsx("span",{children:"Configuration Required"})]}),e.jsxs("p",{className:"mb-2",children:["The ",r," integration is missing the following environment variables:"]}),e.jsx("ul",{className:"list-disc list-inside bg-white/50 dark:bg-black/20 p-2 rounded mb-3 font-mono text-xs",children:i.map(a=>e.jsx("li",{children:a},a))}),e.jsx("p",{className:"mb-2",children:e.jsx("strong",{children:"How to fix:"})}),e.jsxs("ol",{className:"list-decimal list-inside space-y-1 ml-1 mb-3",children:[e.jsxs("li",{children:["Go to"," ",e.jsx("a",{href:n,target:"_blank",rel:"noopener noreferrer",className:"underline font-medium hover:text-destructive/80",children:s}),"."]}),e.jsx("li",{children:"Copy your API keys."}),e.jsxs("li",{children:["Open your ",e.jsx("code",{children:".env"})," (or variables settings in Vercel/Railway)."]}),e.jsx("li",{children:"Add the keys listed above."}),e.jsx("li",{children:"Restart your development server."})]})]})}exports.PaymentsClient=j;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),f=require("react"),r=require("@nextblock-cms/ui"),u=require("lucide-react"),y=require("react-dom");function v({initialEnabledProviders:s,configStatus:t,credentials:i,saveAction:a,saveCredentialsAction:l}){const[d,m]=f.useState(s),c=t?.stripe?.hasKeys,o=t?.freemius?.hasKeys;return e.jsxs("div",{className:"space-y-6 max-w-3xl p-8",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"text-3xl font-bold tracking-tight",children:"Payment Settings"}),e.jsx("p",{className:"text-sm text-muted-foreground",children:"Enter your provider API keys, then enable the providers your store needs. Physical products use Stripe and digital products use Freemius."})]}),e.jsx(g,{credentials:i,saveAction:l}),e.jsxs("form",{action:a,className:"space-y-6",children:[e.jsx("input",{type:"hidden",name:"stripe_enabled",value:d.stripe?"true":"false"}),e.jsx("input",{type:"hidden",name:"freemius_enabled",value:d.freemius?"true":"false"}),e.jsxs(r.Card,{children:[e.jsxs(r.CardHeader,{children:[e.jsx(r.CardTitle,{children:"Payment Providers"}),e.jsx(r.CardDescription,{children:"You can run both providers at the same time. Each product picks its provider from its product type."})]}),e.jsxs(r.CardContent,{className:"space-y-6",children:[e.jsx(x,{id:"stripe-enabled",label:"Stripe for Physical Products",description:"Use Stripe Checkout for physical merchandise and other shippable goods.",checked:d.stripe,disabled:!c,onCheckedChange:h=>m(p=>({...p,stripe:h})),ready:c,children:c?e.jsxs("div",{className:"mt-2 text-sm text-green-600 flex items-center gap-2",children:[e.jsx(u.CheckCircle2,{className:"w-4 h-4"}),e.jsx("span",{children:"Ready to process physical product checkout"})]}):e.jsx(j,{provider:"Stripe",missingKeys:t.stripe.missing,docsUrl:"https://dashboard.stripe.com/apikeys",docsLabel:"Stripe Dashboard -> Developers -> API Keys"})}),e.jsx(x,{id:"freemius-enabled",label:"Freemius for Digital Products",description:"Use Freemius for software licenses, SaaS plans, and other digital products.",checked:d.freemius,disabled:!o,onCheckedChange:h=>m(p=>({...p,freemius:h})),ready:o,children:o?e.jsxs("div",{className:"mt-2 text-sm text-green-600 flex items-center gap-2",children:[e.jsx(u.CheckCircle2,{className:"w-4 h-4"}),e.jsx("span",{children:"Ready to process digital product checkout"})]}):e.jsx(j,{provider:"Freemius",missingKeys:t.freemius.missing,docsUrl:"https://dashboard.freemius.com/",docsLabel:"Freemius Dashboard -> Developers -> Credentials"})}),e.jsx("div",{className:"flex justify-end pt-4",children:e.jsx(b,{})})]})]})]})]})}function b(){const{pending:s}=y.useFormStatus();return e.jsx(r.Button,{type:"submit",disabled:s,children:s?"Saving...":"Save Changes"})}function n({id:s,label:t,type:i="text",defaultValue:a,placeholder:l,hint:d}){return e.jsxs("div",{className:"space-y-1.5",children:[e.jsx(r.Label,{htmlFor:s,children:t}),e.jsx(r.Input,{id:s,name:s,type:i,defaultValue:a,placeholder:l,autoComplete:i==="password"?"new-password":"off"}),d&&e.jsx("p",{className:"text-xs text-muted-foreground",children:d})]})}function g({credentials:s,saveAction:t}){const i="•••••••• (stored — leave blank to keep)";return e.jsxs(r.Card,{children:[e.jsxs(r.CardHeader,{children:[e.jsx(r.CardTitle,{children:"Provider API Keys"}),e.jsxs(r.CardDescription,{children:["Keys are encrypted at rest and used DB-first (these override any ",e.jsx("code",{children:".env"})," ","values). Leave a secret blank to keep the stored value."]})]}),e.jsx(r.CardContent,{children:e.jsxs("form",{action:t,className:"space-y-6",children:[e.jsxs("div",{className:"space-y-4",children:[e.jsx("h3",{className:"text-sm font-semibold",children:"Stripe (physical products)"}),e.jsxs("div",{className:"grid gap-4 sm:grid-cols-2",children:[e.jsx(n,{id:"stripe_publishableKey",label:"Publishable key",defaultValue:s.stripe.publishableKey,placeholder:"pk_live_…"}),e.jsx(n,{id:"stripe_secretKey",label:"Secret key",type:"password",placeholder:s.stripe.hasSecretKey?i:"sk_live_…"}),e.jsx(n,{id:"stripe_webhookSecret",label:"Webhook signing secret",type:"password",placeholder:s.stripe.hasWebhookSecret?i:"whsec_…"})]})]}),e.jsxs("div",{className:"space-y-4",children:[e.jsx("h3",{className:"text-sm font-semibold",children:"Freemius (digital products)"}),e.jsxs("div",{className:"grid gap-4 sm:grid-cols-2",children:[e.jsx(n,{id:"freemius_developerId",label:"Developer ID",defaultValue:s.freemius.developerId}),e.jsx(n,{id:"freemius_productId",label:"Product ID",defaultValue:s.freemius.productId}),e.jsx(n,{id:"freemius_publicKey",label:"Public key",defaultValue:s.freemius.publicKey,placeholder:"pk_…"}),e.jsx(n,{id:"freemius_secretKey",label:"Secret key",type:"password",placeholder:s.freemius.hasSecretKey?i:"sk_…"}),e.jsx(n,{id:"freemius_apiKey",label:"API key",type:"password",placeholder:s.freemius.hasApiKey?i:"API key"})]})]}),s.envFallbackActive&&e.jsx("p",{className:"text-xs text-amber-700",children:"Stripe keys are currently read from environment variables. Saving here moves them into the database and takes precedence."}),e.jsx("div",{className:"flex justify-end",children:e.jsx(b,{})})]})})]})}function x({id:s,label:t,description:i,checked:a,disabled:l,ready:d,onCheckedChange:m,children:c}){return e.jsx("div",{className:"rounded-md border p-4",children:e.jsxs("div",{className:"flex items-start gap-3",children:[e.jsx(r.Checkbox,{id:s,checked:a,disabled:l,onCheckedChange:o=>m(!!o),className:"mt-1"}),e.jsxs("div",{className:"grid gap-1.5 leading-none w-full",children:[e.jsxs("div",{className:"flex items-center justify-between gap-3 flex-wrap",children:[e.jsx(r.Label,{htmlFor:s,className:"font-semibold text-base cursor-pointer",children:t}),e.jsx("span",{className:`text-xs font-medium ${a?"text-foreground":"text-muted-foreground"}`,children:a?"Enabled":"Disabled"})]}),e.jsx("p",{className:"text-sm text-muted-foreground",children:i}),!d&&e.jsx("p",{className:"text-xs text-amber-700",children:"This provider cannot be enabled until all required environment variables are present."}),c]})]})})}function j({provider:s,missingKeys:t,docsUrl:i,docsLabel:a}){return e.jsxs("div",{className:"mt-3 text-sm p-4 rounded-md border border-destructive/20 bg-destructive/5 text-foreground",children:[e.jsxs("div",{className:"flex items-center gap-2 font-semibold text-destructive mb-2",children:[e.jsx(u.AlertCircle,{className:"w-4 h-4"}),e.jsx("span",{children:"Configuration Required"})]}),e.jsxs("p",{className:"mb-2",children:["The ",s," integration still needs the following:"]}),e.jsx("ul",{className:"list-disc list-inside bg-white/50 dark:bg-black/20 p-2 rounded mb-3 text-xs",children:t.map(l=>e.jsx("li",{children:l},l))}),e.jsx("p",{className:"mb-2",children:e.jsx("strong",{children:"How to fix:"})}),e.jsxs("ol",{className:"list-decimal list-inside space-y-1 ml-1 mb-3",children:[e.jsxs("li",{children:["Get your API keys from"," ",e.jsx("a",{href:i,target:"_blank",rel:"noopener noreferrer",className:"underline font-medium hover:text-destructive/80",children:a}),"."]}),e.jsxs("li",{children:["Enter them in the ",e.jsx("strong",{children:"Provider API Keys"})," section above and save."]}),e.jsx("li",{children:"This provider can then be enabled."})]})]})}exports.PaymentsClient=v;
@@ -1,3 +1,4 @@
1
+ import { PaymentCredentialsView } from '../../../payment-config';
1
2
  interface ConfigStatus {
2
3
  stripe: {
3
4
  hasKeys: boolean;
@@ -8,12 +9,14 @@ interface ConfigStatus {
8
9
  missing: string[];
9
10
  };
10
11
  }
11
- export declare function PaymentsClient({ initialEnabledProviders, configStatus, saveAction, }: {
12
+ export declare function PaymentsClient({ initialEnabledProviders, configStatus, credentials, saveAction, saveCredentialsAction, }: {
12
13
  initialEnabledProviders: {
13
14
  stripe: boolean;
14
15
  freemius: boolean;
15
16
  };
16
17
  configStatus: ConfigStatus;
18
+ credentials: PaymentCredentialsView;
17
19
  saveAction: (formData: FormData) => Promise<void>;
20
+ saveCredentialsAction: (formData: FormData) => Promise<void>;
18
21
  }): import("react").JSX.Element;
19
22
  export {};