@lunora/payment 1.0.0-alpha.102 → 1.0.0-alpha.103
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/createPayment-CS5Pd2yI.mjs +1 -0
- package/dist/packem_shared/{lunoraDatabaseToPaymentDatabase-BJkDz3DL.mjs → lunoraDatabaseToPaymentDatabase-DcpOgifo.mjs} +1 -1
- package/package.json +2 -2
- package/dist/packem_shared/createPayment-uEUXhWny.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-DcpOgifo.mjs";import{createPayment as y,webhookResponse as i}from"./packem_shared/createPayment-CS5Pd2yI.mjs";import{createDatabasePaymentStore as s}from"./packem_shared/createDatabasePaymentStore-DWe1Vz4V.mjs";import{entitlementsForReference as c,featureNames as x,hasActivePrice as S,resolveEntitlements as l,usagePeriodStart as M}from"./packem_shared/entitlementsForReference-Bq-9UUL8.mjs";import{LunoraPaymentError as u}from"./packem_shared/LunoraPaymentError-BSxyhWgu.mjs";import{idempotencyKey as b}from"./packem_shared/idempotencyKey-DBL6bpmj.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-Cm45pURT.mjs";import{default as F}from"./packem_shared/paymentTables-Be-_11ux.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-tA4tF4im.mjs";import{MemoryPaymentStore as K}from"./packem_shared/MemoryPaymentStore-CZbrrEb1.mjs";import{default as Y}from"./packem_shared/applyWebhookAction-DMAgu_yP.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{toErrorBody as R}from"@lunora/errors";import{usagePeriodStart as b,resolveEntitlements as S,featureNames as O,hasActivePrice as U}from"./entitlementsForReference-Bq-9UUL8.mjs";import{LunoraPaymentError as f}from"./LunoraPaymentError-BSxyhWgu.mjs";import{derivedIdempotencyKey as v,localRefundKey as _,LOCAL_REFUND_CLAIM_TYPE as D,idempotencyKey as A}from"./idempotencyKey-DBL6bpmj.mjs";import{subtractMoney as K,addMoney as B,compareMoney as k,isZeroMoney as C}from"./addMoney-B4ufWAnD.mjs";import{n as E}from"./observability-BteZ2dJS.mjs";import F from"./applyWebhookAction-DMAgu_yP.mjs";const y=(o,n=200,s)=>{const m=new Headers({"content-type":"application/json"});for(const[l,w]of Object.entries({}))m.set(l,w);return Response.json(o,{headers:m,status:n})};let h=0;const T=()=>{const o=Date.now();return h=o>h?o:h+1,h},P=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),W=o=>{const{adapter:n,store:s}=o,m=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 m(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 v("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}},N=async(e,t,r,a,i)=>{const d=e.limit(a);if(d!==void 0){const c=await s.sumUsage(r,a,b(t));return w(d,c,i)}return{allowed:e.has(a),unlimited:e.has(a)}},I=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 m(r.referenceId)}catch{throw t()}return r},p=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 I(e),a=t?.idempotencyKey??A("cancel_payment",n.identifier,e),i=await n.cancelPayment(e,{...t,idempotencyKey:a});return p(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 m(r.referenceId)}catch{throw new f("NOT_FOUND",`subscription "${e}" not found`)}const a=t?.idempotencyKey??A("cancel_subscription",n.identifier,e),i=await n.cancelSubscription(e,{...t,idempotencyKey:a});return await s.upsertSubscription(i),i},capturePayment:async e=>{const t=await I(e.sessionId),r=e.idempotencyKey??await v("capture_payment",n.identifier,e.sessionId,P(e.amount)),a=await n.capturePayment({...e,idempotencyKey:r});return p(t,{capturedAmount:a.capturedAmount,state:a.state})},check:async e=>{if(await m(e.referenceId),e.featureId===void 0&&e.priceId===void 0)throw new f("CONFIG_INVALID","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:U(t,e.priceId),unlimited:!1};if(e.featureId===void 0)throw new f("CONFIG_INVALID","check() requires a featureId or priceId");if(!o.entitlements)throw new f("CONFIG_INVALID","check() requires `entitlements` to be configured");const r=S(o.entitlements,t);return N(r,t,e.referenceId,e.featureId,e.quantity??1)},createCheckout:async e=>l(e),createPortalSession:async(e,t)=>{await m(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}=R(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 m(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=S(o.entitlements,t),a=O(o.entitlements),i=a.filter(c=>r.limit(c)!==void 0),d=i.length===0?new Map:await s.sumUsageByFeature(e,i,b(t));return a.map(c=>{const u=r.limit(c);return u!==void 0?{featureId:c,...w(u,d.get(c)??0,1)}:{featureId:c,allowed:r.has(c),unlimited:r.has(c)}})},listSubscriptions:async e=>(await m(e),s.listSubscriptionsByReference(e)),refundPayment:async e=>{const t=await I(e.sessionId),r=e.amount??K(t.capturedAmount,t.refundedAmount),a=B(t.refundedAmount,r);if(k(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(C(r))return t;const i=e.amount??(k(r,t.capturedAmount)===0?void 0:r),d=e.idempotencyKey??await v("refund_payment",n.identifier,e.sessionId,P(i),e.reason??""),c=await n.refundPayment({...e,amount:i,idempotencyKey:d});if(c.pending)return t;const u=_(e.sessionId,c.refundId,r);await s.markEventProcessed(n.identifier,u,D);try{return await p(t,{refundedAmount:a,state:k(a,t.capturedAmount)<0?"partially_refunded":"refunded"})}catch(g){throw await s.releaseEvent(n.identifier,u),g}},store:s,track:async e=>{await m(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",i=a?await s.listSubscriptionsByReference(e.referenceId):void 0,d=i===void 0?0:await s.sumUsage(e.referenceId,e.featureId,b(i));if(a?t===d:t===0)return{recorded:!1,reportedToProvider:!1};const c=a?t-d:t;if(!await s.recordUsage({createdAt:T(),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 g=await s.getCustomerByReference(n.identifier,e.referenceId);return await n.reportUsage({customerId:g?.id,featureId:e.featureId,idempotencyKey:r,quantity:c,referenceId:e.referenceId}),await s.markUsageReported(n.identifier,r),{recorded:!0,reportedToProvider:!0}}catch{return E(o.observability,{featureId:e.featureId,provider:n.identifier,referenceId:e.referenceId,type:"usage.report_failed"}),{recorded:!0,reportedToProvider:!1}}}}};export{W as createPayment,G as webhookResponse};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createPayment as i}from"./createPayment-
|
|
1
|
+
import{createPayment as i}from"./createPayment-CS5Pd2yI.mjs";import{createDatabasePaymentStore as s}from"./createDatabasePaymentStore-DWe1Vz4V.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.103",
|
|
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.30",
|
|
75
|
-
"@lunora/server": "1.0.0-alpha.
|
|
75
|
+
"@lunora/server": "1.0.0-alpha.101",
|
|
76
76
|
"@lunora/values": "1.0.0-alpha.38",
|
|
77
77
|
"dinero.js": "2.0.2"
|
|
78
78
|
},
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{toErrorBody as R}from"@lunora/errors";import{usagePeriodStart as p,resolveEntitlements as S,featureNames as O,hasActivePrice as U}from"./entitlementsForReference-Bq-9UUL8.mjs";import{LunoraPaymentError as f}from"./LunoraPaymentError-BSxyhWgu.mjs";import{derivedIdempotencyKey as g,localRefundKey as _,LOCAL_REFUND_CLAIM_TYPE as D,idempotencyKey as A}from"./idempotencyKey-DBL6bpmj.mjs";import{subtractMoney as K,addMoney as B,compareMoney as b,isZeroMoney as C}from"./addMoney-B4ufWAnD.mjs";import{n as E}from"./observability-BteZ2dJS.mjs";import F from"./applyWebhookAction-DMAgu_yP.mjs";const y=(o,n=200,s)=>Response.json(o,{headers:{"content-type":"application/json",...s},status:n});let l=0;const T=()=>{const o=Date.now();return l=o>l?o:l+1,l},P=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),W=o=>{const{adapter:n,store:s}=o,m=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}"`)},v=async e=>{await m(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 g("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})},k=(e,t,r)=>{const a=e-t;return{allowed:a>=r,balance:a,limit:e,unlimited:!1,used:t}},N=async(e,t,r,a,i)=>{const d=e.limit(a);if(d!==void 0){const c=await s.sumUsage(r,a,p(t));return k(d,c,i)}return{allowed:e.has(a),unlimited:e.has(a)}},w=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 m(r.referenceId)}catch{throw t()}return r},h=async(e,t)=>{const r={...e,...t,updatedAt:Date.now()};return await s.upsertPaymentSession(r),r};return{adapter:n,attach:async e=>v({...e,mode:e.mode??"subscription"}),cancelPayment:async(e,t)=>{const r=await w(e),a=t?.idempotencyKey??A("cancel_payment",n.identifier,e),i=await n.cancelPayment(e,{...t,idempotencyKey:a});return h(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 m(r.referenceId)}catch{throw new f("NOT_FOUND",`subscription "${e}" not found`)}const a=t?.idempotencyKey??A("cancel_subscription",n.identifier,e),i=await n.cancelSubscription(e,{...t,idempotencyKey:a});return await s.upsertSubscription(i),i},capturePayment:async e=>{const t=await w(e.sessionId),r=e.idempotencyKey??await g("capture_payment",n.identifier,e.sessionId,P(e.amount)),a=await n.capturePayment({...e,idempotencyKey:r});return h(t,{capturedAmount:a.capturedAmount,state:a.state})},check:async e=>{if(await m(e.referenceId),e.featureId===void 0&&e.priceId===void 0)throw new f("CONFIG_INVALID","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:U(t,e.priceId),unlimited:!1};if(e.featureId===void 0)throw new f("CONFIG_INVALID","check() requires a featureId or priceId");if(!o.entitlements)throw new f("CONFIG_INVALID","check() requires `entitlements` to be configured");const r=S(o.entitlements,t);return N(r,t,e.referenceId,e.featureId,e.quantity??1)},createCheckout:async e=>v(e),createPortalSession:async(e,t)=>{await m(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}=R(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 m(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=S(o.entitlements,t),a=O(o.entitlements),i=a.filter(c=>r.limit(c)!==void 0),d=i.length===0?new Map:await s.sumUsageByFeature(e,i,p(t));return a.map(c=>{const u=r.limit(c);return u!==void 0?{featureId:c,...k(u,d.get(c)??0,1)}:{featureId:c,allowed:r.has(c),unlimited:r.has(c)}})},listSubscriptions:async e=>(await m(e),s.listSubscriptionsByReference(e)),refundPayment:async e=>{const t=await w(e.sessionId),r=e.amount??K(t.capturedAmount,t.refundedAmount),a=B(t.refundedAmount,r);if(b(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(C(r))return t;const i=e.amount??(b(r,t.capturedAmount)===0?void 0:r),d=e.idempotencyKey??await g("refund_payment",n.identifier,e.sessionId,P(i),e.reason??""),c=await n.refundPayment({...e,amount:i,idempotencyKey:d});if(c.pending)return t;const u=_(e.sessionId,c.refundId,r);await s.markEventProcessed(n.identifier,u,D);try{return await h(t,{refundedAmount:a,state:b(a,t.capturedAmount)<0?"partially_refunded":"refunded"})}catch(I){throw await s.releaseEvent(n.identifier,u),I}},store:s,track:async e=>{await m(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",i=a?await s.listSubscriptionsByReference(e.referenceId):void 0,d=i===void 0?0:await s.sumUsage(e.referenceId,e.featureId,p(i));if(a?t===d:t===0)return{recorded:!1,reportedToProvider:!1};const c=a?t-d:t;if(!await s.recordUsage({createdAt:T(),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 I=await s.getCustomerByReference(n.identifier,e.referenceId);return await n.reportUsage({customerId:I?.id,featureId:e.featureId,idempotencyKey:r,quantity:c,referenceId:e.referenceId}),await s.markUsageReported(n.identifier,r),{recorded:!0,reportedToProvider:!0}}catch{return E(o.observability,{featureId:e.featureId,provider:n.identifier,referenceId:e.referenceId,type:"usage.report_failed"}),{recorded:!0,reportedToProvider:!1}}}}};export{W as createPayment,G as webhookResponse};
|