@flopay/node 1.4.18 → 1.4.20

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/README.md CHANGED
@@ -60,6 +60,16 @@ const result = await flopay.checkout.sessions.create({
60
60
  // result = { status: 204 } (payment method already on file)
61
61
  ```
62
62
 
63
+ To authorise an eligible one-time card cart without capturing it, pass
64
+ `captureMethod: 'manual'` to `checkout.sessions.create`. Do not combine manual
65
+ capture with subscriptions; the SDK rejects that cart before making a request.
66
+ Omitting the option preserves immediate capture and leaves the wire key absent.
67
+
68
+ The later full capture is intentionally not an `@flopay/node` method. Use the
69
+ merchant-authenticated REST endpoint from trusted server code with a stable
70
+ `Idempotency-Key`; never expose merchant credentials to a browser. See the
71
+ repository's [pre-authorisation guide](../../docs/PREAUTHORIZATION.md).
72
+
63
73
  ### Retrieve / Expire a Checkout Session (Stripe Direct)
64
74
 
65
75
  ```ts
@@ -151,8 +161,8 @@ new FloPay(secretKey: string, options?: FloPayNodeOptions)
151
161
  | Type | Description |
152
162
  |------|-------------|
153
163
  | `FloPayNodeOptions` | Constructor options |
154
- | `CreateSessionParams` | Full session creation parameters (from `@flopay/shared`) |
164
+ | `CreateSessionParams` | Full session creation parameters (from `@flopay/shared`), including optional `captureMethod: 'automatic' \| 'manual'` |
155
165
  | `CheckoutSessionResult` | `{ status: 201, redirectUrl, nonce }` / `{ status: 204 }` / `{ status: number }` |
156
- | `CheckoutSession` | Normalized session: `id`, `clientSecret`, `mode`, `status`, `amount`, `currency`, `metadata` |
166
+ | `CheckoutSession` | Normalized session; `status` can be `authorized`, with `paymentId` and `authorizationExpiresAt`, for manual capture |
157
167
  | `Customer` | `id`, `email`, `firstName?`, `lastName?` |
158
168
  | `WebhookEvent` | `id`, `type`, `data`, `created` |
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";var A=Object.create;var u=Object.defineProperty;var z=Object.getOwnPropertyDescriptor;var D=Object.getOwnPropertyNames;var F=Object.getPrototypeOf,M=Object.prototype.hasOwnProperty;var q=(s,e)=>{for(var t in e)u(s,t,{get:e[t],enumerable:!0})},b=(s,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of D(e))!M.call(s,o)&&o!==t&&u(s,o,{get:()=>e[o],enumerable:!(r=z(e,o))||r.enumerable});return s};var W=(s,e,t)=>(t=s!=null?A(F(s)):{},b(e||!s||!s.__esModule?u(t,"default",{value:s,enumerable:!0}):t,s)),$=s=>b(u({},"__esModule",{value:!0}),s);var B={};q(B,{FloPay:()=>m,STRIPE_API_VERSION:()=>y});module.exports=$(B);var v=W(require("stripe"),1),i=require("@flopay/shared"),y="2026-04-22.dahlia",m=class{constructor(e,t){if(!e)throw new i.FloPayError("A secret key is required to initialize the FloPay server SDK.","authentication_error");this.stripe=new v.default(t?.stripeSecretKey??e,{apiVersion:t?.apiVersion??y,appInfo:{name:"flopay-node",version:i.SDK_VERSION}}),this.checkout={sessions:{create:this.createSession.bind(this),retrieve:this.retrieveSession.bind(this),expire:this.expireSession.bind(this),listLineItems:this.listLineItems.bind(this)}},this.webhooks={constructEvent:this.constructWebhookEvent.bind(this)},this.customers={create:this.createCustomer.bind(this),retrieve:this.retrieveCustomer.bind(this),update:this.updateCustomer.bind(this)}}async createSession(e){let{billingApiUrl:t,checkoutBaseUrl:r,items:o=[],subscriptions:C=[],products:w,account:n,successUrl:I,cancelUrl:N,checkoutMode:E="confirm",couponCodes:R=[],tagsData:S,redirectParams:U={},timeoutMs:x=12e3,clientId:_,currency:L,utmMetadata:g}=e,P=w??(0,i.foldIntoProducts)(o,C),l=(0,i.resolveSessionCurrency)(L,o,C,P);if(!l)throw new i.FloPayError("currency is required: pass `currency` on the session, or include a `currency` on the first item/subscription/product.","validation_error",{code:"CurrencyRequired",param:"currency"});let d={clientId:_,checkoutVersion:i.SDK_VERSION,successUrl:I,cancelUrl:N,currency:l,checkoutMode:E,products:P.map(a=>(0,i.buildProductPayload)(a,l)),accountData:{userId:n.userId,firstName:n.firstName??null,lastName:n.lastName??null,email:n.email,country:n.country??null,gender:n.gender??null,city:n.city??null,state:n.state??null,zip:n.zip??null},couponCodes:R};S&&(d.tagsData=S),g?.length&&(d.utmMetadata=g);let O=`${t.replace(/\/+$/,"")}/v1/checkouts/sessions`,f=new AbortController,T=setTimeout(()=>f.abort(),x),c,p;try{let a=await fetch(O,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(d),signal:f.signal});c=a.status;try{p=await a.json()}catch{}}finally{clearTimeout(T)}if(c===201){let a=p?.data?.uuid,k=p?.data?.nonce;if(!a)throw new Error("Checkout session created but no UUID was returned by the billing API");if(!k)throw new i.FloPayError("Checkout session created but no `nonce` was returned by the billing API. Upgrade the billing service to TeamFloPay/backend#640 or later.","api_error",{code:"MissingCheckoutSessionToken"});let h=new URL(`${r.replace(/\/+$/,"")}/secure`);h.searchParams.set("id",a);for(let[V,j]of Object.entries(U))h.searchParams.set(V,j);return{status:201,redirectUrl:h.toString(),nonce:k}}return c===204?{status:204}:{status:c}}normalizeStripeSession(e){let t={open:"open",complete:"complete",expired:"expired"};return{id:e.id,clientSecret:e.client_secret??"",mode:e.mode,status:t[e.status??"open"]??"open",amount:e.amount_total??0,currency:e.currency??"usd",metadata:e.metadata??{}}}async retrieveSession(e){let t=await this.stripe.checkout.sessions.retrieve(e);return this.normalizeStripeSession(t)}async expireSession(e){let t=await this.stripe.checkout.sessions.expire(e);return this.normalizeStripeSession(t)}async listLineItems(e){return(await this.stripe.checkout.sessions.listLineItems(e)).data.map(r=>({price:r.price?.id,quantity:r.quantity??1}))}constructWebhookEvent(e,t,r){let o=this.stripe.webhooks.constructEvent(e,t,r);return{id:o.id,type:o.type,data:o.data,created:o.created}}async createCustomer(e){let t=await this.stripe.customers.create({email:e.email,name:e.name,metadata:e.metadata});return{id:t.id,email:t.email??e.email,firstName:e.name?.split(" ")[0],lastName:e.name?.split(" ").slice(1).join(" ")}}async retrieveCustomer(e){let t=await this.stripe.customers.retrieve(e);if(t.deleted)throw new i.FloPayError(`Customer ${e} has been deleted`,"api_error",{code:"resource_missing"});return{id:t.id,email:t.email??"",firstName:t.name?.split(" ")[0],lastName:t.name?.split(" ").slice(1).join(" ")}}async updateCustomer(e,t){let r=await this.stripe.customers.update(e,{email:t.email,name:t.name,metadata:t.metadata});return{id:r.id,email:r.email??"",firstName:r.name?.split(" ")[0],lastName:r.name?.split(" ").slice(1).join(" ")}}};0&&(module.exports={FloPay,STRIPE_API_VERSION});
1
+ "use strict";var A=Object.create;var m=Object.defineProperty;var z=Object.getOwnPropertyDescriptor;var D=Object.getOwnPropertyNames;var F=Object.getPrototypeOf,q=Object.prototype.hasOwnProperty;var W=(r,e)=>{for(var t in e)m(r,t,{get:e[t],enumerable:!0})},v=(r,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of D(e))!q.call(r,o)&&o!==t&&m(r,o,{get:()=>e[o],enumerable:!(i=z(e,o))||i.enumerable});return r};var $=(r,e,t)=>(t=r!=null?A(F(r)):{},v(e||!r||!r.__esModule?m(t,"default",{value:r,enumerable:!0}):t,r)),B=r=>v(m({},"__esModule",{value:!0}),r);var K={};W(K,{FloPay:()=>d,STRIPE_API_VERSION:()=>g});module.exports=B(K);var w=$(require("stripe"),1),s=require("@flopay/shared"),g="2026-04-22.dahlia",d=class{constructor(e,t){if(!e)throw new s.FloPayError("A secret key is required to initialize the FloPay server SDK.","authentication_error");this.stripe=new w.default(t?.stripeSecretKey??e,{apiVersion:t?.apiVersion??g,appInfo:{name:"flopay-node",version:s.SDK_VERSION}}),this.checkout={sessions:{create:this.createSession.bind(this),retrieve:this.retrieveSession.bind(this),expire:this.expireSession.bind(this),listLineItems:this.listLineItems.bind(this)}},this.webhooks={constructEvent:this.constructWebhookEvent.bind(this)},this.customers={create:this.createCustomer.bind(this),retrieve:this.retrieveCustomer.bind(this),update:this.updateCustomer.bind(this)}}async createSession(e){let{billingApiUrl:t,checkoutBaseUrl:i,items:o=[],subscriptions:S=[],products:I,account:n,successUrl:N,cancelUrl:E,checkoutMode:R="confirm",captureMethod:l,couponCodes:U=[],tagsData:P,redirectParams:x={},timeoutMs:_=12e3,clientId:L,currency:M,utmMetadata:f}=e,p=I??(0,s.foldIntoProducts)(o,S);(0,s.assertCaptureMethodEligible)({captureMethod:l,products:p});let h=(0,s.resolveSessionCurrency)(M,o,S,p);if(!h)throw new s.FloPayError("currency is required: pass `currency` on the session, or include a `currency` on the first item/subscription/product.","validation_error",{code:"CurrencyRequired",param:"currency"});let c={clientId:L,checkoutVersion:s.SDK_VERSION,successUrl:N,cancelUrl:E,currency:h,checkoutMode:R,products:p.map(a=>(0,s.buildProductPayload)(a,h)),accountData:{userId:n.userId,firstName:n.firstName??null,lastName:n.lastName??null,email:n.email,country:n.country??null,gender:n.gender??null,city:n.city??null,state:n.state??null,zip:n.zip??null},couponCodes:U};l==="manual"&&(c.captureMethod=l),P&&(c.tagsData=P),f?.length&&(c.utmMetadata=f);let O=`${t.replace(/\/+$/,"")}/v1/checkouts/sessions`,k=new AbortController,T=setTimeout(()=>k.abort(),_),u,y;try{let a=await fetch(O,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(c),signal:k.signal});u=a.status;try{y=await a.json()}catch{}}finally{clearTimeout(T)}if(u===201){let a=y?.data?.uuid,b=y?.data?.nonce;if(!a)throw new Error("Checkout session created but no UUID was returned by the billing API");if(!b)throw new s.FloPayError("Checkout session created but no `nonce` was returned by the billing API. Upgrade the billing service to TeamFloPay/backend#640 or later.","api_error",{code:"MissingCheckoutSessionToken"});let C=new URL(`${i.replace(/\/+$/,"")}/secure`);C.searchParams.set("id",a);for(let[V,j]of Object.entries(x))C.searchParams.set(V,j);return{status:201,redirectUrl:C.toString(),nonce:b}}return u===204?{status:204}:{status:u}}normalizeStripeSession(e){let t={open:"open",complete:"complete",expired:"expired"};return{id:e.id,clientSecret:e.client_secret??"",mode:e.mode,status:t[e.status??"open"]??"open",amount:e.amount_total??0,currency:e.currency??"usd",metadata:e.metadata??{}}}async retrieveSession(e){let t=await this.stripe.checkout.sessions.retrieve(e);return this.normalizeStripeSession(t)}async expireSession(e){let t=await this.stripe.checkout.sessions.expire(e);return this.normalizeStripeSession(t)}async listLineItems(e){return(await this.stripe.checkout.sessions.listLineItems(e)).data.map(i=>({price:i.price?.id,quantity:i.quantity??1}))}constructWebhookEvent(e,t,i){let o=this.stripe.webhooks.constructEvent(e,t,i);return{id:o.id,type:o.type,data:o.data,created:o.created}}async createCustomer(e){let t=await this.stripe.customers.create({email:e.email,name:e.name,metadata:e.metadata});return{id:t.id,email:t.email??e.email,firstName:e.name?.split(" ")[0],lastName:e.name?.split(" ").slice(1).join(" ")}}async retrieveCustomer(e){let t=await this.stripe.customers.retrieve(e);if(t.deleted)throw new s.FloPayError(`Customer ${e} has been deleted`,"api_error",{code:"resource_missing"});return{id:t.id,email:t.email??"",firstName:t.name?.split(" ")[0],lastName:t.name?.split(" ").slice(1).join(" ")}}async updateCustomer(e,t){let i=await this.stripe.customers.update(e,{email:t.email,name:t.name,metadata:t.metadata});return{id:i.id,email:i.email??"",firstName:i.name?.split(" ")[0],lastName:i.name?.split(" ").slice(1).join(" ")}}};0&&(module.exports={FloPay,STRIPE_API_VERSION});
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import T from"stripe";import{FloPayError as a,SDK_VERSION as P,buildProductPayload as V,foldIntoProducts as j,resolveSessionCurrency as A}from"@flopay/shared";var f="2026-04-22.dahlia",d=class{constructor(e,t){if(!e)throw new a("A secret key is required to initialize the FloPay server SDK.","authentication_error");this.stripe=new T(t?.stripeSecretKey??e,{apiVersion:t?.apiVersion??f,appInfo:{name:"flopay-node",version:P}}),this.checkout={sessions:{create:this.createSession.bind(this),retrieve:this.retrieveSession.bind(this),expire:this.expireSession.bind(this),listLineItems:this.listLineItems.bind(this)}},this.webhooks={constructEvent:this.constructWebhookEvent.bind(this)},this.customers={create:this.createCustomer.bind(this),retrieve:this.retrieveCustomer.bind(this),update:this.updateCustomer.bind(this)}}async createSession(e){let{billingApiUrl:t,checkoutBaseUrl:s,items:i=[],subscriptions:p=[],products:k,account:r,successUrl:b,cancelUrl:v,checkoutMode:w="confirm",couponCodes:I=[],tagsData:h,redirectParams:N={},timeoutMs:E=12e3,clientId:R,currency:U,utmMetadata:y}=e,C=k??j(i,p),c=A(U,i,p,C);if(!c)throw new a("currency is required: pass `currency` on the session, or include a `currency` on the first item/subscription/product.","validation_error",{code:"CurrencyRequired",param:"currency"});let u={clientId:R,checkoutVersion:P,successUrl:b,cancelUrl:v,currency:c,checkoutMode:w,products:C.map(o=>V(o,c)),accountData:{userId:r.userId,firstName:r.firstName??null,lastName:r.lastName??null,email:r.email,country:r.country??null,gender:r.gender??null,city:r.city??null,state:r.state??null,zip:r.zip??null},couponCodes:I};h&&(u.tagsData=h),y?.length&&(u.utmMetadata=y);let x=`${t.replace(/\/+$/,"")}/v1/checkouts/sessions`,S=new AbortController,_=setTimeout(()=>S.abort(),E),n,m;try{let o=await fetch(x,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(u),signal:S.signal});n=o.status;try{m=await o.json()}catch{}}finally{clearTimeout(_)}if(n===201){let o=m?.data?.uuid,g=m?.data?.nonce;if(!o)throw new Error("Checkout session created but no UUID was returned by the billing API");if(!g)throw new a("Checkout session created but no `nonce` was returned by the billing API. Upgrade the billing service to TeamFloPay/backend#640 or later.","api_error",{code:"MissingCheckoutSessionToken"});let l=new URL(`${s.replace(/\/+$/,"")}/secure`);l.searchParams.set("id",o);for(let[L,O]of Object.entries(N))l.searchParams.set(L,O);return{status:201,redirectUrl:l.toString(),nonce:g}}return n===204?{status:204}:{status:n}}normalizeStripeSession(e){let t={open:"open",complete:"complete",expired:"expired"};return{id:e.id,clientSecret:e.client_secret??"",mode:e.mode,status:t[e.status??"open"]??"open",amount:e.amount_total??0,currency:e.currency??"usd",metadata:e.metadata??{}}}async retrieveSession(e){let t=await this.stripe.checkout.sessions.retrieve(e);return this.normalizeStripeSession(t)}async expireSession(e){let t=await this.stripe.checkout.sessions.expire(e);return this.normalizeStripeSession(t)}async listLineItems(e){return(await this.stripe.checkout.sessions.listLineItems(e)).data.map(s=>({price:s.price?.id,quantity:s.quantity??1}))}constructWebhookEvent(e,t,s){let i=this.stripe.webhooks.constructEvent(e,t,s);return{id:i.id,type:i.type,data:i.data,created:i.created}}async createCustomer(e){let t=await this.stripe.customers.create({email:e.email,name:e.name,metadata:e.metadata});return{id:t.id,email:t.email??e.email,firstName:e.name?.split(" ")[0],lastName:e.name?.split(" ").slice(1).join(" ")}}async retrieveCustomer(e){let t=await this.stripe.customers.retrieve(e);if(t.deleted)throw new a(`Customer ${e} has been deleted`,"api_error",{code:"resource_missing"});return{id:t.id,email:t.email??"",firstName:t.name?.split(" ")[0],lastName:t.name?.split(" ").slice(1).join(" ")}}async updateCustomer(e,t){let s=await this.stripe.customers.update(e,{email:t.email,name:t.name,metadata:t.metadata});return{id:s.id,email:s.email??"",firstName:s.name?.split(" ")[0],lastName:s.name?.split(" ").slice(1).join(" ")}}};export{d as FloPay,f as STRIPE_API_VERSION};
1
+ import T from"stripe";import{FloPayError as c,SDK_VERSION as f,assertCaptureMethodEligible as V,buildProductPayload as j,foldIntoProducts as A,resolveSessionCurrency as z}from"@flopay/shared";var k="2026-04-22.dahlia",h=class{constructor(e,t){if(!e)throw new c("A secret key is required to initialize the FloPay server SDK.","authentication_error");this.stripe=new T(t?.stripeSecretKey??e,{apiVersion:t?.apiVersion??k,appInfo:{name:"flopay-node",version:f}}),this.checkout={sessions:{create:this.createSession.bind(this),retrieve:this.retrieveSession.bind(this),expire:this.expireSession.bind(this),listLineItems:this.listLineItems.bind(this)}},this.webhooks={constructEvent:this.constructWebhookEvent.bind(this)},this.customers={create:this.createCustomer.bind(this),retrieve:this.retrieveCustomer.bind(this),update:this.updateCustomer.bind(this)}}async createSession(e){let{billingApiUrl:t,checkoutBaseUrl:s,items:i=[],subscriptions:y=[],products:b,account:r,successUrl:v,cancelUrl:w,checkoutMode:I="confirm",captureMethod:u,couponCodes:N=[],tagsData:C,redirectParams:E={},timeoutMs:R=12e3,clientId:U,currency:x,utmMetadata:g}=e,m=b??A(i,y);V({captureMethod:u,products:m});let d=z(x,i,y,m);if(!d)throw new c("currency is required: pass `currency` on the session, or include a `currency` on the first item/subscription/product.","validation_error",{code:"CurrencyRequired",param:"currency"});let n={clientId:U,checkoutVersion:f,successUrl:v,cancelUrl:w,currency:d,checkoutMode:I,products:m.map(o=>j(o,d)),accountData:{userId:r.userId,firstName:r.firstName??null,lastName:r.lastName??null,email:r.email,country:r.country??null,gender:r.gender??null,city:r.city??null,state:r.state??null,zip:r.zip??null},couponCodes:N};u==="manual"&&(n.captureMethod=u),C&&(n.tagsData=C),g?.length&&(n.utmMetadata=g);let _=`${t.replace(/\/+$/,"")}/v1/checkouts/sessions`,S=new AbortController,L=setTimeout(()=>S.abort(),R),a,l;try{let o=await fetch(_,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(n),signal:S.signal});a=o.status;try{l=await o.json()}catch{}}finally{clearTimeout(L)}if(a===201){let o=l?.data?.uuid,P=l?.data?.nonce;if(!o)throw new Error("Checkout session created but no UUID was returned by the billing API");if(!P)throw new c("Checkout session created but no `nonce` was returned by the billing API. Upgrade the billing service to TeamFloPay/backend#640 or later.","api_error",{code:"MissingCheckoutSessionToken"});let p=new URL(`${s.replace(/\/+$/,"")}/secure`);p.searchParams.set("id",o);for(let[M,O]of Object.entries(E))p.searchParams.set(M,O);return{status:201,redirectUrl:p.toString(),nonce:P}}return a===204?{status:204}:{status:a}}normalizeStripeSession(e){let t={open:"open",complete:"complete",expired:"expired"};return{id:e.id,clientSecret:e.client_secret??"",mode:e.mode,status:t[e.status??"open"]??"open",amount:e.amount_total??0,currency:e.currency??"usd",metadata:e.metadata??{}}}async retrieveSession(e){let t=await this.stripe.checkout.sessions.retrieve(e);return this.normalizeStripeSession(t)}async expireSession(e){let t=await this.stripe.checkout.sessions.expire(e);return this.normalizeStripeSession(t)}async listLineItems(e){return(await this.stripe.checkout.sessions.listLineItems(e)).data.map(s=>({price:s.price?.id,quantity:s.quantity??1}))}constructWebhookEvent(e,t,s){let i=this.stripe.webhooks.constructEvent(e,t,s);return{id:i.id,type:i.type,data:i.data,created:i.created}}async createCustomer(e){let t=await this.stripe.customers.create({email:e.email,name:e.name,metadata:e.metadata});return{id:t.id,email:t.email??e.email,firstName:e.name?.split(" ")[0],lastName:e.name?.split(" ").slice(1).join(" ")}}async retrieveCustomer(e){let t=await this.stripe.customers.retrieve(e);if(t.deleted)throw new c(`Customer ${e} has been deleted`,"api_error",{code:"resource_missing"});return{id:t.id,email:t.email??"",firstName:t.name?.split(" ")[0],lastName:t.name?.split(" ").slice(1).join(" ")}}async updateCustomer(e,t){let s=await this.stripe.customers.update(e,{email:t.email,name:t.name,metadata:t.metadata});return{id:s.id,email:s.email??"",firstName:s.name?.split(" ")[0],lastName:s.name?.split(" ").slice(1).join(" ")}}};export{h as FloPay,k as STRIPE_API_VERSION};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flopay/node",
3
- "version": "1.4.18",
3
+ "version": "1.4.20",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "publishConfig": {
@@ -27,7 +27,7 @@
27
27
  ],
28
28
  "dependencies": {
29
29
  "stripe": "^22.2.1",
30
- "@flopay/shared": "1.4.18"
30
+ "@flopay/shared": "1.4.20"
31
31
  },
32
32
  "devDependencies": {
33
33
  "tsup": "^8.3.0",