@lunora/payment 1.0.0-alpha.31 → 1.0.0-alpha.32

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 (44) hide show
  1. package/dist/index.mjs +1 -14
  2. package/dist/packem_shared/LunoraPaymentError-DWQ-v_NC.mjs +1 -0
  3. package/dist/packem_shared/MemoryPaymentStore-BICI1RdZ.mjs +1 -0
  4. package/dist/packem_shared/PAYMENT_TERMINAL_STATES-DVNbZxBy.mjs +1 -0
  5. package/dist/packem_shared/addMoney-BiiMn67T.mjs +1 -0
  6. package/dist/packem_shared/applyWebhookAction-BMVcvJ3t.mjs +1 -0
  7. package/dist/packem_shared/constantTimeEqual-BpVBvmN-.mjs +1 -0
  8. package/dist/packem_shared/createAdapterRegistry-CNIpj46I.mjs +1 -0
  9. package/dist/packem_shared/createDatabasePaymentStore-IG8Cb3_L.mjs +1 -0
  10. package/dist/packem_shared/createPayment-CW_iR6Hg.mjs +1 -0
  11. package/dist/packem_shared/entitlementsForReference-BbEOKu5d.mjs +1 -0
  12. package/dist/packem_shared/idempotencyKey-CfJKqcE3.mjs +1 -0
  13. package/dist/packem_shared/json-D85Nbd92.mjs +1 -0
  14. package/dist/packem_shared/lunoraDatabaseToPaymentDatabase-BFBlKLG3.mjs +1 -0
  15. package/dist/packem_shared/not-supported-DwGy4SQL.mjs +1 -0
  16. package/dist/packem_shared/observability-C9tz8mj_.mjs +1 -0
  17. package/dist/packem_shared/paymentTables-BMtUrXkM.mjs +1 -0
  18. package/dist/packem_shared/reconcile-DDdfivoy.mjs +1 -0
  19. package/dist/packem_shared/subscription-event-C-71ye9J.mjs +1 -0
  20. package/dist/providers/autumn-features.mjs +1 -95
  21. package/dist/providers/autumn.mjs +1 -275
  22. package/dist/providers/creem.mjs +1 -236
  23. package/dist/providers/dodopayments.mjs +1 -259
  24. package/dist/providers/polar.mjs +1 -231
  25. package/dist/providers/stripe.mjs +1 -271
  26. package/package.json +2 -2
  27. package/dist/packem_shared/LunoraPaymentError-BeQlhkZj.mjs +0 -19
  28. package/dist/packem_shared/MemoryPaymentStore-DvgdWa3C.mjs +0 -72
  29. package/dist/packem_shared/PAYMENT_TERMINAL_STATES-DrxV0clv.mjs +0 -26
  30. package/dist/packem_shared/addMoney-jSh_7TfF.mjs +0 -60
  31. package/dist/packem_shared/applyWebhookAction-CLAx4svt.mjs +0 -177
  32. package/dist/packem_shared/constantTimeEqual-BQjoW85H.mjs +0 -71
  33. package/dist/packem_shared/createAdapterRegistry-Ds7bx_TK.mjs +0 -24
  34. package/dist/packem_shared/createDatabasePaymentStore-C0NUCj1H.mjs +0 -172
  35. package/dist/packem_shared/createPayment-CKQAznOL.mjs +0 -200
  36. package/dist/packem_shared/entitlementsForReference-CzZGXPoZ.mjs +0 -51
  37. package/dist/packem_shared/idempotencyKey-BzaHGdHD.mjs +0 -9
  38. package/dist/packem_shared/json-D0Cb5aMv.mjs +0 -29
  39. package/dist/packem_shared/lunoraDatabaseToPaymentDatabase-DTsgPvfO.mjs +0 -29
  40. package/dist/packem_shared/not-supported-C0onRyia.mjs +0 -7
  41. package/dist/packem_shared/observability-CvhJ205g.mjs +0 -11
  42. package/dist/packem_shared/paymentTables-D4TbhaIk.mjs +0 -59
  43. package/dist/packem_shared/reconcile-qE3ERi3D.mjs +0 -85
  44. package/dist/packem_shared/subscription-event-CwEsWRCK.mjs +0 -17
