@lunora/payment 1.0.0-alpha.131 → 1.0.0-alpha.133
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.mjs +1 -1
- package/dist/packem_shared/applyWebhookAction-DyRG8lhp.mjs +1 -0
- package/dist/packem_shared/createDatabasePaymentStore-DG4P5AzL.mjs +1 -0
- package/dist/packem_shared/{createPayment-Dimez2zY.mjs → createPayment-BB3ri82t.mjs} +1 -1
- package/dist/packem_shared/{lunoraDatabaseToPaymentDatabase-CFPYSEu0.mjs → lunoraDatabaseToPaymentDatabase-DgCDEqqC.mjs} +1 -1
- package/package.json +2 -2
- package/dist/packem_shared/applyWebhookAction-CvLpZjKP.mjs +0 -1
- package/dist/packem_shared/createDatabasePaymentStore-BGcHX1uq.mjs +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createAdapterRegistry as r}from"./packem_shared/createAdapterRegistry-DyCsry1Q.mjs";import{lunoraDatabaseToPaymentDatabase as a,paymentsFromContext as n}from"./packem_shared/lunoraDatabaseToPaymentDatabase-
|
|
1
|
+
import{createAdapterRegistry as r}from"./packem_shared/createAdapterRegistry-DyCsry1Q.mjs";import{lunoraDatabaseToPaymentDatabase as a,paymentsFromContext as n}from"./packem_shared/lunoraDatabaseToPaymentDatabase-DgCDEqqC.mjs";import{createPayment as y,webhookResponse as i}from"./packem_shared/createPayment-BB3ri82t.mjs";import{createDatabasePaymentStore as s}from"./packem_shared/createDatabasePaymentStore-DG4P5AzL.mjs";import{entitlementsForReference as c,featureNames as x,hasActivePrice as S,resolveEntitlements as l,usagePeriodStart as M}from"./packem_shared/entitlementsForReference-PMmhAOWa.mjs";import{LunoraPaymentError as u}from"./packem_shared/LunoraPaymentError-BSxyhWgu.mjs";import{idempotencyKey as b}from"./packem_shared/idempotencyKey-qQJLcEQX.mjs";import{addMoney as A,allocateMoney as E,compareMoney as N,formatMoney as h,fromMoneyJSON as R,isZeroDecimalCurrency as v,isZeroMoney as C,money as D,subtractMoney as I,toMoneyJSON as _,zeroMoney as g}from"./packem_shared/addMoney-B4ufWAnD.mjs";import{reconcile as L}from"./packem_shared/reconcile-fbxX8j9k.mjs";import{default as F}from"./packem_shared/paymentTables-Ve_lJxAt.mjs";import{PAYMENT_TERMINAL_STATES as W,SUBSCRIPTION_TERMINAL_STATES as Z,canTransitionPayment as q,canTransitionSubscription as w,nextPaymentState as z,nextSubscriptionState as B}from"./packem_shared/PAYMENT_TERMINAL_STATES-C-ICZkun.mjs";import{MemoryPaymentStore as K}from"./packem_shared/MemoryPaymentStore-CZbrrEb1.mjs";import{default as Y}from"./packem_shared/applyWebhookAction-DyRG8lhp.mjs";import{constantTimeEqual as G,hmacSha256Hex as Q,verifyCreemSignature as V,verifyStandardWebhook as X}from"./packem_shared/constantTimeEqual-ggrwGvRa.mjs";export{u as LunoraPaymentError,K as MemoryPaymentStore,W as PAYMENT_TERMINAL_STATES,Z as SUBSCRIPTION_TERMINAL_STATES,A as addMoney,E as allocateMoney,Y as applyWebhookAction,q as canTransitionPayment,w as canTransitionSubscription,N as compareMoney,G as constantTimeEqual,r as createAdapterRegistry,s as createDatabasePaymentStore,y as createPayment,c as entitlementsForReference,x as featureNames,h as formatMoney,R as fromMoneyJSON,S as hasActivePrice,Q as hmacSha256Hex,b as idempotencyKey,v as isZeroDecimalCurrency,C as isZeroMoney,a as lunoraDatabaseToPaymentDatabase,D as money,z as nextPaymentState,B as nextSubscriptionState,F as paymentTables,n as paymentsFromContext,L as reconcile,l as resolveEntitlements,I as subtractMoney,_ as toMoneyJSON,M as usagePeriodStart,V as verifyCreemSignature,X as verifyStandardWebhook,i as webhookResponse,g as zeroMoney};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{LunoraPaymentError as A}from"./LunoraPaymentError-BSxyhWgu.mjs";import{localRefundKey as _,LOCAL_REFUND_CLAIM_TYPE as S}from"./idempotencyKey-qQJLcEQX.mjs";import{zeroMoney as l,compareMoney as y,addMoney as w}from"./addMoney-B4ufWAnD.mjs";import{n as c}from"./observability-BteZ2dJS.mjs";import{nextPaymentState as b,nextSubscriptionState as h}from"./PAYMENT_TERMINAL_STATES-C-ICZkun.mjs";const T={"payment.authorized":"authorize","payment.captured":"capture","payment.failed":"fail","payment.refunded":"refund"},R={"subscription.active":"active","subscription.canceled":"canceled","subscription.past_due":"past_due","subscription.paused":"paused"},k="orphan-retry:",O="marker.orphan_retry",Y={"subscription.active":"activate","subscription.canceled":"cancel","subscription.past_due":"mark_past_due","subscription.paused":"pause"},N=(t,e)=>y(t,e)>0?t:e,M=new Set(["authorized","initiated"]),I=async()=>{},q=async(t,e,r)=>{if(!r||!e.sessionId||!e.amount||e.amountKind==="absolute")return{action:e,release:I};const n=_(e.sessionId,e.refundId,e.amount),s=await t.markEventProcessed(e.provider,n,S),d={...e,amount:l(e.amount.currency)};return e.refundId===void 0?(await t.releaseEvent(e.provider,n),{action:s?e:d,release:I}):s?{action:e,release:async()=>{await t.releaseEvent(e.provider,n)}}:{action:d,release:I}},E=(t,e)=>{if(!e.amount||t.refundedAmount.currency!==e.amount.currency||t.capturedAmount.currency!==e.amount.currency)return;const r=e.amountKind==="absolute"?N(e.amount,t.refundedAmount):w(t.refundedAmount,e.amount);if(!(y(r,t.capturedAmount)>0))return r},C=(t,e)=>{if(!t)return"refund";const r=E(t,e);return r&&y(r,t.capturedAmount)<0?"partial_refund":"refund"},g=async(t,e,r)=>{if(!e.sessionId)return{applied:!1,reason:"unhandled"};const n=await t.getPaymentSession(e.provider,e.sessionId),s=n?.state??"initiated",d=Date.now(),u=e.amount?.currency??n?.amount.currency??"USD",i=r==="refund"?await q(t,e,n):void 0,f=i?.action??e,p=r==="refund"?C(n,f):r,m=b(s,p);if(!m){const a=r==="refund"&&M.has(s);return await i?.release(),{applied:!1,reason:a?"orphaned":"illegal_transition"}}const o=n??{amount:e.amount??l(u),capturedAmount:l(u),createdAt:d,id:e.sessionId,provider:e.provider,referenceId:e.referenceId??"",refundedAmount:l(u),state:s,updatedAt:d};let{capturedAmount:v,refundedAmount:P}=o;if(p==="capture"&&e.amount&&(v=e.amount),(p==="partial_refund"||p==="refund")&&f.amount){const a=E(o,f);if(!a)return await i?.release(),{applied:!1,reason:"invalid_refund_amount"};P=a}try{await t.upsertPaymentSession({...o,capturedAmount:v,referenceId:e.referenceId??o.referenceId,refundedAmount:P,state:m,updatedAt:d})}catch(a){throw await i?.release(),a}return{applied:!0,reason:"ok"}},B=(t,e,r)=>{if(e==="active"){if(t==="active")return"renew";if(t==="paused")return"resume"}return Y[r]},x=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,priceIds:e.priceIds??r.priceIds,quantity:e.quantity??r.quantity,updatedAt:n}),{applied:!0,reason:"ok"}):{applied:!1,reason:"orphaned"};const s=R[e.type];if(!s)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??"",priceIds:e.priceIds,provider:e.provider,quantity:e.quantity??1,referenceId:e.referenceId??"",state:s,updatedAt:n}),{applied:!0,reason:"ok"};const d=B(r.state,s,e.type),u=d?h(r.state,d):void 0;return u?(await t.upsertSubscription({...r,cancelAtPeriodEnd:e.cancelAtPeriodEnd??r.cancelAtPeriodEnd,currentPeriodEnd:e.currentPeriodEnd??r.currentPeriodEnd,currentPeriodStart:e.currentPeriodStart??r.currentPeriodStart,priceId:e.priceId??r.priceId,priceIds:e.priceIds??r.priceIds,quantity:e.quantity??r.quantity,state:u,updatedAt:n}),{applied:!0,reason:"ok"}):{applied:!1,reason:"illegal_transition"}},H=async(t,e,r)=>{if(e.type==="unhandled")return{applied:!1,reason:"unhandled"};if(!e.eventId?.trim())throw new A("WEBHOOK_EVENT_ID_MISSING",`webhook event id is missing or blank for provider "${e.provider}"`);if(!await t.markEventProcessed(e.provider,e.eventId,e.type))return c(r,{eventId:e.eventId,provider:e.provider,type:"webhook.duplicate"}),{applied:!1,reason:"duplicate"};const s=T[e.type];let d;try{d=s?await g(t,e,s):await x(t,e)}catch(u){throw await t.releaseEvent(e.provider,e.eventId),u}return d.reason==="orphaned"&&(await t.markEventProcessed(e.provider,`${k}${e.eventId}`,O)?await t.releaseEvent(e.provider,e.eventId):d={applied:!1,reason:"unhandled"}),c(r,{action:e.type,eventId:e.eventId,provider:e.provider,reason:d.reason,type:"webhook.applied"}),e.type==="payment.failed"?c(r,{provider:e.provider,referenceId:e.referenceId,sessionId:e.sessionId,type:"payment.failed"}):e.type==="subscription.past_due"&&c(r,{provider:e.provider,referenceId:e.referenceId,subscriptionId:e.subscriptionId,type:"subscription.past_due"}),d};export{H as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{money as y}from"./addMoney-B4ufWAnD.mjs";import{foldUsage as m}from"./MemoryPaymentStore-CZbrrEb1.mjs";const o=(e,n)=>typeof e[n]=="string"?e[n]:"",l=(e,n)=>typeof e[n]=="string"?e[n]:void 0,c=(e,n)=>typeof e[n]=="number"?e[n]:0,v=(e,n)=>typeof e[n]=="number"?e[n]:void 0,A=(e,n)=>e[n]===!0,S=(e,n)=>{const t=e[n];return Array.isArray(t)?t.filter(d=>typeof d=="string"):void 0},f=(e,n)=>{const t=e[n];return typeof t=="bigint"?t:typeof t=="number"||typeof t=="string"?BigInt(t):0n},P=e=>({createdAt:e.createdAt,...e.email===void 0?{}:{email:e.email},provider:e.provider,providerCustomerId:e.id,referenceId:e.referenceId}),E=e=>({createdAt:c(e,"createdAt"),email:l(e,"email"),id:o(e,"providerCustomerId"),provider:o(e,"provider"),referenceId:o(e,"referenceId")}),M=e=>({amountMinor:e.amount.minorUnits,capturedMinor:e.capturedAmount.minorUnits,createdAt:e.createdAt,currency:e.amount.currency,provider:e.provider,providerSessionId:e.id,referenceId:e.referenceId,refundedMinor:e.refundedAmount.minorUnits,state:e.state,updatedAt:e.updatedAt}),T=e=>{const n=o(e,"currency");return{amount:y(f(e,"amountMinor"),n),capturedAmount:y(f(e,"capturedMinor"),n),createdAt:c(e,"createdAt"),id:o(e,"providerSessionId"),provider:o(e,"provider"),referenceId:o(e,"referenceId"),refundedAmount:y(f(e,"refundedMinor"),n),state:o(e,"state"),updatedAt:c(e,"updatedAt")}},q=e=>({cancelAtPeriodEnd:e.cancelAtPeriodEnd,createdAt:e.createdAt,...e.currentPeriodEnd===void 0?{}:{currentPeriodEnd:e.currentPeriodEnd},...e.currentPeriodStart===void 0?{}:{currentPeriodStart:e.currentPeriodStart},priceId:e.priceId,...e.priceIds===void 0?{}:{priceIds:[...e.priceIds]},provider:e.provider,providerSubscriptionId:e.id,quantity:e.quantity,referenceId:e.referenceId,state:e.state,updatedAt:e.updatedAt}),I=e=>({cancelAtPeriodEnd:A(e,"cancelAtPeriodEnd"),createdAt:c(e,"createdAt"),currentPeriodEnd:v(e,"currentPeriodEnd"),currentPeriodStart:v(e,"currentPeriodStart"),id:o(e,"providerSubscriptionId"),priceId:o(e,"priceId"),priceIds:S(e,"priceIds"),provider:o(e,"provider"),quantity:c(e,"quantity"),referenceId:o(e,"referenceId"),state:o(e,"state"),updatedAt:c(e,"updatedAt")}),K=e=>({createdAt:c(e,"createdAt"),featureId:o(e,"featureId"),idempotencyKey:o(e,"idempotencyKey"),mode:e.mode==="set"?"set":"add",provider:o(e,"provider"),quantity:c(e,"quantity"),referenceId:o(e,"referenceId"),reportedToProvider:A(e,"reportedToProvider")}),U=e=>({createdAt:e.createdAt,featureId:e.featureId,idempotencyKey:e.idempotencyKey,...e.mode==="set"?{mode:"set"}:{},provider:e.provider,quantity:e.quantity,referenceId:e.referenceId,reportedToProvider:e.reportedToProvider}),h=e=>{const n=async(t,d,r)=>{const a=await e.findFirst(t,d);if(a){await e.patch(a._id,r);return}await e.insert(t,r)};return{getCustomerByReference:async(t,d)=>{const r=await e.findFirst("customers",{provider:t,referenceId:d});return r?E(r):void 0},getPaymentSession:async(t,d)=>{const r=await e.findFirst("paymentSessions",{provider:t,providerSessionId:d});return r?T(r):void 0},getSubscription:async(t,d)=>{const r=await e.findFirst("subscriptions",{provider:t,providerSubscriptionId:d});return r?I(r):void 0},listSubscriptionsByReference:async t=>{const{rows:d}=await e.findMany("subscriptions",{referenceId:t});return d.map(r=>I(r))},listUnreportedUsage:async(t,d)=>{const r=Math.max(0,Math.floor(d));if(r===0)return[];const a=[{createdAt:"asc"},{idempotencyKey:"asc"}],s=[];let i;do{const u=await e.findMany("usageEvents",{provider:t,reportedToProvider:!1},{cursor:i,limit:r,orderBy:a});for(const g of u.rows){const p=K(g);if(p.mode!=="set"&&p.quantity>0&&(s.push(p),s.length===r))return s}i=u.cursor}while(i!==void 0);return s},markEventProcessed:async(t,d,r)=>await e.findFirst("events",{provider:t,providerEventId:d})?!1:(await e.insert("events",{processedAt:Date.now(),provider:t,providerEventId:d,type:r}),!0),releaseEvent:async(t,d)=>{const r=await e.findFirst("events",{provider:t,providerEventId:d});r&&await e.delete(r._id)},markUsageReported:async(t,d)=>{const r=await e.findFirst("usageEvents",{idempotencyKey:d,provider:t});r&&await e.patch(r._id,{reportedToProvider:!0})},recordUsage:async t=>await e.findFirst("usageEvents",{idempotencyKey:t.idempotencyKey,provider:t.provider})?!1:(await e.insert("usageEvents",U(t)),!0),sumUsage:async(t,d,r)=>{const{rows:a}=await e.findMany("usageEvents",{featureId:d,referenceId:t}),s=a.filter(i=>c(i,"createdAt")>=r).map(i=>({createdAt:c(i,"createdAt"),idempotencyKey:typeof i.idempotencyKey=="string"?i.idempotencyKey:"",mode:i.mode==="set"?"set":"add",quantity:c(i,"quantity")}));return m(s)},sumUsageByFeature:async(t,d,r)=>{const{rows:a}=await e.findMany("usageEvents",{referenceId:t}),s=new Map(d.map(i=>[i,[]]));for(const i of a)c(i,"createdAt")<r||s.get(typeof i.featureId=="string"?i.featureId:"")?.push({createdAt:c(i,"createdAt"),idempotencyKey:typeof i.idempotencyKey=="string"?i.idempotencyKey:"",mode:i.mode==="set"?"set":"add",quantity:c(i,"quantity")});return new Map([...s].map(([i,u])=>[i,m(u)]))},upsertCustomer:async t=>n("customers",{provider:t.provider,referenceId:t.referenceId},P(t)),upsertPaymentSession:async t=>n("paymentSessions",{provider:t.provider,providerSessionId:t.id},M(t)),upsertSubscription:async t=>n("subscriptions",{provider:t.provider,providerSubscriptionId:t.id},q(t))}};export{h as createDatabasePaymentStore};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{toErrorBody as N}from"@lunora/errors";import{usagePeriodStart as g,resolveEntitlements as A,featureNames as U,hasActivePrice as _}from"./entitlementsForReference-PMmhAOWa.mjs";import{LunoraPaymentError as f}from"./LunoraPaymentError-BSxyhWgu.mjs";import{derivedIdempotencyKey as b,localRefundKey as D,LOCAL_REFUND_CLAIM_TYPE as E,idempotencyKey as k}from"./idempotencyKey-qQJLcEQX.mjs";import{subtractMoney as K,addMoney as T,compareMoney as v,isZeroMoney as B}from"./addMoney-B4ufWAnD.mjs";import{n as C}from"./observability-BteZ2dJS.mjs";import F from"./applyWebhookAction-
|
|
1
|
+
import{toErrorBody as N}from"@lunora/errors";import{usagePeriodStart as g,resolveEntitlements as A,featureNames as U,hasActivePrice as _}from"./entitlementsForReference-PMmhAOWa.mjs";import{LunoraPaymentError as f}from"./LunoraPaymentError-BSxyhWgu.mjs";import{derivedIdempotencyKey as b,localRefundKey as D,LOCAL_REFUND_CLAIM_TYPE as E,idempotencyKey as k}from"./idempotencyKey-qQJLcEQX.mjs";import{subtractMoney as K,addMoney as T,compareMoney as v,isZeroMoney as B}from"./addMoney-B4ufWAnD.mjs";import{n as C}from"./observability-BteZ2dJS.mjs";import F from"./applyWebhookAction-DyRG8lhp.mjs";const y=(o,n=200,s)=>{const u=new Headers({"content-type":"application/json"});for(const[l,w]of Object.entries({}))u.set(l,w);return Response.json(o,{headers:u,status:n})};let h=0;const $=()=>{const o=Date.now();return h=o>h?o:h+1,h},O=o=>o?`${o.currency}:${String(o.minorUnits)}`:"full",q=o=>o&&"referenceId"in o?Object.fromEntries(Object.entries(o).filter(([n])=>n!=="referenceId")):o,G=o=>y({applied:o.applied},o.status),H=o=>{const{adapter:n,store:s}=o,u=async e=>{if(!o.authorize)return;let t;try{t=await o.authorize(e)}catch{throw new f("FORBIDDEN",`caller not authorized for reference "${e}"`)}if(t!==!0)throw new f("FORBIDDEN",`caller not authorized for reference "${e}"`)},l=async e=>{await u(e.referenceId);const t=q(e.metadata);let r;const a=await s.getCustomerByReference(n.identifier,e.referenceId);if(a)r=a.id;else{const d=await n.getOrCreateCustomer({email:e.email,referenceId:e.referenceId});r=d.id,await s.upsertCustomer(d)}const i=e.idempotencyKey??await b("checkout",n.identifier,e.referenceId,e.priceId,e.mode,String(e.quantity??1),e.successUrl,e.cancelUrl,t?JSON.stringify(t):"");return n.createCheckout({...e,customerId:r,idempotencyKey:i,metadata:t})},w=(e,t,r)=>{const a=e-t;return{allowed:a>=r,balance:a,limit:e,unlimited:!1,used:t}},S=async(e,t,r,a,i)=>{const d=e.limit(a);if(d!==void 0){const c=await s.sumUsage(r,a,g(t));return w(d,c,i)}return{allowed:e.has(a),unlimited:e.has(a)}},p=async e=>{const t=()=>new f("NOT_FOUND",`payment session "${e}" not found`);let r=await s.getPaymentSession(n.identifier,e);if(!r)try{r=await n.getPaymentStatus(e)}catch{throw t()}if(!r.referenceId)throw t();try{await u(r.referenceId)}catch{throw t()}return r},I=async(e,t)=>{const r={...e,...t,updatedAt:Date.now()};return await s.upsertPaymentSession(r),r};return{adapter:n,attach:async e=>l({...e,mode:e.mode??"subscription"}),cancelPayment:async(e,t)=>{const r=await p(e),a=t?.idempotencyKey??k("cancel_payment",n.identifier,e),i=await n.cancelPayment(e,{...t,idempotencyKey:a});return I(r,{state:i.state})},cancelSubscription:async(e,t)=>{const r=await s.getSubscription(n.identifier,e);if(!r)throw new f("NOT_FOUND",`subscription "${e}" not found`);try{await u(r.referenceId)}catch{throw new f("NOT_FOUND",`subscription "${e}" not found`)}const a=t?.idempotencyKey??k("cancel_subscription",n.identifier,e),i=await n.cancelSubscription(e,{...t,idempotencyKey:a}),d={...i,referenceId:r.referenceId===""?i.referenceId:r.referenceId};return await s.upsertSubscription(d),d},capturePayment:async e=>{const t=await p(e.sessionId),r=e.idempotencyKey??await b("capture_payment",n.identifier,e.sessionId,O(e.amount)),a=await n.capturePayment({...e,idempotencyKey:r});return I(t,{capturedAmount:a.capturedAmount,state:a.state})},check:async e=>{if(await u(e.referenceId),e.featureId===void 0&&e.priceId===void 0)throw new f("VALIDATION_ERROR","check() requires a featureId or priceId");if(n.checkEntitlement)return n.checkEntitlement(e);const t=await s.listSubscriptionsByReference(e.referenceId);if(e.priceId!==void 0)return{allowed:_(t,e.priceId),unlimited:!1};if(e.featureId===void 0)throw new f("VALIDATION_ERROR","check() requires a featureId or priceId");if(!o.entitlements)throw new f("CONFIG_INVALID","check() requires `entitlements` to be configured");const r=A(o.entitlements,t);return S(r,t,e.referenceId,e.featureId,e.quantity??1)},createCheckout:async e=>l(e),createPortalSession:async(e,t)=>{await u(e);const r=await s.getCustomerByReference(n.identifier,e);if(!r)throw new f("NOT_FOUND",`no customer for reference "${e}"`);return n.createPortalSession({customerId:r.id,returnUrl:t})},handleWebhook:async e=>{let t;try{const a=await e.text();t=await n.parseWebhook({headers:e.headers,payload:a})}catch(a){if(a instanceof f){const{body:i,status:d}=N(a);return y({error:i.message},d)}return y({error:"webhook error"},400)}const r=await F(s,t,o.observability);return r.reason==="orphaned"?y({applied:r.applied,reason:r.reason},500):y({applied:r.applied,reason:r.reason},200)},listBalances:async e=>{if(await u(e),n.getBalances)return n.getBalances(e);if(!o.entitlements)throw new f("CONFIG_INVALID","listBalances() requires `entitlements` to be configured");const t=await s.listSubscriptionsByReference(e),r=A(o.entitlements,t),a=U(o.entitlements),i=a.filter(c=>r.limit(c)!==void 0),d=i.length===0?new Map:await s.sumUsageByFeature(e,i,g(t));return a.map(c=>{const m=r.limit(c);return m!==void 0?{featureId:c,...w(m,d.get(c)??0,1)}:{featureId:c,allowed:r.has(c),unlimited:r.has(c)}})},listSubscriptions:async e=>(await u(e),s.listSubscriptionsByReference(e)),refundPayment:async e=>{const t=await p(e.sessionId),r=e.amount??K(t.capturedAmount,t.refundedAmount),a=T(t.refundedAmount,r);if(v(a,t.capturedAmount)>0)throw new f("VALIDATION_ERROR",`refundPayment(): refunding ${String(e.amount?.minorUnits)} would exceed the captured amount on session "${e.sessionId}"`);if(B(r))return t;const i=e.amount??(v(r,t.capturedAmount)===0?void 0:r),d=e.idempotencyKey??await b("refund_payment",n.identifier,e.sessionId,O(i),e.reason??""),c=await n.refundPayment({...e,amount:i,idempotencyKey:d});if(c.pending)return t;const m=D(e.sessionId,c.refundId,r);if(!await s.markEventProcessed(n.identifier,m,E)&&c.refundId!==void 0)return t;try{return await I(t,{refundedAmount:a,state:v(a,t.capturedAmount)<0?"partially_refunded":"refunded"})}catch(P){throw await s.releaseEvent(n.identifier,m),P}},store:s,track:async e=>{await u(e.referenceId);const t=e.quantity===void 0?1:e.quantity;if(!Number.isSafeInteger(t)||t<0)throw new f("VALIDATION_ERROR",`track(): \`quantity\` must be a non-negative safe integer (got ${String(e.quantity)})`);const r=e.idempotencyKey??crypto.randomUUID(),a=e.mode==="set";if(a&&n.capabilities.usageMetering&&n.reportUsage)throw new f("VALIDATION_ERROR",`track(): \`mode: "set"\` is not supported on "${n.identifier}", whose meter is additive — a lowering set cannot be forwarded, so the provider would over-bill against the local period total. Use \`mode: "add"\`.`);const i=a?await s.listSubscriptionsByReference(e.referenceId):void 0,d=i===void 0?0:await s.sumUsage(e.referenceId,e.featureId,g(i));if(a?t===d:t===0)return{recorded:!1,reportedToProvider:!1};const c=t;if(!await s.recordUsage({createdAt:$(),featureId:e.featureId,idempotencyKey:r,...a?{mode:"set"}:{},provider:n.identifier,quantity:t,referenceId:e.referenceId,reportedToProvider:!1}))return{recorded:!1,reportedToProvider:!1};if(c<=0||!n.capabilities.usageMetering||!n.reportUsage)return{recorded:!0,reportedToProvider:!1};try{const R=await s.getCustomerByReference(n.identifier,e.referenceId);return await n.reportUsage({customerId:R?.id,featureId:e.featureId,idempotencyKey:r,quantity:c,referenceId:e.referenceId}),await s.markUsageReported(n.identifier,r),{recorded:!0,reportedToProvider:!0}}catch{return C(o.observability,{featureId:e.featureId,provider:n.identifier,referenceId:e.referenceId,type:"usage.report_failed"}),{recorded:!0,reportedToProvider:!1}}}}};export{H as createPayment,G as webhookResponse};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createPayment as i}from"./createPayment-
|
|
1
|
+
import{createPayment as i}from"./createPayment-BB3ri82t.mjs";import{createDatabasePaymentStore as s}from"./createDatabasePaymentStore-DG4P5AzL.mjs";const o=r=>({delete:async t=>r.delete(t),findFirst:async(t,e)=>await r.findFirst(t,{where:e}),findMany:async(t,e,a)=>{const n=await r.findMany(t,{...a,where:e});return{cursor:n.continueCursor??void 0,rows:n.page}},insert:async(t,e)=>r.insert(t,e),patch:async(t,e)=>r.patch(t,e)}),y=(r,t)=>{const e=r.auth?.userId??void 0;return i({adapter:t.adapter,authorize:t.authorize??(a=>a.trim()!==""&&e!==void 0&&a===e),entitlements:t.entitlements,observability:t.observability,store:s(o(r.db))})};export{o as lunoraDatabaseToPaymentDatabase,y as paymentsFromContext};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lunora/payment",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.133",
|
|
4
4
|
"description": "Provider-agnostic payments for Lunora: Stripe-first adapter, webhook sync, and subscription/payment state machine",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"billing",
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
},
|
|
73
73
|
"dependencies": {
|
|
74
74
|
"@lunora/errors": "1.0.0-alpha.39",
|
|
75
|
-
"@lunora/server": "1.0.0-alpha.
|
|
75
|
+
"@lunora/server": "1.0.0-alpha.131",
|
|
76
76
|
"@lunora/values": "1.0.0-alpha.49",
|
|
77
77
|
"dinero.js": "2.0.2"
|
|
78
78
|
},
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{LunoraPaymentError as P}from"./LunoraPaymentError-BSxyhWgu.mjs";import{localRefundKey as A,LOCAL_REFUND_CLAIM_TYPE as E}from"./idempotencyKey-qQJLcEQX.mjs";import{zeroMoney as o,compareMoney as l,addMoney as _}from"./addMoney-B4ufWAnD.mjs";import{n as a}from"./observability-BteZ2dJS.mjs";import{nextPaymentState as S,nextSubscriptionState as w}from"./PAYMENT_TERMINAL_STATES-C-ICZkun.mjs";const b={"payment.authorized":"authorize","payment.captured":"capture","payment.failed":"fail","payment.refunded":"refund"},h={"subscription.active":"active","subscription.canceled":"canceled","subscription.past_due":"past_due","subscription.paused":"paused"},T="orphan-retry:",R="marker.orphan_retry",k={"subscription.active":"activate","subscription.canceled":"cancel","subscription.past_due":"mark_past_due","subscription.paused":"pause"},O=(t,e)=>l(t,e)>0?t:e,Y=new Set(["authorized","initiated"]),N=async(t,e,r)=>{if(!r||!e.sessionId||!e.amount||e.amountKind==="absolute")return e;const d=A(e.sessionId,e.refundId,e.amount),n=await t.markEventProcessed(e.provider,d,E);return await t.releaseEvent(e.provider,d),n?e:{...e,amount:o(e.amount.currency)}},v=(t,e)=>{if(!e.amount||t.refundedAmount.currency!==e.amount.currency||t.capturedAmount.currency!==e.amount.currency)return;const r=e.amountKind==="absolute"?O(e.amount,t.refundedAmount):_(t.refundedAmount,e.amount);if(!(l(r,t.capturedAmount)>0))return r},M=(t,e)=>{if(!t)return"refund";const r=v(t,e);return r&&l(r,t.capturedAmount)<0?"partial_refund":"refund"},q=async(t,e,r)=>{if(!e.sessionId)return{applied:!1,reason:"unhandled"};const d=await t.getPaymentSession(e.provider,e.sessionId),n=d?.state??"initiated",u=Date.now(),s=e.amount?.currency??d?.amount.currency??"USD",c=r==="refund"?await N(t,e,d):e,p=r==="refund"?M(d,c):r,I=S(n,p);if(!I)return{applied:!1,reason:r==="refund"&&Y.has(n)?"orphaned":"illegal_transition"};const i=d??{amount:e.amount??o(s),capturedAmount:o(s),createdAt:u,id:e.sessionId,provider:e.provider,referenceId:e.referenceId??"",refundedAmount:o(s),state:n,updatedAt:u};let{capturedAmount:m,refundedAmount:y}=i;if(p==="capture"&&e.amount&&(m=e.amount),(p==="partial_refund"||p==="refund")&&c.amount){const f=v(i,c);if(!f)return{applied:!1,reason:"invalid_refund_amount"};y=f}return await t.upsertPaymentSession({...i,capturedAmount:m,referenceId:e.referenceId??i.referenceId,refundedAmount:y,state:I,updatedAt:u}),{applied:!0,reason:"ok"}},C=(t,e,r)=>{if(e==="active"){if(t==="active")return"renew";if(t==="paused")return"resume"}return k[r]},g=async(t,e)=>{if(!e.subscriptionId)return{applied:!1,reason:"unhandled"};const r=await t.getSubscription(e.provider,e.subscriptionId),d=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,priceIds:e.priceIds??r.priceIds,quantity:e.quantity??r.quantity,updatedAt:d}),{applied:!0,reason:"ok"}):{applied:!1,reason:"orphaned"};const n=h[e.type];if(!n)return{applied:!1,reason:"unhandled"};if(!r)return await t.upsertSubscription({cancelAtPeriodEnd:e.cancelAtPeriodEnd??!1,createdAt:d,currentPeriodEnd:e.currentPeriodEnd,currentPeriodStart:e.currentPeriodStart??d,id:e.subscriptionId,priceId:e.priceId??"",priceIds:e.priceIds,provider:e.provider,quantity:e.quantity??1,referenceId:e.referenceId??"",state:n,updatedAt:d}),{applied:!0,reason:"ok"};const u=C(r.state,n,e.type),s=u?w(r.state,u):void 0;return s?(await t.upsertSubscription({...r,cancelAtPeriodEnd:e.cancelAtPeriodEnd??r.cancelAtPeriodEnd,currentPeriodEnd:e.currentPeriodEnd??r.currentPeriodEnd,currentPeriodStart:e.currentPeriodStart??r.currentPeriodStart,priceId:e.priceId??r.priceId,priceIds:e.priceIds??r.priceIds,quantity:e.quantity??r.quantity,state:s,updatedAt:d}),{applied:!0,reason:"ok"}):{applied:!1,reason:"illegal_transition"}},U=async(t,e,r)=>{if(e.type==="unhandled")return{applied:!1,reason:"unhandled"};if(!e.eventId?.trim())throw new P("WEBHOOK_EVENT_ID_MISSING",`webhook event id is missing or blank for provider "${e.provider}"`);if(!await t.markEventProcessed(e.provider,e.eventId,e.type))return a(r,{eventId:e.eventId,provider:e.provider,type:"webhook.duplicate"}),{applied:!1,reason:"duplicate"};const n=b[e.type];let u;try{u=n?await q(t,e,n):await g(t,e)}catch(s){throw await t.releaseEvent(e.provider,e.eventId),s}return u.reason==="orphaned"&&(await t.markEventProcessed(e.provider,`${T}${e.eventId}`,R)?await t.releaseEvent(e.provider,e.eventId):u={applied:!1,reason:"unhandled"}),a(r,{action:e.type,eventId:e.eventId,provider:e.provider,reason:u.reason,type:"webhook.applied"}),e.type==="payment.failed"?a(r,{provider:e.provider,referenceId:e.referenceId,sessionId:e.sessionId,type:"payment.failed"}):e.type==="subscription.past_due"&&a(r,{provider:e.provider,referenceId:e.referenceId,subscriptionId:e.subscriptionId,type:"subscription.past_due"}),u};export{U as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{money as y}from"./addMoney-B4ufWAnD.mjs";import{foldUsage as m}from"./MemoryPaymentStore-CZbrrEb1.mjs";const o=(e,n)=>typeof e[n]=="string"?e[n]:"",l=(e,n)=>typeof e[n]=="string"?e[n]:void 0,c=(e,n)=>typeof e[n]=="number"?e[n]:0,v=(e,n)=>typeof e[n]=="number"?e[n]:void 0,A=(e,n)=>e[n]===!0,S=(e,n)=>{const t=e[n];return Array.isArray(t)?t.filter(d=>typeof d=="string"):void 0},f=(e,n)=>{const t=e[n];return typeof t=="bigint"?t:typeof t=="number"||typeof t=="string"?BigInt(t):0n},P=e=>({createdAt:e.createdAt,email:e.email,provider:e.provider,providerCustomerId:e.id,referenceId:e.referenceId}),E=e=>({createdAt:c(e,"createdAt"),email:l(e,"email"),id:o(e,"providerCustomerId"),provider:o(e,"provider"),referenceId:o(e,"referenceId")}),M=e=>({amountMinor:e.amount.minorUnits,capturedMinor:e.capturedAmount.minorUnits,createdAt:e.createdAt,currency:e.amount.currency,provider:e.provider,providerSessionId:e.id,referenceId:e.referenceId,refundedMinor:e.refundedAmount.minorUnits,state:e.state,updatedAt:e.updatedAt}),T=e=>{const n=o(e,"currency");return{amount:y(f(e,"amountMinor"),n),capturedAmount:y(f(e,"capturedMinor"),n),createdAt:c(e,"createdAt"),id:o(e,"providerSessionId"),provider:o(e,"provider"),referenceId:o(e,"referenceId"),refundedAmount:y(f(e,"refundedMinor"),n),state:o(e,"state"),updatedAt:c(e,"updatedAt")}},q=e=>({cancelAtPeriodEnd:e.cancelAtPeriodEnd,createdAt:e.createdAt,currentPeriodEnd:e.currentPeriodEnd,currentPeriodStart:e.currentPeriodStart,priceId:e.priceId,priceIds:e.priceIds===void 0?void 0:[...e.priceIds],provider:e.provider,providerSubscriptionId:e.id,quantity:e.quantity,referenceId:e.referenceId,state:e.state,updatedAt:e.updatedAt}),I=e=>({cancelAtPeriodEnd:A(e,"cancelAtPeriodEnd"),createdAt:c(e,"createdAt"),currentPeriodEnd:v(e,"currentPeriodEnd"),currentPeriodStart:v(e,"currentPeriodStart"),id:o(e,"providerSubscriptionId"),priceId:o(e,"priceId"),priceIds:S(e,"priceIds"),provider:o(e,"provider"),quantity:c(e,"quantity"),referenceId:o(e,"referenceId"),state:o(e,"state"),updatedAt:c(e,"updatedAt")}),K=e=>({createdAt:c(e,"createdAt"),featureId:o(e,"featureId"),idempotencyKey:o(e,"idempotencyKey"),mode:e.mode==="set"?"set":"add",provider:o(e,"provider"),quantity:c(e,"quantity"),referenceId:o(e,"referenceId"),reportedToProvider:A(e,"reportedToProvider")}),U=e=>({createdAt:e.createdAt,featureId:e.featureId,idempotencyKey:e.idempotencyKey,...e.mode==="set"?{mode:"set"}:{},provider:e.provider,quantity:e.quantity,referenceId:e.referenceId,reportedToProvider:e.reportedToProvider}),h=e=>{const n=async(t,d,r)=>{const a=await e.findFirst(t,d);if(a){await e.patch(a._id,r);return}await e.insert(t,r)};return{getCustomerByReference:async(t,d)=>{const r=await e.findFirst("customers",{provider:t,referenceId:d});return r?E(r):void 0},getPaymentSession:async(t,d)=>{const r=await e.findFirst("paymentSessions",{provider:t,providerSessionId:d});return r?T(r):void 0},getSubscription:async(t,d)=>{const r=await e.findFirst("subscriptions",{provider:t,providerSubscriptionId:d});return r?I(r):void 0},listSubscriptionsByReference:async t=>{const{rows:d}=await e.findMany("subscriptions",{referenceId:t});return d.map(r=>I(r))},listUnreportedUsage:async(t,d)=>{const r=Math.max(0,Math.floor(d));if(r===0)return[];const a=[{createdAt:"asc"},{idempotencyKey:"asc"}],s=[];let i;do{const u=await e.findMany("usageEvents",{provider:t,reportedToProvider:!1},{cursor:i,limit:r,orderBy:a});for(const g of u.rows){const p=K(g);if(p.mode!=="set"&&p.quantity>0&&(s.push(p),s.length===r))return s}i=u.cursor}while(i!==void 0);return s},markEventProcessed:async(t,d,r)=>await e.findFirst("events",{provider:t,providerEventId:d})?!1:(await e.insert("events",{processedAt:Date.now(),provider:t,providerEventId:d,type:r}),!0),releaseEvent:async(t,d)=>{const r=await e.findFirst("events",{provider:t,providerEventId:d});r&&await e.delete(r._id)},markUsageReported:async(t,d)=>{const r=await e.findFirst("usageEvents",{idempotencyKey:d,provider:t});r&&await e.patch(r._id,{reportedToProvider:!0})},recordUsage:async t=>await e.findFirst("usageEvents",{idempotencyKey:t.idempotencyKey,provider:t.provider})?!1:(await e.insert("usageEvents",U(t)),!0),sumUsage:async(t,d,r)=>{const{rows:a}=await e.findMany("usageEvents",{featureId:d,referenceId:t}),s=a.filter(i=>c(i,"createdAt")>=r).map(i=>({createdAt:c(i,"createdAt"),idempotencyKey:typeof i.idempotencyKey=="string"?i.idempotencyKey:"",mode:i.mode==="set"?"set":"add",quantity:c(i,"quantity")}));return m(s)},sumUsageByFeature:async(t,d,r)=>{const{rows:a}=await e.findMany("usageEvents",{referenceId:t}),s=new Map(d.map(i=>[i,[]]));for(const i of a)c(i,"createdAt")<r||s.get(typeof i.featureId=="string"?i.featureId:"")?.push({createdAt:c(i,"createdAt"),idempotencyKey:typeof i.idempotencyKey=="string"?i.idempotencyKey:"",mode:i.mode==="set"?"set":"add",quantity:c(i,"quantity")});return new Map([...s].map(([i,u])=>[i,m(u)]))},upsertCustomer:async t=>n("customers",{provider:t.provider,referenceId:t.referenceId},P(t)),upsertPaymentSession:async t=>n("paymentSessions",{provider:t.provider,providerSessionId:t.id},M(t)),upsertSubscription:async t=>n("subscriptions",{provider:t.provider,providerSubscriptionId:t.id},q(t))}};export{h as createDatabasePaymentStore};
|