package/dist/index.mjs CHANGED
@@ -1,14 +1 @@
1
- export { createAdapterRegistry } from './packem_shared/createAdapterRegistry-Ds7bx_TK.mjs';
2
- export { lunoraDatabaseToPaymentDatabase, paymentsFromContext } from './packem_shared/lunoraDatabaseToPaymentDatabase-DTsgPvfO.mjs';
3
- export { createPayment } from './packem_shared/createPayment-CKQAznOL.mjs';
4
- export { createDatabasePaymentStore } from './packem_shared/createDatabasePaymentStore-C0NUCj1H.mjs';
5
- export { entitlementsForReference, featureNames, hasActivePrice, resolveEntitlements, usagePeriodStart } from './packem_shared/entitlementsForReference-CzZGXPoZ.mjs';
6
- export { LunoraPaymentError } from './packem_shared/LunoraPaymentError-BeQlhkZj.mjs';
7
- export { idempotencyKey } from './packem_shared/idempotencyKey-BzaHGdHD.mjs';
8
- export { addMoney, allocateMoney, compareMoney, formatMoney, fromMoneyJSON, isZeroDecimalCurrency, isZeroMoney, money, subtractMoney, toMoneyJSON, zeroMoney } from './packem_shared/addMoney-jSh_7TfF.mjs';
9
- export { reconcile } from './packem_shared/reconcile-qE3ERi3D.mjs';
10
- export { default as paymentTables } from './packem_shared/paymentTables-D4TbhaIk.mjs';
11
- export { PAYMENT_TERMINAL_STATES, SUBSCRIPTION_TERMINAL_STATES, canTransitionPayment, canTransitionSubscription, nextPaymentState, nextSubscriptionState } from './packem_shared/PAYMENT_TERMINAL_STATES-DrxV0clv.mjs';
12
- export { MemoryPaymentStore } from './packem_shared/MemoryPaymentStore-DvgdWa3C.mjs';
13
- export { default as applyWebhookAction } from './packem_shared/applyWebhookAction-CLAx4svt.mjs';
14
- export { constantTimeEqual, hmacSha256Hex, verifyCreemSignature, verifyStandardWebhook } from './packem_shared/constantTimeEqual-BQjoW85H.mjs';
1
+ import{createAdapterRegistry as r}from"./packem_shared/createAdapterRegistry-CNIpj46I.mjs";import{lunoraDatabaseToPaymentDatabase as a,paymentsFromContext as n}from"./packem_shared/lunoraDatabaseToPaymentDatabase-BFBlKLG3.mjs";import{createPayment as y}from"./packem_shared/createPayment-CW_iR6Hg.mjs";import{createDatabasePaymentStore as p}from"./packem_shared/createDatabasePaymentStore-IG8Cb3_L.mjs";import{entitlementsForReference as s,featureNames as c,hasActivePrice as x,resolveEntitlements as S,usagePeriodStart as l}from"./packem_shared/entitlementsForReference-BbEOKu5d.mjs";import{LunoraPaymentError as T}from"./packem_shared/LunoraPaymentError-DWQ-v_NC.mjs";import{idempotencyKey as P}from"./packem_shared/idempotencyKey-CfJKqcE3.mjs";import{addMoney as d,allocateMoney as A,compareMoney as E,formatMoney as N,fromMoneyJSON as h,isZeroDecimalCurrency as R,isZeroMoney as v,money as C,subtractMoney as D,toMoneyJSON as I,zeroMoney as _}from"./packem_shared/addMoney-BiiMn67T.mjs";import{reconcile as L}from"./packem_shared/reconcile-DDdfivoy.mjs";import{default as k}from"./packem_shared/paymentTables-BMtUrXkM.mjs";import{PAYMENT_TERMINAL_STATES as J,SUBSCRIPTION_TERMINAL_STATES as W,canTransitionPayment as Z,canTransitionSubscription as q,nextPaymentState as z,nextSubscriptionState as B}from"./packem_shared/PAYMENT_TERMINAL_STATES-DVNbZxBy.mjs";import{MemoryPaymentStore as K}from"./packem_shared/MemoryPaymentStore-BICI1RdZ.mjs";import{default as Y}from"./packem_shared/applyWebhookAction-BMVcvJ3t.mjs";import{constantTimeEqual as w,hmacSha256Hex as G,verifyCreemSignature as Q,verifyStandardWebhook as V}from"./packem_shared/constantTimeEqual-BpVBvmN-.mjs";export{T as LunoraPaymentError,K as MemoryPaymentStore,J as PAYMENT_TERMINAL_STATES,W as SUBSCRIPTION_TERMINAL_STATES,d as addMoney,A as allocateMoney,Y as applyWebhookAction,Z as canTransitionPayment,q as canTransitionSubscription,E as compareMoney,w as constantTimeEqual,r as createAdapterRegistry,p as createDatabasePaymentStore,y as createPayment,s as entitlementsForReference,c as featureNames,N as formatMoney,h as fromMoneyJSON,x as hasActivePrice,G as hmacSha256Hex,P as idempotencyKey,R as isZeroDecimalCurrency,v as isZeroMoney,a as lunoraDatabaseToPaymentDatabase,C as money,z as nextPaymentState,B as nextSubscriptionState,k as paymentTables,n as paymentsFromContext,L as reconcile,S as resolveEntitlements,D as subtractMoney,I as toMoneyJSON,l as usagePeriodStart,Q as verifyCreemSignature,V as verifyStandardWebhook,_ as zeroMoney};
@@ -0,0 +1 @@
1
+ import{LunoraError as o}from"@lunora/errors";const E={CONFIG_INVALID:500,CURRENCY_MISMATCH:400,FORBIDDEN:403,INVALID_TRANSITION:409,NOT_FOUND:404,PROVIDER_ERROR:502,WEBHOOK_SIGNATURE_INVALID:400,WEBHOOK_TIMESTAMP_INVALID:400};class t extends o{constructor(r,I){super(r,I,{name:"LunoraPaymentError",status:E[r]})}}export{t as LunoraPaymentError};
@@ -0,0 +1 @@
1
+ const u=(i,e)=>`${i}:${e}`,r=(i,e)=>`${i}:${e}`;class v{customers=new Map;processedEvents=new Set;sessions=new Map;subscriptions=new Map;usageEvents=new Map;getCustomerByReference(e,s){return Promise.resolve(this.customers.get(u(e,s)))}getPaymentSession(e,s){return Promise.resolve(this.sessions.get(r(e,s)))}getSubscription(e,s){return Promise.resolve(this.subscriptions.get(r(e,s)))}listSubscriptionsByReference(e){return Promise.resolve([...this.subscriptions.values()].filter(s=>s.referenceId===e))}markEventProcessed(e,s){const t=r(e,s);return this.processedEvents.has(t)?Promise.resolve(!1):(this.processedEvents.add(t),Promise.resolve(!0))}releaseEvent(e,s){return this.processedEvents.delete(r(e,s)),Promise.resolve()}markUsageReported(e,s){const t=r(e,s),o=this.usageEvents.get(t);return o&&this.usageEvents.set(t,{...o,reportedToProvider:!0}),Promise.resolve()}recordUsage(e){const s=r(e.provider,e.idempotencyKey);return this.usageEvents.has(s)?Promise.resolve(!1):(this.usageEvents.set(s,e),Promise.resolve(!0))}sumUsage(e,s,t){let o=0;for(const n of this.usageEvents.values())n.referenceId===e&&n.featureId===s&&n.createdAt>=t&&(o+=n.quantity);return Promise.resolve(o)}upsertCustomer(e){return this.customers.set(u(e.provider,e.referenceId),e),Promise.resolve()}upsertPaymentSession(e){return this.sessions.set(r(e.provider,e.id),e),Promise.resolve()}upsertSubscription(e){return this.subscriptions.set(r(e.provider,e.id),e),Promise.resolve()}}export{v as MemoryPaymentStore};
@@ -0,0 +1 @@
1
+ const c={authorized:{cancel:"canceled",capture:"captured",fail:"failed"},canceled:{},captured:{partial_refund:"partially_refunded",refund:"refunded"},failed:{},initiated:{authorize:"authorized",cancel:"canceled",capture:"captured",fail:"failed"},partially_refunded:{partial_refund:"partially_refunded",refund:"refunded"},refunded:{}},d={active:{cancel:"canceled",mark_past_due:"past_due",pause:"paused",renew:"active"},canceled:{},past_due:{activate:"active",cancel:"canceled",pause:"paused",renew:"active"},paused:{cancel:"canceled",resume:"active"},trialing:{activate:"active",cancel:"canceled",mark_past_due:"past_due"}},i=new Set(["canceled","failed","refunded"]),r=new Set(["canceled"]),n=(e,a)=>c[e][a],u=(e,a)=>n(e,a)!==void 0,t=(e,a)=>d[e][a],l=(e,a)=>t(e,a)!==void 0;export{i as PAYMENT_TERMINAL_STATES,r as SUBSCRIPTION_TERMINAL_STATES,u as canTransitionPayment,l as canTransitionSubscription,n as nextPaymentState,t as nextSubscriptionState};
@@ -0,0 +1 @@
1
+ import{toSnapshot as u}from"dinero.js";import{add as m,allocate as s,compare as p,subtract as U,dinero as M}from"dinero.js/bigint";import{LunoraPaymentError as C}from"./LunoraPaymentError-DWQ-v_NC.mjs";const F=new Set(["BIF","CLP","DJF","GNF","JPY","KMF","KRW","MGA","PYG","RWF","UGX","VND","VUV","XAF","XOF","XPF"]),d=new Set(["BHD","IQD","JOD","KWD","LYD","OMR","TND"]),t=r=>F.has(r)?0n:d.has(r)?3n:2n,D=r=>({base:10n,code:r.toUpperCase(),exponent:t(r.toUpperCase())}),n=r=>M({amount:r.minorUnits,currency:D(r.currency)}),c=r=>{const e=u(r);return{currency:e.currency.code,minorUnits:e.amount}},y=(r,e)=>{if(r.currency!==e.currency)throw new C("CURRENCY_MISMATCH",`cannot combine ${r.currency} with ${e.currency}`)},S=r=>t(r.toUpperCase())===0n,i=(r,e)=>{const o=typeof r=="bigint"?r:BigInt(Math.trunc(r));return{currency:e.toUpperCase(),minorUnits:o}},h=r=>i(0n,r),l=(r,e="en-US")=>{const o=Number(t(r.currency.toUpperCase())),a=Number(r.minorUnits)/10**o;try{return new Intl.NumberFormat(e,{currency:r.currency,style:"currency"}).format(a)}catch{return`${a.toFixed(o)} ${r.currency}`}},w=(r,e)=>(y(r,e),c(m(n(r),n(e)))),I=(r,e)=>(y(r,e),c(U(n(r),n(e)))),J=(r,e)=>(y(r,e),p(n(r),n(e))),O=(r,e)=>s(n(r),[...e]).map(o=>c(o)),P=r=>r.minorUnits===0n,R=r=>({currency:r.currency,minorUnits:r.minorUnits.toString()}),g=r=>i(BigInt(r.minorUnits),r.currency);export{w as addMoney,O as allocateMoney,J as compareMoney,l as formatMoney,g as fromMoneyJSON,S as isZeroDecimalCurrency,P as isZeroMoney,i as money,I as subtractMoney,R as toMoneyJSON,h as zeroMoney};
@@ -0,0 +1 @@
1
+ import{compareMoney as l,zeroMoney as s,addMoney as v}from"./addMoney-BiiMn67T.mjs";import{n as p}from"./observability-C9tz8mj_.mjs";import{nextPaymentState as P,nextSubscriptionState as A}from"./PAYMENT_TERMINAL_STATES-DVNbZxBy.mjs";const b={"payment.authorized":"authorize","payment.captured":"capture","payment.failed":"fail","payment.refunded":"refund"},E={"subscription.active":"active","subscription.canceled":"canceled","subscription.past_due":"past_due","subscription.paused":"paused"},S={"subscription.active":"activate","subscription.canceled":"cancel","subscription.past_due":"mark_past_due","subscription.paused":"pause"},w=(t,e)=>l(t,e)>0?t:e,I=(t,e)=>{if(!e.amount||t.refundedAmount.currency!==e.amount.currency||t.capturedAmount.currency!==e.amount.currency)return;const r=e.amountKind==="absolute"?w(e.amount,t.refundedAmount):v(t.refundedAmount,e.amount);if(!(l(r,t.capturedAmount)>0))return r},_=async(t,e,r)=>{if(!e.sessionId)return{applied:!1,reason:"unhandled"};const n=await t.getPaymentSession(e.provider,e.sessionId),a=n?.state??"initiated",d=Date.now(),i=e.amount?.currency??n?.amount.currency??"USD";let o=r;if(r==="refund"&&n){const u=I(n,e);u&&l(u,n.capturedAmount)<0&&(o="partial_refund")}const f=P(a,o);if(!f)return{applied:!1,reason:"illegal_transition"};const c=n??{amount:e.amount??s(i),capturedAmount:s(i),createdAt:d,id:e.sessionId,provider:e.provider,referenceId:e.referenceId??"",refundedAmount:s(i),state:a,updatedAt:d};let{capturedAmount:m,refundedAmount:y}=c;if(o==="capture"&&e.amount&&(m=e.amount),(o==="partial_refund"||o==="refund")&&e.amount){const u=I(c,e);if(!u)return{applied:!1,reason:"invalid_refund_amount"};y=u}return await t.upsertPaymentSession({...c,capturedAmount:m,referenceId:e.referenceId??c.referenceId,refundedAmount:y,state:f,updatedAt:d}),{applied:!0,reason:"ok"}},h=async(t,e)=>{if(!e.subscriptionId)return{applied:!1,reason:"unhandled"};const r=await t.getSubscription(e.provider,e.subscriptionId),n=Date.now();if(e.type==="subscription.updated")return r?(await t.upsertSubscription({...r,cancelAtPeriodEnd:e.cancelAtPeriodEnd??r.cancelAtPeriodEnd,currentPeriodEnd:e.currentPeriodEnd??r.currentPeriodEnd,currentPeriodStart:e.currentPeriodStart??r.currentPeriodStart,priceId:e.priceId??r.priceId,quantity:e.quantity??r.quantity,updatedAt:n}),{applied:!0,reason:"ok"}):{applied:!1,reason:"unhandled"};const a=E[e.type];if(!a)return{applied:!1,reason:"unhandled"};if(!r)return await t.upsertSubscription({cancelAtPeriodEnd:e.cancelAtPeriodEnd??!1,createdAt:n,currentPeriodEnd:e.currentPeriodEnd,currentPeriodStart:e.currentPeriodStart??n,id:e.subscriptionId,priceId:e.priceId??"",provider:e.provider,quantity:e.quantity??1,referenceId:e.referenceId??"",state:a,updatedAt:n}),{applied:!0,reason:"ok"};const d=r.state==="active"&&a==="active"?"renew":S[e.type],i=d?A(r.state,d):void 0;return i?(await t.upsertSubscription({...r,cancelAtPeriodEnd:e.cancelAtPeriodEnd??r.cancelAtPeriodEnd,currentPeriodEnd:e.currentPeriodEnd??r.currentPeriodEnd,currentPeriodStart:e.currentPeriodStart??r.currentPeriodStart,priceId:e.priceId??r.priceId,quantity:e.quantity??r.quantity,state:i,updatedAt:n}),{applied:!0,reason:"ok"}):{applied:!1,reason:"illegal_transition"}},x=async(t,e,r)=>{if(e.type==="unhandled")return{applied:!1,reason:"unhandled"};if(!await t.markEventProcessed(e.provider,e.eventId))return p(r,{eventId:e.eventId,provider:e.provider,type:"webhook.duplicate"}),{applied:!1,reason:"duplicate"};const n=b[e.type];let a;try{a=n?await _(t,e,n):await h(t,e)}catch(d){throw await t.releaseEvent(e.provider,e.eventId),d}return p(r,{action:e.type,eventId:e.eventId,provider:e.provider,reason:a.reason,type:"webhook.applied"}),e.type==="payment.failed"?p(r,{provider:e.provider,referenceId:e.referenceId,sessionId:e.sessionId,type:"payment.failed"}):e.type==="subscription.past_due"&&p(r,{provider:e.provider,referenceId:e.referenceId,subscriptionId:e.subscriptionId,type:"subscription.past_due"}),a};export{x as default};
@@ -0,0 +1 @@
1
+ import{LunoraPaymentError as o}from"./LunoraPaymentError-DWQ-v_NC.mjs";const i=new TextEncoder,I=t=>[...new Uint8Array(t)].map(e=>e.toString(16).padStart(2,"0")).join(""),h="whsec_",d=t=>new Uint8Array(Array.from(atob(t),e=>e.codePointAt(0)??0)),f=t=>btoa(String.fromCodePoint(...new Uint8Array(t))),u=async(t,e)=>{const r=await crypto.subtle.importKey("raw",t,{hash:"SHA-256",name:"HMAC"},!1,["sign"]),n=await crypto.subtle.sign("HMAC",r,i.encode(e));return f(n)},g=(t,e)=>{if(t.length!==e.length)return!1;let r=0;for(let n=0;n<t.length;n+=1)r|=(t.codePointAt(n)??0)^(e.codePointAt(n)??0);return r===0},A=async(t,e)=>{const r=await crypto.subtle.importKey("raw",i.encode(t),{hash:"SHA-256",name:"HMAC"},!1,["sign"]),n=await crypto.subtle.sign("HMAC",r,i.encode(e));return I(n)},b=async t=>{if(!t.secret)throw new o("CONFIG_INVALID","webhook secret not configured");const e=t.toleranceSeconds??300,r=t.now??Date.now(),n=Number(t.webhookTimestamp);if(!t.webhookId||!t.webhookSignature||!Number.isFinite(n))throw new o("WEBHOOK_SIGNATURE_INVALID","missing standard-webhooks headers");if(Math.abs(Math.floor(r/1e3)-n)>e)throw new o("WEBHOOK_TIMESTAMP_INVALID","signature timestamp outside tolerance");const s=t.secret.startsWith(h)?t.secret.slice(h.length):t.secret;if(!s)throw new o("CONFIG_INVALID","webhook secret not configured");const c=d(s);if(c.length===0)throw new o("CONFIG_INVALID","webhook secret not configured");const m=await u(c,`${t.webhookId}.${t.webhookTimestamp}.${t.payload}`);if(!t.webhookSignature.split(" ").map(a=>{const w=a.indexOf(",");return w===-1?"":a.slice(w+1)}).filter(Boolean).some(a=>g(a,m)))throw new o("WEBHOOK_SIGNATURE_INVALID","no matching signature")},y=async t=>{if(!t.secret)throw new o("CONFIG_INVALID","webhook secret not configured");if(!t.signature)throw new o("WEBHOOK_SIGNATURE_INVALID","missing creem-signature header");const e=await A(t.secret,t.payload);if(!g(t.signature,e))throw new o("WEBHOOK_SIGNATURE_INVALID","no matching signature")};export{g as constantTimeEqual,A as hmacSha256Hex,y as verifyCreemSignature,b as verifyStandardWebhook};
@@ -0,0 +1 @@
1
+ import{LunoraPaymentError as o}from"./LunoraPaymentError-DWQ-v_NC.mjs";const n=i=>{const e=new Map;for(const r of i){if(e.has(r.identifier))throw new o("CONFIG_INVALID",`duplicate adapter for provider "${r.identifier}"`);e.set(r.identifier,r)}return{all:()=>[...e.values()],get:r=>{const t=e.get(r);if(!t)throw new o("CONFIG_INVALID",`no adapter registered for provider "${r}"`);return t},has:r=>e.has(r)}};export{n as createAdapterRegistry};
@@ -0,0 +1 @@
1
+ import{money as s}from"./addMoney-BiiMn67T.mjs";const n=(r,t)=>typeof r[t]=="string"?r[t]:"",f=(r,t)=>typeof r[t]=="string"?r[t]:void 0,o=(r,t)=>typeof r[t]=="number"?r[t]:0,v=(r,t)=>typeof r[t]=="number"?r[t]:void 0,m=(r,t)=>r[t]===!0,c=(r,t)=>{const e=r[t];return typeof e=="bigint"?e:typeof e=="number"||typeof e=="string"?BigInt(e):0n},I=r=>({createdAt:r.createdAt,email:r.email,provider:r.provider,providerCustomerId:r.id,referenceId:r.referenceId}),A=r=>({createdAt:o(r,"createdAt"),email:f(r,"email"),id:n(r,"providerCustomerId"),provider:n(r,"provider"),referenceId:n(r,"referenceId")}),S=r=>({amountMinor:r.amount.minorUnits,capturedMinor:r.capturedAmount.minorUnits,createdAt:r.createdAt,currency:r.amount.currency,provider:r.provider,providerSessionId:r.id,referenceId:r.referenceId,refundedMinor:r.refundedAmount.minorUnits,state:r.state,updatedAt:r.updatedAt}),P=r=>{const t=n(r,"currency");return{amount:s(c(r,"amountMinor"),t),capturedAmount:s(c(r,"capturedMinor"),t),createdAt:o(r,"createdAt"),id:n(r,"providerSessionId"),provider:n(r,"provider"),referenceId:n(r,"referenceId"),refundedAmount:s(c(r,"refundedMinor"),t),state:n(r,"state"),updatedAt:o(r,"updatedAt")}},w=r=>({cancelAtPeriodEnd:r.cancelAtPeriodEnd,createdAt:r.createdAt,currentPeriodEnd:r.currentPeriodEnd,currentPeriodStart:r.currentPeriodStart,priceId:r.priceId,provider:r.provider,providerSubscriptionId:r.id,quantity:r.quantity,referenceId:r.referenceId,state:r.state,updatedAt:r.updatedAt}),y=r=>({cancelAtPeriodEnd:m(r,"cancelAtPeriodEnd"),createdAt:o(r,"createdAt"),currentPeriodEnd:v(r,"currentPeriodEnd"),currentPeriodStart:v(r,"currentPeriodStart"),id:n(r,"providerSubscriptionId"),priceId:n(r,"priceId"),provider:n(r,"provider"),quantity:o(r,"quantity"),referenceId:n(r,"referenceId"),state:n(r,"state"),updatedAt:o(r,"updatedAt")}),E=r=>({createdAt:r.createdAt,featureId:r.featureId,idempotencyKey:r.idempotencyKey,provider:r.provider,quantity:r.quantity,referenceId:r.referenceId,reportedToProvider:r.reportedToProvider}),b=r=>{const t=async(e,i,d)=>{const a=await r.findFirst(e,i);if(a){await r.patch(a._id,d);return}await r.insert(e,d)};return{getCustomerByReference:async(e,i)=>{const d=await r.findFirst("customers",{provider:e,referenceId:i});return d?A(d):void 0},getPaymentSession:async(e,i)=>{const d=await r.findFirst("paymentSessions",{provider:e,providerSessionId:i});return d?P(d):void 0},getSubscription:async(e,i)=>{const d=await r.findFirst("subscriptions",{provider:e,providerSubscriptionId:i});return d?y(d):void 0},listSubscriptionsByReference:async e=>(await r.findMany("subscriptions",{referenceId:e})).map(i=>y(i)),markEventProcessed:async(e,i)=>await r.findFirst("events",{provider:e,providerEventId:i})?!1:(await r.insert("events",{processedAt:Date.now(),provider:e,providerEventId:i,type:""}),!0),releaseEvent:async(e,i)=>{const d=await r.findFirst("events",{provider:e,providerEventId:i});d&&await r.delete(d._id)},markUsageReported:async(e,i)=>{const d=await r.findFirst("usageEvents",{idempotencyKey:i,provider:e});d&&await r.patch(d._id,{reportedToProvider:!0})},recordUsage:async e=>await r.findFirst("usageEvents",{idempotencyKey:e.idempotencyKey,provider:e.provider})?!1:(await r.insert("usageEvents",E(e)),!0),sumUsage:async(e,i,d)=>{const a=await r.findMany("usageEvents",{featureId:i,referenceId:e});let p=0;for(const u of a)o(u,"createdAt")>=d&&(p+=o(u,"quantity"));return p},upsertCustomer:async e=>t("customers",{provider:e.provider,providerCustomerId:e.id},I(e)),upsertPaymentSession:async e=>t("paymentSessions",{provider:e.provider,providerSessionId:e.id},S(e)),upsertSubscription:async e=>t("subscriptions",{provider:e.provider,providerSubscriptionId:e.id},w(e))}};export{b as createDatabasePaymentStore};
@@ -0,0 +1 @@
1
+ import{toErrorBody as h}from"@lunora/errors";import{usagePeriodStart as I,resolveEntitlements as w,featureNames as b,hasActivePrice as g}from"./entitlementsForReference-BbEOKu5d.mjs";import{LunoraPaymentError as d}from"./LunoraPaymentError-DWQ-v_NC.mjs";import{idempotencyKey as v,derivedIdempotencyKey as N}from"./idempotencyKey-CfJKqcE3.mjs";import{n as O}from"./observability-C9tz8mj_.mjs";import S from"./applyWebhookAction-BMVcvJ3t.mjs";const u=(a,r=200,o)=>Response.json(a,{headers:{"content-type":"application/json",...o},status:r}),k=a=>a&&"referenceId"in a?Object.fromEntries(Object.entries(a).filter(([r])=>r!=="referenceId")):a,q=a=>{const{adapter:r,store:o}=a,f=async e=>{if(!a.authorize)return;let t;try{t=await a.authorize(e)}catch{throw new d("FORBIDDEN",`caller not authorized for reference "${e}"`)}if(!t)throw new d("FORBIDDEN",`caller not authorized for reference "${e}"`)},l=async e=>{await f(e.referenceId);const t=k(e.metadata);let n;const i=await o.getCustomerByReference(r.identifier,e.referenceId);if(i)n=i.id;else{const s=await r.getOrCreateCustomer({email:e.email,referenceId:e.referenceId});n=s.id,await o.upsertCustomer(s)}const c=e.idempotencyKey??await N("checkout",r.identifier,e.referenceId,e.priceId,e.mode,String(e.quantity??1),e.successUrl,e.cancelUrl,t?JSON.stringify(t):"");return r.createCheckout({...e,customerId:n,idempotencyKey:c,metadata:t})},p=async(e,t,n,i,c)=>{const s=e.limit(i);if(s!==void 0){const m=await o.sumUsage(n,i,I(t)),y=s-m;return{allowed:y>=c,balance:y,limit:s,unlimited:!1,used:m}}return{allowed:e.has(i),unlimited:e.has(i)}};return{adapter:r,attach:async e=>l({...e,mode:e.mode??"subscription"}),cancelSubscription:async(e,t)=>{const n=await o.getSubscription(r.identifier,e);if(!n)throw new d("NOT_FOUND",`subscription "${e}" not found`);try{await f(n.referenceId)}catch{throw new d("NOT_FOUND",`subscription "${e}" not found`)}const i=t?.idempotencyKey??v("cancel_subscription",r.identifier,e),c=await r.cancelSubscription(e,{...t,idempotencyKey:i});return await o.upsertSubscription(c),c},check:async e=>{if(await f(e.referenceId),e.featureId===void 0&&e.priceId===void 0)throw new d("CONFIG_INVALID","check() requires a featureId or priceId");if(r.checkEntitlement)return r.checkEntitlement(e);const t=await o.listSubscriptionsByReference(e.referenceId);if(e.priceId!==void 0)return{allowed:g(t,e.priceId),unlimited:!1};if(e.featureId===void 0)throw new d("CONFIG_INVALID","check() requires a featureId or priceId");if(!a.entitlements)throw new d("CONFIG_INVALID","check() requires `entitlements` to be configured");const n=w(a.entitlements,t);return p(n,t,e.referenceId,e.featureId,e.quantity??1)},createCheckout:async e=>l(e),createPortalSession:async(e,t)=>{await f(e);const n=await o.getCustomerByReference(r.identifier,e);if(!n)throw new d("NOT_FOUND",`no customer for reference "${e}"`);return r.createPortalSession({customerId:n.id,returnUrl:t})},handleWebhook:async e=>{let t;try{const i=await e.text();t=await r.parseWebhook({headers:e.headers,payload:i})}catch(i){if(i instanceof d){const{body:c,status:s}=h(i);return u({error:c.message},s)}return u({error:"webhook error"},400)}const n=await S(o,t,a.observability);return u({applied:n.applied,reason:n.reason},200)},listBalances:async e=>{if(await f(e),r.getBalances)return r.getBalances(e);if(!a.entitlements)throw new d("CONFIG_INVALID","listBalances() requires `entitlements` to be configured");const t=await o.listSubscriptionsByReference(e),n=w(a.entitlements,t);return Promise.all(b(a.entitlements).map(async i=>({featureId:i,...await p(n,t,e,i,1)})))},listSubscriptions:async e=>(await f(e),o.listSubscriptionsByReference(e)),store:o,track:async e=>{await f(e.referenceId);const t=e.quantity??1,n=e.idempotencyKey??crypto.randomUUID();let i=t;if(e.mode==="set"){const c=await o.listSubscriptionsByReference(e.referenceId),s=await o.sumUsage(e.referenceId,e.featureId,I(c));i=t-s}if(i===0)return{recorded:!1,reportedToProvider:!1};if(!await o.recordUsage({createdAt:Date.now(),featureId:e.featureId,idempotencyKey:n,provider:r.identifier,quantity:i,referenceId:e.referenceId,reportedToProvider:!1}))return{recorded:!1,reportedToProvider:!1};if(i<=0||!r.capabilities.usageMetering||!r.reportUsage)return{recorded:!0,reportedToProvider:!1};try{const c=await o.getCustomerByReference(r.identifier,e.referenceId);return await r.reportUsage({customerId:c?.id,featureId:e.featureId,idempotencyKey:n,quantity:i,referenceId:e.referenceId}),await o.markUsageReported(r.identifier,n),{recorded:!0,reportedToProvider:!0}}catch{return O(a.observability,{featureId:e.featureId,provider:r.identifier,referenceId:e.referenceId,type:"usage.report_failed"}),{recorded:!0,reportedToProvider:!1}}}}};export{q as createPayment};
@@ -0,0 +1 @@
1
+ const f=new Set(["active","trialing"]),m=s=>{let t=0;for(const e of s)f.has(e.state)&&e.currentPeriodStart!==void 0&&(t=Math.max(t,e.currentPeriodStart));return t},p=s=>{const t=new Set;for(const e of Object.values(s.plans)){for(const o of e.features??[])t.add(o);for(const o of Object.keys(e.limits??{}))t.add(o)}return[...t].toSorted((e,o)=>e.localeCompare(o))},h=(s,t)=>s.some(e=>e.priceId===t&&f.has(e.state)),u=(s,t)=>{const e=new Set(t.filter(r=>f.has(r.state)).map(r=>r.priceId)),o=[],n=new Set,c=new Map;for(const[r,i]of Object.entries(s.plans))if(i.priceIds.some(a=>e.has(a))){o.push(r);for(const a of i.features??[])n.add(a);for(const[a,l]of Object.entries(i.limits??{})){const d=c.get(a);c.set(a,d===void 0?l:Math.max(d,l))}}return{features:n,has:r=>n.has(r),limit:r=>c.get(r),plans:o}},S=async(s,t,e)=>u(t,await s.listSubscriptionsByReference(e));export{S as entitlementsForReference,p as featureNames,h as hasActivePrice,u as resolveEntitlements,m as usagePeriodStart};
@@ -0,0 +1 @@
1
+ const r=new TextEncoder,a=async e=>[...new Uint8Array(await crypto.subtle.digest("SHA-256",r.encode(e)))].map(t=>t.toString(16).padStart(2,"0")).join(""),o=(e,...t)=>[e,...t.map(String)].join(":"),c=async(e,t,...n)=>{const i=await a(JSON.stringify([t,...n.map(String)]));return`${e}:${t}:${i}`};export{c as derivedIdempotencyKey,o as idempotencyKey};
@@ -0,0 +1 @@
1
+ const s=e=>typeof e=="object"&&e!==null?e:{},t=(e,r)=>typeof e[r]=="string"?e[r]:void 0,d=(e,r)=>typeof e[r]=="number"?e[r]:void 0,f=(e,r)=>typeof e[r]=="boolean"?e[r]:void 0,n=(e,...r)=>{for(const a of r){const o=t(e,a);if(o!==void 0)return o}},i=(e,...r)=>{for(const a of r){const o=d(e,a);if(o!==void 0)return o}},c=e=>t(s(e.metadata),"referenceId"),p=e=>{const r=typeof e=="string"?Date.parse(e):Number.NaN;return Number.isNaN(r)?void 0:r};export{s as a,d as b,n as c,i as d,f as e,c as f,p,t as r};
@@ -0,0 +1 @@
1
+ import{createPayment as i}from"./createPayment-CW_iR6Hg.mjs";import{createDatabasePaymentStore as n}from"./createDatabasePaymentStore-IG8Cb3_L.mjs";const s=e=>({delete:async t=>e.delete(t),findFirst:async(t,a)=>await e.findFirst(t,{where:a}),findMany:async(t,a)=>(await e.findMany(t,{where:a})).page,insert:async(t,a)=>e.insert(t,a),patch:async(t,a)=>e.patch(t,a)}),d=(e,t)=>{const a=e.auth?.userId??void 0;return i({adapter:t.adapter,authorize:t.authorize??(r=>r.trim()!==""&&a!==void 0&&r===a),entitlements:t.entitlements,observability:t.observability,store:n(s(e.db))})};export{s as lunoraDatabaseToPaymentDatabase,d as paymentsFromContext};
@@ -0,0 +1 @@
1
+ import{LunoraPaymentError as t}from"./LunoraPaymentError-DWQ-v_NC.mjs";const n=o=>r=>{throw new t("PROVIDER_ERROR",`${o} does not support ${r}`)};export{n as e};
@@ -0,0 +1 @@
1
+ const c=(t,r)=>{if(t)try{t(r)}catch{}};export{c as n};
@@ -0,0 +1 @@
1
+ import{defineTable as r}from"@lunora/server";import{v as e}from"@lunora/values";const n=r({createdAt:e.number(),email:e.optional(e.string()),provider:e.string(),providerCustomerId:e.string(),referenceId:e.string()}).index("by_provider_customer",["provider","providerCustomerId"],{unique:!0}).index("by_reference",["referenceId"]),i=r({cancelAtPeriodEnd:e.boolean(),createdAt:e.number(),currentPeriodEnd:e.optional(e.number()),currentPeriodStart:e.optional(e.number()),priceId:e.string(),provider:e.string(),providerSubscriptionId:e.string(),quantity:e.number(),referenceId:e.string(),state:e.string(),updatedAt:e.number()}).index("by_provider_subscription",["provider","providerSubscriptionId"],{unique:!0}).index("by_reference",["referenceId"]),t=r({amountMinor:e.bigint(),capturedMinor:e.bigint(),createdAt:e.number(),currency:e.string(),provider:e.string(),providerSessionId:e.string(),referenceId:e.string(),refundedMinor:e.bigint(),state:e.string(),updatedAt:e.number()}).index("by_provider_session",["provider","providerSessionId"],{unique:!0}).index("by_reference",["referenceId"]),d=r({processedAt:e.number(),provider:e.string(),providerEventId:e.string(),type:e.string()}).index("by_provider_event",["provider","providerEventId"],{unique:!0}),o=r({createdAt:e.number(),featureId:e.string(),idempotencyKey:e.string(),provider:e.string(),quantity:e.number(),referenceId:e.string(),reportedToProvider:e.boolean()}).index("by_idempotency",["provider","idempotencyKey"],{unique:!0}).index("by_reference_feature",["referenceId","featureId"]),p={customers:n,events:d,paymentSessions:t,subscriptions:i,usageEvents:o};export{p as default};
@@ -0,0 +1 @@
1
+ import{compareMoney as l}from"./addMoney-BiiMn67T.mjs";import{n as c}from"./observability-C9tz8mj_.mjs";const p=(t,e)=>t.currency===e.currency&&l(t,e)===0,m=(t,e)=>t?.state!==e.state||t.cancelAtPeriodEnd!==e.cancelAtPeriodEnd||t.currentPeriodEnd!==e.currentPeriodEnd||t.priceId!==e.priceId||t.quantity!==e.quantity,y=(t,e)=>t?.state!==e.state||!p(t.capturedAmount,e.capturedAmount)||!p(t.refundedAmount,e.refundedAmount),A=new Set(["partially_refunded","refunded"]),S=(t,e)=>{if(!t)return e;const r=t.refundedAmount.currency===e.refundedAmount.currency&&l(t.refundedAmount,e.refundedAmount)>0?t.refundedAmount:e.refundedAmount,a=e.state==="captured"&&A.has(t.state)?t.state:e.state,n=e.referenceId===""?t.referenceId:e.referenceId;return{...e,referenceId:n,refundedAmount:r,state:a}},b=async(t,e,r,a)=>{const n=await t.getSubscriptionStatus(r),d=await e.getSubscription(t.identifier,r);return m(d,n)?(await e.upsertSubscription({...n,createdAt:d?.createdAt??n.createdAt}),c(a,{id:r,kind:"subscription",provider:t.identifier,type:"reconcile.drift"}),!0):!1},P=async(t,e,r,a)=>{const n=await t.getPaymentStatus(r),d=await e.getPaymentSession(t.identifier,r),i=S(d,n);return y(d,i)?(await e.upsertPaymentSession({...i,createdAt:d?.createdAt??i.createdAt}),c(a,{id:r,kind:"payment",provider:t.identifier,type:"reconcile.drift"}),!0):!1},f=async(t,e,r,a,n)=>{const d=await Promise.allSettled(t.map(u=>r(u)));let i=0,s=0;for(const[u,o]of d.entries())o.status==="fulfilled"?o.value&&(i+=1):(s+=1,c(n,{error:o.reason,id:t[u]??"",kind:e,provider:a.identifier,type:"reconcile.error"}));return{failed:s,updated:i}},v=async t=>{const{adapter:e,observability:r,store:a}=t,n=t.subscriptionIds??[],d=t.paymentSessionIds??[],i=await f(n,"subscription",u=>b(e,a,u,r),e,r),s=await f(d,"payment",u=>P(e,a,u,r),e,r);return c(r,{failedPayments:s.failed,failedSubscriptions:i.failed,provider:e.identifier,type:"reconcile.completed",updatedPayments:s.updated,updatedSubscriptions:i.updated}),{checkedPayments:d.length,checkedSubscriptions:n.length,failedPayments:s.failed,failedSubscriptions:i.failed,updatedPayments:s.updated,updatedSubscriptions:i.updated}};export{v as reconcile};
@@ -0,0 +1 @@
1
+ const s=i=>i==="canceled"?"subscription.canceled":i==="past_due"?"subscription.past_due":i==="paused"?"subscription.paused":i==="active"||i==="trialing"?"subscription.active":"subscription.updated";export{s as i};
@@ -1,95 +1 @@
1
- import { a as asRecord, r as readString, b as readNumber } from '../packem_shared/json-D0Cb5aMv.mjs';
2
-
3
- const toFeatureId = (featureId) => typeof featureId === "string" ? featureId : [...featureId];
4
- const entityFrom = (record) => {
5
- return {
6
- featureId: readString(record, "featureId") ?? readString(record, "feature_id"),
7
- id: readString(record, "id") ?? "",
8
- name: readString(record, "name"),
9
- raw: record
10
- };
11
- };
12
- const pointsFrom = (result) => {
13
- const list = result.list ?? result.data ?? result.events;
14
- const rows = Array.isArray(list) ? list : [];
15
- return rows.map((entry) => {
16
- const record = asRecord(entry);
17
- return { count: readNumber(record, "count") ?? readNumber(record, "value") ?? 0, period: readString(record, "period"), raw: record };
18
- });
19
- };
20
- const createAutumnFeatures = (options) => {
21
- const client = options.client;
22
- return {
23
- /**
24
- * Native Autumn checkout — supports opting out of a plan's free trial, prepaid feature
25
- * quantities, entity scoping, and reward codes. Returns the hosted checkout URL (empty when
26
- * Autumn applied the change directly without a payment step).
27
- */
28
- checkout: async (referenceId, input) => {
29
- const result = asRecord(
30
- await client.billing.attach({
31
- customerId: referenceId,
32
- // Autumn applies the plan's configured trial by default; `null` opts this checkout out.
33
- // eslint-disable-next-line unicorn/no-null -- the SDK's customize.freeTrial requires null (not undefined) to disable
34
- ...input.freeTrial === false ? { customize: { freeTrial: null } } : {},
35
- discounts: input.reward ? [{ rewardId: input.reward }] : void 0,
36
- entityId: input.entityId,
37
- featureQuantities: input.options?.map((option) => {
38
- return { featureId: option.featureId, quantity: option.quantity };
39
- }),
40
- planId: input.planId,
41
- successUrl: input.successUrl
42
- })
43
- );
44
- const url = readString(result, "paymentUrl") ?? readString(result, "checkoutUrl") ?? readString(result, "url") ?? "";
45
- return { raw: result, url };
46
- },
47
- entities: {
48
- /** Create a per-seat / per-workspace sub-customer under a reference. */
49
- create: async (referenceId, input) => {
50
- const created = await client.entities.create({ customerId: referenceId, entityId: input.id, featureId: input.featureId, name: input.name });
51
- return entityFrom(asRecord(created));
52
- },
53
- /** Remove an entity — frees its seat/allowance. */
54
- delete: async (referenceId, entityId) => {
55
- await client.entities.delete({ customerId: referenceId, entityId });
56
- },
57
- /** Fetch one entity and its balances. */
58
- get: async (referenceId, entityId) => {
59
- const entity = await client.entities.get({ customerId: referenceId, entityId });
60
- return entityFrom(asRecord(entity));
61
- }
62
- },
63
- events: {
64
- /** Bucketed usage aggregation for a reference's feature(s) over a range. */
65
- aggregate: async (referenceId, input) => {
66
- const result = await client.events.aggregate({ customerId: referenceId, featureId: toFeatureId(input.featureId), range: input.range });
67
- return pointsFrom(asRecord(result));
68
- },
69
- /** Raw usage events for a reference's feature(s). */
70
- list: async (referenceId, input) => {
71
- const result = await client.events.list({ customerId: referenceId, featureId: toFeatureId(input.featureId) });
72
- return pointsFrom(asRecord(result));
73
- }
74
- },
75
- plans: {
76
- /** The configured plan catalog. */
77
- list: async () => {
78
- const result = asRecord(await client.plans.list());
79
- const list = result.list ?? result.data ?? result.plans;
80
- return Array.isArray(list) ? list.map((entry) => asRecord(entry)) : [];
81
- }
82
- },
83
- referrals: {
84
- /** Mint (or fetch) a referral code for a reference in a referral program. */
85
- createCode: async (referenceId, programId) => {
86
- const result = asRecord(await client.referrals.createCode({ customerId: referenceId, programId }));
87
- return { code: readString(result, "code") ?? "", raw: result };
88
- },
89
- /** Redeem a referral code on behalf of a reference. */
90
- redeemCode: async (referenceId, code) => asRecord(await client.referrals.redeemCode({ code, customerId: referenceId }))
91
- }
92
- };
93
- };
94
-
95
- export { createAutumnFeatures };
1
+ import{a as s,r as d,b as i}from"../packem_shared/json-D85Nbd92.mjs";const u=r=>typeof r=="string"?r:[...r],o=r=>({featureId:d(r,"featureId")??d(r,"feature_id"),id:d(r,"id")??"",name:d(r,"name"),raw:r}),l=r=>{const n=r.list??r.data??r.events;return(Array.isArray(n)?n:[]).map(t=>{const e=s(t);return{count:i(e,"count")??i(e,"value")??0,period:d(e,"period"),raw:e}})},y=r=>{const n=r.client;return{checkout:async(t,e)=>{const a=s(await n.billing.attach({customerId:t,...e.freeTrial===!1?{customize:{freeTrial:null}}:{},discounts:e.reward?[{rewardId:e.reward}]:void 0,entityId:e.entityId,featureQuantities:e.options?.map(c=>({featureId:c.featureId,quantity:c.quantity})),planId:e.planId,successUrl:e.successUrl})),I=d(a,"paymentUrl")??d(a,"checkoutUrl")??d(a,"url")??"";return{raw:a,url:I}},entities:{create:async(t,e)=>{const a=await n.entities.create({customerId:t,entityId:e.id,featureId:e.featureId,name:e.name});return o(s(a))},delete:async(t,e)=>{await n.entities.delete({customerId:t,entityId:e})},get:async(t,e)=>{const a=await n.entities.get({customerId:t,entityId:e});return o(s(a))}},events:{aggregate:async(t,e)=>{const a=await n.events.aggregate({customerId:t,featureId:u(e.featureId),range:e.range});return l(s(a))},list:async(t,e)=>{const a=await n.events.list({customerId:t,featureId:u(e.featureId)});return l(s(a))}},plans:{list:async()=>{const t=s(await n.plans.list()),e=t.list??t.data??t.plans;return Array.isArray(e)?e.map(a=>s(a)):[]}},referrals:{createCode:async(t,e)=>{const a=s(await n.referrals.createCode({customerId:t,programId:e}));return{code:d(a,"code")??"",raw:a}},redeemCode:async(t,e)=>s(await n.referrals.redeemCode({code:e,customerId:t}))}}};export{y as createAutumnFeatures};
@@ -1,275 +1 @@
1
- import { LunoraPaymentError } from '../packem_shared/LunoraPaymentError-BeQlhkZj.mjs';
2
- import { a as asRecord, c as readAny, d as readAnyNumber, e as readBoolean, r as readString } from '../packem_shared/json-D0Cb5aMv.mjs';
3
- import { money } from '../packem_shared/addMoney-jSh_7TfF.mjs';
4
- import { verifyStandardWebhook } from '../packem_shared/constantTimeEqual-BQjoW85H.mjs';
5
- import { m as makeNotSupported } from '../packem_shared/not-supported-C0onRyia.mjs';
6
- import { s as stateToEventType } from '../packem_shared/subscription-event-CwEsWRCK.mjs';
7
-
8
- const SUBSCRIPTION_STATE_BY_AUTUMN_STATUS = {
9
- active: "active",
10
- canceled: "canceled",
11
- expired: "canceled",
12
- past_due: "past_due",
13
- // A scheduled product has not started yet — non-entitling until it activates.
14
- scheduled: "paused",
15
- trialing: "trialing"
16
- };
17
- const SUBSCRIPTION_STATE_BY_AUTUMN_ACTION = {
18
- activated: "active",
19
- canceled: "canceled",
20
- cancelled: "canceled",
21
- expired: "canceled",
22
- scheduled: "paused"
23
- };
24
- const SUBSCRIPTION_ID_SEPARATOR = "::";
25
- const notSupported = makeNotSupported("autumn");
26
- const autumnSubscriptionId = (customerId, productId) => `${customerId}${SUBSCRIPTION_ID_SEPARATOR}${productId}`;
27
- const parseAutumnSubscriptionId = (subscriptionId) => {
28
- const index = subscriptionId.lastIndexOf(SUBSCRIPTION_ID_SEPARATOR);
29
- if (index === -1) {
30
- throw new LunoraPaymentError("PROVIDER_ERROR", `malformed autumn subscription id "${subscriptionId}" (expected "<customerId>::<productId>")`);
31
- }
32
- return { customerId: subscriptionId.slice(0, index), productId: subscriptionId.slice(index + SUBSCRIPTION_ID_SEPARATOR.length) };
33
- };
34
- const isCanceling = (product) => readAnyNumber(product, "canceled_at", "canceledAt") !== void 0 || readAny(product, "status") === "scheduled";
35
- const productToSubscription = (customerId, product) => {
36
- const now = Date.now();
37
- const productId = readAny(product, "id", "product_id", "productId", "plan_id", "planId") ?? "";
38
- const status = readAny(product, "status") ?? "active";
39
- const pastDue = readBoolean(product, "past_due") ?? readBoolean(product, "pastDue") ?? false;
40
- return {
41
- cancelAtPeriodEnd: isCanceling(product),
42
- createdAt: now,
43
- currentPeriodEnd: readAnyNumber(product, "current_period_end", "currentPeriodEnd") ?? void 0,
44
- currentPeriodStart: readAnyNumber(product, "current_period_start", "currentPeriodStart") ?? void 0,
45
- id: autumnSubscriptionId(customerId, productId),
46
- priceId: productId,
47
- provider: "autumn",
48
- quantity: readAnyNumber(product, "quantity") ?? 1,
49
- referenceId: customerId,
50
- // Fail closed: a past-due flag, or an unrecognized Autumn status, is non-entitling `past_due`.
51
- state: pastDue ? "past_due" : SUBSCRIPTION_STATE_BY_AUTUMN_STATUS[status] ?? "past_due",
52
- updatedAt: now
53
- };
54
- };
55
- const asRecordList = (value) => Array.isArray(value) ? value.map((entry) => asRecord(entry)) : [];
56
- const findProduct = (customer, productId) => {
57
- const rows = [...asRecordList(customer.products), ...asRecordList(customer.subscriptions)];
58
- return rows.find((entry) => (readAny(entry, "id", "product_id", "productId", "plan_id", "planId") ?? "") === productId);
59
- };
60
- const balanceFields = (balance) => {
61
- return {
62
- balance: readAnyNumber(balance, "remaining", "balance"),
63
- limit: readAnyNumber(balance, "granted", "included_usage", "limit"),
64
- unlimited: readBoolean(balance, "unlimited") ?? false,
65
- used: readAnyNumber(balance, "usage", "used")
66
- };
67
- };
68
- const constructedSubscription = (customerId, productId, state, cancelAtPeriodEnd) => {
69
- const now = Date.now();
70
- return {
71
- cancelAtPeriodEnd,
72
- createdAt: now,
73
- id: autumnSubscriptionId(customerId, productId),
74
- priceId: productId,
75
- provider: "autumn",
76
- quantity: 1,
77
- referenceId: customerId,
78
- state,
79
- updatedAt: now
80
- };
81
- };
82
- const readSubscription = async (client, customerId, productId) => {
83
- const customer = asRecord(await client.customers.get({ customerId }));
84
- const product = findProduct(customer, productId);
85
- return product ? productToSubscription(customerId, product) : constructedSubscription(customerId, productId, "canceled", false);
86
- };
87
- const referenceFromEvent = (object) => readAny(object, "customer_id", "customerId");
88
- const mapBillingUpdated = (eventId, object) => {
89
- const base = { eventId, provider: "autumn", raw: { object, type: "billing.updated" } };
90
- const customerId = referenceFromEvent(object);
91
- const change = asRecordList(object.plan_changes)[0] ?? object;
92
- const subscription = change.subscription ? asRecord(change.subscription) : change;
93
- const planId = readAny(subscription, "plan_id", "planId", "product_id", "productId", "id");
94
- const status = readAny(subscription, "status");
95
- const action = readAny(change, "action");
96
- const pastDue = readBoolean(subscription, "past_due") ?? readBoolean(subscription, "pastDue") ?? false;
97
- const fromStatus = status === void 0 ? void 0 : SUBSCRIPTION_STATE_BY_AUTUMN_STATUS[status];
98
- const fromAction = action === void 0 ? void 0 : SUBSCRIPTION_STATE_BY_AUTUMN_ACTION[action];
99
- const state = pastDue ? "past_due" : fromStatus ?? fromAction;
100
- return {
101
- ...base,
102
- cancelAtPeriodEnd: readBoolean(subscription, "cancel_at_period_end") ?? isCanceling(subscription),
103
- currentPeriodEnd: readAnyNumber(subscription, "current_period_end", "currentPeriodEnd"),
104
- currentPeriodStart: readAnyNumber(subscription, "current_period_start", "currentPeriodStart"),
105
- customerId,
106
- priceId: planId,
107
- referenceId: customerId,
108
- subscriptionId: customerId === void 0 || planId === void 0 ? void 0 : autumnSubscriptionId(customerId, planId),
109
- type: stateToEventType(state)
110
- };
111
- };
112
- const mapEvent = (eventId, eventType, object) => {
113
- const base = { eventId, provider: "autumn", raw: { object, type: eventType } };
114
- const currency = readAny(object, "currency") ?? "usd";
115
- switch (eventType) {
116
- // Auto-topup settles a real invoice — surface it as a captured payment.
117
- case "billing.auto_topup_succeeded": {
118
- const invoice = asRecord(object.invoice);
119
- const amount = readAnyNumber(invoice, "total", "amount") ?? readAnyNumber(object, "total", "amount");
120
- const invoiceCurrency = readAny(invoice, "currency") ?? currency;
121
- return {
122
- ...base,
123
- amount: amount === void 0 ? void 0 : money(BigInt(Math.round(amount)), invoiceCurrency),
124
- customerId: referenceFromEvent(object),
125
- referenceId: referenceFromEvent(object),
126
- sessionId: readAny(invoice, "id", "stripe_id", "invoice_id") ?? readAny(object, "id"),
127
- type: "payment.captured"
128
- };
129
- }
130
- case "billing.updated": {
131
- return mapBillingUpdated(eventId, object);
132
- }
133
- // Product lifecycle — the entitling truth. `data` is the product row (or wraps it).
134
- case "customer.product.added":
135
- case "customer.product.canceled":
136
- case "customer.product.expired":
137
- case "customer.product.updated":
138
- case "product.attached": {
139
- const product = object.product ? asRecord(object.product) : object;
140
- const status = eventType === "customer.product.canceled" || eventType === "customer.product.expired" ? "canceled" : readAny(product, "status");
141
- const customerId = referenceFromEvent(object) ?? referenceFromEvent(product);
142
- return {
143
- ...base,
144
- cancelAtPeriodEnd: readBoolean(product, "cancel_at_period_end") ?? isCanceling(product),
145
- currentPeriodEnd: readAnyNumber(product, "current_period_end", "currentPeriodEnd"),
146
- currentPeriodStart: readAnyNumber(product, "current_period_start", "currentPeriodStart"),
147
- customerId,
148
- priceId: readAny(product, "id", "product_id", "productId"),
149
- referenceId: customerId,
150
- subscriptionId: customerId === void 0 ? void 0 : autumnSubscriptionId(customerId, readAny(product, "id", "product_id", "productId") ?? ""),
151
- type: stateToEventType(SUBSCRIPTION_STATE_BY_AUTUMN_STATUS[status ?? ""])
152
- };
153
- }
154
- // Money movement — Autumn surfaces settled invoices/payments.
155
- case "invoice.paid":
156
- case "payment.succeeded": {
157
- const amount = readAnyNumber(object, "total", "amount", "amount_paid");
158
- return {
159
- ...base,
160
- // Autumn amounts are assumed integer minor units, but the event catalog is unverified —
161
- // round defensively so a provider-sent decimal can't throw a RangeError out of
162
- // `parseWebhook` (which would 400 the endpoint and wedge Autumn into infinite retries).
163
- amount: amount === void 0 ? void 0 : money(BigInt(Math.round(amount)), currency),
164
- customerId: referenceFromEvent(object),
165
- referenceId: referenceFromEvent(object),
166
- sessionId: readAny(object, "id", "invoice_id", "stripe_id"),
167
- type: "payment.captured"
168
- };
169
- }
170
- default: {
171
- return { ...base, type: "unhandled" };
172
- }
173
- }
174
- };
175
- const checkoutUrlFrom = (result) => readAny(result, "checkout_url", "checkoutUrl", "payment_url", "paymentUrl", "url") ?? "";
176
- const createAutumnAdapter = (options) => {
177
- const { webhookSecret } = options;
178
- const client = options.client;
179
- return {
180
- // Autumn abstracts Stripe money movement; there is no payment intent to cancel/capture/refund.
181
- cancelPayment: () => notSupported("manual payment cancellation"),
182
- cancelSubscription: async (subscriptionId, cancelOptions) => {
183
- const { customerId, productId } = parseAutumnSubscriptionId(subscriptionId);
184
- await client.billing.update({
185
- cancelAction: cancelOptions?.atPeriodEnd ? "cancel_end_of_cycle" : "cancel_immediately",
186
- customerId,
187
- planId: productId
188
- });
189
- if (cancelOptions?.atPeriodEnd) {
190
- return { ...await readSubscription(client, customerId, productId), cancelAtPeriodEnd: true };
191
- }
192
- return constructedSubscription(customerId, productId, "canceled", false);
193
- },
194
- capabilities: { merchantOfRecord: false, portal: true, usageMetering: true },
195
- capturePayment: (_input) => notSupported("manual capture"),
196
- checkEntitlement: async (input) => {
197
- if (input.featureId === void 0) {
198
- const customer = asRecord(await client.customers.get({ customerId: input.referenceId }));
199
- const product = findProduct(customer, input.priceId ?? "");
200
- const state = product ? productToSubscription(input.referenceId, product).state : void 0;
201
- return { allowed: state === "active" || state === "trialing", unlimited: false };
202
- }
203
- const result = asRecord(await client.check({ customerId: input.referenceId, featureId: input.featureId, requiredBalance: input.quantity ?? 1 }));
204
- const allowed = readBoolean(result, "allowed") ?? false;
205
- const rawBalance = result.balance;
206
- const balance = typeof rawBalance === "object" && rawBalance !== null ? asRecord(rawBalance) : result;
207
- return { allowed, ...balanceFields(balance) };
208
- },
209
- createCheckout: async (input) => {
210
- const result = asRecord(await client.billing.attach({ customerId: input.referenceId, planId: input.priceId }));
211
- return { id: autumnSubscriptionId(input.referenceId, input.priceId), provider: "autumn", url: checkoutUrlFrom(result) };
212
- },
213
- createPortalSession: async (input) => {
214
- const result = asRecord(await client.billing.openCustomerPortal({ customerId: input.customerId }));
215
- return { url: readAny(result, "url") ?? "" };
216
- },
217
- getBalances: async (referenceId) => {
218
- const customer = asRecord(await client.customers.get({ customerId: referenceId }));
219
- const balances = asRecord(customer.balances ?? customer.features);
220
- return Object.entries(balances).map(([key, raw]) => {
221
- const balance = asRecord(raw);
222
- const fields = balanceFields(balance);
223
- const allowed = fields.unlimited || (fields.balance ?? 0) > 0;
224
- return { allowed, featureId: readAny(balance, "featureId", "feature_id") ?? key, ...fields };
225
- });
226
- },
227
- getOrCreateCustomer: async (ref) => {
228
- const customer = asRecord(await client.customers.getOrCreate({ customerId: ref.referenceId, email: ref.email, name: ref.metadata?.name }));
229
- return {
230
- createdAt: Date.now(),
231
- email: readAny(customer, "email") ?? ref.email,
232
- id: readAny(customer, "id") ?? ref.referenceId,
233
- provider: "autumn",
234
- referenceId: ref.referenceId
235
- };
236
- },
237
- // Autumn is entitlement-centric; it exposes no standalone one-time payment-session lookup.
238
- getPaymentStatus: () => notSupported("payment-session reconciliation"),
239
- getSubscriptionStatus: async (subscriptionId) => {
240
- const { customerId, productId } = parseAutumnSubscriptionId(subscriptionId);
241
- return readSubscription(client, customerId, productId);
242
- },
243
- identifier: "autumn",
244
- parseWebhook: async ({ headers, payload }) => {
245
- const webhookId = headers.get("svix-id") ?? headers.get("webhook-id") ?? "";
246
- await verifyStandardWebhook({
247
- payload,
248
- secret: webhookSecret,
249
- toleranceSeconds: options.webhookToleranceSeconds,
250
- webhookId,
251
- webhookSignature: headers.get("svix-signature") ?? headers.get("webhook-signature") ?? "",
252
- webhookTimestamp: headers.get("svix-timestamp") ?? headers.get("webhook-timestamp") ?? ""
253
- });
254
- const event = asRecord(JSON.parse(payload));
255
- return mapEvent(webhookId, readString(event, "type") ?? "", asRecord(event.data));
256
- },
257
- refundPayment: () => notSupported("refunds"),
258
- reportUsage: async (input) => {
259
- await client.track({ customerId: input.referenceId, featureId: input.featureId, value: input.quantity });
260
- },
261
- resumeSubscription: async (subscriptionId) => {
262
- const { customerId, productId } = parseAutumnSubscriptionId(subscriptionId);
263
- await client.billing.update({ cancelAction: "uncancel", customerId, planId: productId });
264
- return { ...await readSubscription(client, customerId, productId), cancelAtPeriodEnd: false };
265
- },
266
- updateSubscription: async (subscriptionId, patch) => {
267
- const { customerId, productId } = parseAutumnSubscriptionId(subscriptionId);
268
- const targetProduct = patch.priceId ?? productId;
269
- await client.billing.attach({ customerId, planId: targetProduct });
270
- return readSubscription(client, customerId, targetProduct);
271
- }
272
- };
273
- };
274
-
275
- export { createAutumnAdapter };
1
+ import{LunoraPaymentError as C}from"../packem_shared/LunoraPaymentError-DWQ-v_NC.mjs";import{a as u,c as o,d as s,e as l,r as D}from"../packem_shared/json-D85Nbd92.mjs";import{money as A}from"../packem_shared/addMoney-BiiMn67T.mjs";import{verifyStandardWebhook as B}from"../packem_shared/constantTimeEqual-BpVBvmN-.mjs";import{e as $}from"../packem_shared/not-supported-DwGy4SQL.mjs";import{i as E}from"../packem_shared/subscription-event-C-71ye9J.mjs";const h={active:"active",canceled:"canceled",expired:"canceled",past_due:"past_due",scheduled:"paused",trialing:"trialing"},j={activated:"active",canceled:"canceled",cancelled:"canceled",expired:"canceled",scheduled:"paused"},b="::",y=$("autumn"),f=(a,c)=>`${a}${b}${c}`,v=a=>{const c=a.lastIndexOf(b);if(c===-1)throw new C("PROVIDER_ERROR",`malformed autumn subscription id "${a}" (expected "<customerId>::<productId>")`);return{customerId:a.slice(0,c),productId:a.slice(c+b.length)}},P=a=>s(a,"canceled_at","canceledAt")!==void 0||o(a,"status")==="scheduled",k=(a,c)=>{const r=Date.now(),e=o(c,"id","product_id","productId","plan_id","planId")??"",d=o(c,"status")??"active",t=l(c,"past_due")??l(c,"pastDue")??!1;return{cancelAtPeriodEnd:P(c),createdAt:r,currentPeriodEnd:s(c,"current_period_end","currentPeriodEnd")??void 0,currentPeriodStart:s(c,"current_period_start","currentPeriodStart")??void 0,id:f(a,e),priceId:e,provider:"autumn",quantity:s(c,"quantity")??1,referenceId:a,state:t?"past_due":h[d]??"past_due",updatedAt:r}},w=a=>Array.isArray(a)?a.map(c=>u(c)):[],x=(a,c)=>[...w(a.products),...w(a.subscriptions)].find(r=>(o(r,"id","product_id","productId","plan_id","planId")??"")===c),S=a=>({balance:s(a,"remaining","balance"),limit:s(a,"granted","included_usage","limit"),unlimited:l(a,"unlimited")??!1,used:s(a,"usage","used")}),O=(a,c,r,e)=>{const d=Date.now();return{cancelAtPeriodEnd:e,createdAt:d,id:f(a,c),priceId:c,provider:"autumn",quantity:1,referenceId:a,state:r,updatedAt:d}},g=async(a,c,r)=>{const e=u(await a.customers.get({customerId:c})),d=x(e,r);return d?k(c,d):O(c,r,"canceled",!1)},I=a=>o(a,"customer_id","customerId"),M=(a,c)=>{const r={eventId:a,provider:"autumn",raw:{object:c,type:"billing.updated"}},e=I(c),d=w(c.plan_changes)[0]??c,t=d.subscription?u(d.subscription):d,n=o(t,"plan_id","planId","product_id","productId","id"),i=o(t,"status"),m=o(d,"action"),p=l(t,"past_due")??l(t,"pastDue")??!1,_=i===void 0?void 0:h[i],q=m===void 0?void 0:j[m],R=p?"past_due":_??q;return{...r,cancelAtPeriodEnd:l(t,"cancel_at_period_end")??P(t),currentPeriodEnd:s(t,"current_period_end","currentPeriodEnd"),currentPeriodStart:s(t,"current_period_start","currentPeriodStart"),customerId:e,priceId:n,referenceId:e,subscriptionId:e===void 0||n===void 0?void 0:f(e,n),type:E(R)}},N=(a,c,r)=>{const e={eventId:a,provider:"autumn",raw:{object:r,type:c}},d=o(r,"currency")??"usd";switch(c){case"billing.auto_topup_succeeded":{const t=u(r.invoice),n=s(t,"total","amount")??s(r,"total","amount"),i=o(t,"currency")??d;return{...e,amount:n===void 0?void 0:A(BigInt(Math.round(n)),i),customerId:I(r),referenceId:I(r),sessionId:o(t,"id","stripe_id","invoice_id")??o(r,"id"),type:"payment.captured"}}case"billing.updated":return M(a,r);case"customer.product.added":case"customer.product.canceled":case"customer.product.expired":case"customer.product.updated":case"product.attached":{const t=r.product?u(r.product):r,n=c==="customer.product.canceled"||c==="customer.product.expired"?"canceled":o(t,"status"),i=I(r)??I(t);return{...e,cancelAtPeriodEnd:l(t,"cancel_at_period_end")??P(t),currentPeriodEnd:s(t,"current_period_end","currentPeriodEnd"),currentPeriodStart:s(t,"current_period_start","currentPeriodStart"),customerId:i,priceId:o(t,"id","product_id","productId"),referenceId:i,subscriptionId:i===void 0?void 0:f(i,o(t,"id","product_id","productId")??""),type:E(h[n??""])}}case"invoice.paid":case"payment.succeeded":{const t=s(r,"total","amount","amount_paid");return{...e,amount:t===void 0?void 0:A(BigInt(Math.round(t)),d),customerId:I(r),referenceId:I(r),sessionId:o(r,"id","invoice_id","stripe_id"),type:"payment.captured"}}default:return{...e,type:"unhandled"}}},T=a=>o(a,"checkout_url","checkoutUrl","payment_url","paymentUrl","url")??"",z=a=>{const{webhookSecret:c}=a,r=a.client;return{cancelPayment:()=>y("manual payment cancellation"),cancelSubscription:async(e,d)=>{const{customerId:t,productId:n}=v(e);return await r.billing.update({cancelAction:d?.atPeriodEnd?"cancel_end_of_cycle":"cancel_immediately",customerId:t,planId:n}),d?.atPeriodEnd?{...await g(r,t,n),cancelAtPeriodEnd:!0}:O(t,n,"canceled",!1)},capabilities:{merchantOfRecord:!1,portal:!0,usageMetering:!0},capturePayment:e=>y("manual capture"),checkEntitlement:async e=>{if(e.featureId===void 0){const m=u(await r.customers.get({customerId:e.referenceId})),p=x(m,e.priceId??""),_=p?k(e.referenceId,p).state:void 0;return{allowed:_==="active"||_==="trialing",unlimited:!1}}const d=u(await r.check({customerId:e.referenceId,featureId:e.featureId,requiredBalance:e.quantity??1})),t=l(d,"allowed")??!1,n=d.balance,i=typeof n=="object"&&n!==null?u(n):d;return{allowed:t,...S(i)}},createCheckout:async e=>{const d=u(await r.billing.attach({customerId:e.referenceId,planId:e.priceId}));return{id:f(e.referenceId,e.priceId),provider:"autumn",url:T(d)}},createPortalSession:async e=>{const d=u(await r.billing.openCustomerPortal({customerId:e.customerId}));return{url:o(d,"url")??""}},getBalances:async e=>{const d=u(await r.customers.get({customerId:e})),t=u(d.balances??d.features);return Object.entries(t).map(([n,i])=>{const m=u(i),p=S(m);return{allowed:p.unlimited||(p.balance??0)>0,featureId:o(m,"featureId","feature_id")??n,...p}})},getOrCreateCustomer:async e=>{const d=u(await r.customers.getOrCreate({customerId:e.referenceId,email:e.email,name:e.metadata?.name}));return{createdAt:Date.now(),email:o(d,"email")??e.email,id:o(d,"id")??e.referenceId,provider:"autumn",referenceId:e.referenceId}},getPaymentStatus:()=>y("payment-session reconciliation"),getSubscriptionStatus:async e=>{const{customerId:d,productId:t}=v(e);return g(r,d,t)},identifier:"autumn",parseWebhook:async({headers:e,payload:d})=>{const t=e.get("svix-id")??e.get("webhook-id")??"";await B({payload:d,secret:c,toleranceSeconds:a.webhookToleranceSeconds,webhookId:t,webhookSignature:e.get("svix-signature")??e.get("webhook-signature")??"",webhookTimestamp:e.get("svix-timestamp")??e.get("webhook-timestamp")??""});const n=u(JSON.parse(d));return N(t,D(n,"type")??"",u(n.data))},refundPayment:()=>y("refunds"),reportUsage:async e=>{await r.track({customerId:e.referenceId,featureId:e.featureId,value:e.quantity})},resumeSubscription:async e=>{const{customerId:d,productId:t}=v(e);return await r.billing.update({cancelAction:"uncancel",customerId:d,planId:t}),{...await g(r,d,t),cancelAtPeriodEnd:!1}},updateSubscription:async(e,d)=>{const{customerId:t,productId:n}=v(e),i=d.priceId??n;return await r.billing.attach({customerId:t,planId:i}),g(r,t,i)}}};export{z as createAutumnAdapter};