@shware/purchase 7.3.0 → 7.6.0
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/app-store/index.cjs +1 -0
- package/dist/app-store/index.d.cts +2 -2
- package/dist/app-store/index.d.mts +2 -2
- package/dist/app-store/index.mjs +2 -2
- package/dist/app-store/mapper.cjs +22 -0
- package/dist/app-store/mapper.cjs.map +1 -1
- package/dist/app-store/mapper.d.cts +17 -1
- package/dist/app-store/mapper.d.cts.map +1 -1
- package/dist/app-store/mapper.d.mts +17 -1
- package/dist/app-store/mapper.d.mts.map +1 -1
- package/dist/app-store/mapper.mjs +22 -1
- package/dist/app-store/mapper.mjs.map +1 -1
- package/dist/google-play/index.cjs +1 -0
- package/dist/google-play/index.d.cts +2 -2
- package/dist/google-play/index.d.mts +2 -2
- package/dist/google-play/index.mjs +2 -2
- package/dist/google-play/mapper.cjs +12 -0
- package/dist/google-play/mapper.cjs.map +1 -1
- package/dist/google-play/mapper.d.cts +10 -1
- package/dist/google-play/mapper.d.cts.map +1 -1
- package/dist/google-play/mapper.d.mts +10 -1
- package/dist/google-play/mapper.d.mts.map +1 -1
- package/dist/google-play/mapper.mjs +12 -1
- package/dist/google-play/mapper.mjs.map +1 -1
- package/dist/stripe/config.cjs +9 -0
- package/dist/stripe/config.cjs.map +1 -1
- package/dist/stripe/config.d.cts +6 -0
- package/dist/stripe/config.d.cts.map +1 -1
- package/dist/stripe/config.d.mts +6 -0
- package/dist/stripe/config.d.mts.map +1 -1
- package/dist/stripe/config.mjs +9 -0
- package/dist/stripe/config.mjs.map +1 -1
- package/dist/stripe/index.cjs +7 -0
- package/dist/stripe/index.d.cts +4 -2
- package/dist/stripe/index.d.mts +4 -2
- package/dist/stripe/index.mjs +4 -2
- package/dist/stripe/mapper.cjs +21 -2
- package/dist/stripe/mapper.cjs.map +1 -1
- package/dist/stripe/mapper.d.cts +9 -1
- package/dist/stripe/mapper.d.cts.map +1 -1
- package/dist/stripe/mapper.d.mts +9 -1
- package/dist/stripe/mapper.d.mts.map +1 -1
- package/dist/stripe/mapper.mjs +22 -4
- package/dist/stripe/mapper.mjs.map +1 -1
- package/dist/stripe/payment-method.cjs +34 -0
- package/dist/stripe/payment-method.cjs.map +1 -0
- package/dist/stripe/payment-method.d.cts +22 -0
- package/dist/stripe/payment-method.d.cts.map +1 -0
- package/dist/stripe/payment-method.d.mts +22 -0
- package/dist/stripe/payment-method.d.mts.map +1 -0
- package/dist/stripe/payment-method.mjs +32 -0
- package/dist/stripe/payment-method.mjs.map +1 -0
- package/dist/stripe/subscription.cjs +35 -0
- package/dist/stripe/subscription.cjs.map +1 -0
- package/dist/stripe/subscription.d.cts +33 -0
- package/dist/stripe/subscription.d.cts.map +1 -0
- package/dist/stripe/subscription.d.mts +33 -0
- package/dist/stripe/subscription.d.mts.map +1 -0
- package/dist/stripe/subscription.mjs +33 -0
- package/dist/stripe/subscription.mjs.map +1 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.mjs","names":[],"sources":["../../src/stripe/config.ts"],"sourcesContent":["import { invariant } from '@shware/utils';\nimport ms from 'ms';\nimport type { Metadata } from '../types';\n\nexport type PriceId = `price_${string}`;\n\n/**\n * Which Stripe object space the config addresses. Test and live prices are\n * disjoint, so a config is built for exactly one of them. Derive it from the\n * secret key (`sk_test_` vs `sk_live_`) rather than from the deployment\n * environment: the key is what actually decides which space API calls hit.\n */\nexport type StripeMode = 'live' | 'test';\n\nconst addMethod = Symbol('addMethod');\n\nclass Product<\n NS extends Lowercase<string> = Lowercase<string>,\n PE extends string = string,\n BP extends string = string,\n PL extends string = never,\n PI extends string = never,\n I extends PriceId = never,\n> {\n readonly id: string;\n readonly config: StripeConfig<NS, PE, BP, PL, PI>;\n readonly plan: PE | null;\n readonly billingPeriod: BP | null;\n readonly prices: Map<PriceId, Metadata>;\n\n defaultPriceId: PriceId | null;\n\n constructor(\n config: StripeConfig<NS, PE, BP, PL, PI>,\n id: string,\n plan: PE | null = null,\n billingPeriod: BP | null = null\n ) {\n this.id = id;\n this.config = config;\n this.plan = plan;\n this.billingPeriod = billingPeriod;\n this.prices = new Map();\n this.defaultPriceId = null;\n }\n\n /**\n * Non-default prices stay declared on purpose: they keep resolving for\n * grandfathered subscribers and serve as price-experiment arms. Never\n * create new checkouts with a legacy price unless that is intentional.\n */\n price = <K extends PriceId>(\n priceId: K extends I ? never : K,\n metadata: Metadata = { credits: 0, expiresIn: '0' }\n ): Product<NS, PE, BP, PL, PI, I | K> => {\n invariant(!this.prices.has(priceId), `Duplicate price ${priceId}`);\n this.prices.set(priceId, metadata);\n return this as Product<NS, PE, BP, PL, PI, I | K>;\n };\n\n /**\n * `test` is the sandbox twin of the default price. In test mode it is what\n * checkouts are created with, and it resolves to the default price's\n * entitlement, so credits are declared once and cannot drift between the\n * two modes. Legacy prices need no twin: a sandbox holds no grandfathered\n * subscribers. Live mode ignores it, so a test price reaching a live config\n * still fails as an unknown price instead of being granted an entitlement.\n */\n default = (defaultPriceId: I, twin?: { test: PriceId }) => {\n const metadata = this.prices.get(defaultPriceId);\n invariant(metadata, `Default price ${defaultPriceId} is not declared`);\n if (this.config.mode === 'test') {\n invariant(twin, `Test price not declared for default ${defaultPriceId} of ${this.id}`);\n invariant(!this.prices.has(twin.test), `Duplicate price ${twin.test}`);\n this.prices.set(twin.test, metadata);\n this.defaultPriceId = twin.test;\n } else {\n this.defaultPriceId = defaultPriceId;\n }\n this.config[addMethod](this);\n return this.config;\n };\n}\n\ntype Options = {\n mode: StripeMode;\n returnUrl: string;\n cancelUrl: string;\n successUrl: `${string}session_id={CHECKOUT_SESSION_ID}${string}`;\n cancellationCouponId: string;\n allowPromotionCodes: boolean;\n};\n\nexport class StripeConfig<\n NS extends Lowercase<string> = Lowercase<string>,\n PE extends string = string,\n BP extends string = string,\n PL extends string = never,\n PI extends string = never,\n> {\n private readonly products: Map<string, Product<NS, PE, BP, PL, PI>> = new Map();\n\n public readonly mode: StripeMode;\n public returnUrl: string;\n public cancelUrl: string;\n public successUrl: `${string}session_id={CHECKOUT_SESSION_ID}${string}`;\n public cancellationCouponId: string;\n public allowPromotionCodes: boolean;\n\n get productIds(): string[] {\n return Array.from(this.products.keys());\n }\n\n // runtime mirror of the type-level guards: generic accumulation only\n // protects a single fluent chain, not statement-style re-entry or casts\n [addMethod] = (product: Product<NS, PE, BP, PL, PI>) => {\n invariant(!this.products.has(product.id), `Duplicate product ${product.id}`);\n // A Stripe price belongs to one Stripe product, and getProductId must have one answer.\n for (const existing of this.products.values()) {\n for (const priceId of product.prices.keys()) {\n invariant(!existing.prices.has(priceId), `Duplicate price ${priceId} on ${existing.id}`);\n }\n }\n if (product.plan !== null) {\n for (const existing of this.products.values()) {\n invariant(\n existing.plan !== product.plan || existing.billingPeriod !== product.billingPeriod,\n `Duplicate plan ${product.plan}:${product.billingPeriod}`\n );\n }\n }\n this.products.set(product.id, product);\n return this;\n };\n\n private constructor(options: Options) {\n this.mode = options.mode;\n this.returnUrl = options.returnUrl;\n this.cancelUrl = options.cancelUrl;\n this.successUrl = options.successUrl;\n this.cancellationCouponId = options.cancellationCouponId;\n this.allowPromotionCodes = options.allowPromotionCodes;\n }\n\n static create = <NS extends Lowercase<string>, PE extends string, BP extends string = string>(\n options: Options\n ) => {\n return new StripeConfig<NS, PE, BP>(options);\n };\n\n /**\n * One-time products take no extra args; subscriptions require both plan and billingPeriod.\n * A plan+period pair may be declared on only one product — version its prices in that chain.\n */\n product: {\n <K extends `${NS}.${Lowercase<string>}`>(\n productId: K extends PI ? never : K\n ): Product<NS, PE, BP, PL, K | PI>;\n <K extends `${NS}.${Lowercase<string>}`, L extends PE, P extends BP>(\n productId: K extends PI ? never : K,\n plan: L,\n billingPeriod: `${L}:${P}` extends PL ? never : P\n ): Product<NS, PE, BP, `${L}:${P}` | PL, K | PI>;\n } = (productId: string, plan: PE | null = null, billingPeriod: BP | null = null) =>\n // oxlint-disable-next-line typescript/no-unnecessary-type-assertion -- widens to the overloads' accumulated generics\n new Product(this, productId, plan, billingPeriod) as never;\n\n getPlan = (productId: string): PE => {\n const product = this.products.get(productId);\n invariant(product, `Product not found for ${productId}`);\n invariant(product.plan, `Product ${productId} is not a subscription`);\n return product.plan;\n };\n\n getBillingPeriod = (productId: string): BP => {\n const product = this.products.get(productId);\n invariant(product, `Product not found for ${productId}`);\n invariant(product.billingPeriod, `Product ${productId} is not a subscription`);\n return product.billingPeriod;\n };\n\n getMode = (productId: string): 'payment' | 'subscription' => {\n const product = this.products.get(productId);\n invariant(product, `Product not found for ${productId}`);\n return product.plan === null ? 'payment' : 'subscription';\n };\n\n /** The price new checkouts are created with: the default, or its test twin in test mode. */\n getPriceId = (productId: string): PriceId => {\n const product = this.products.get(productId);\n invariant(product, `Product not found for ${productId}`);\n invariant(product.defaultPriceId, `Default price not found for product ${productId}`);\n return product.defaultPriceId;\n };\n\n /**\n * The product that declares `priceId` (a legacy price or the test twin included), or null for a\n * price this config does not know: one set up by hand in the dashboard, or a test price reaching\n * a live config. Reading the product from the price a Stripe object charged, rather than from\n * metadata that may already name the next plan, is what an invoice should be credited by.\n */\n getProductId = (priceId: string): string | null => {\n for (const product of this.products.values()) {\n if (product.prices.has(priceId as PriceId)) return product.id;\n }\n return null;\n };\n\n getCreditAmount = (productId: string, priceId?: string): number => {\n const product = this.products.get(productId);\n invariant(product, `Product not found for ${productId}`);\n if (priceId) {\n const price = product.prices.get(priceId as PriceId);\n invariant(price, `Price not found for ${priceId}`);\n return price.credits;\n }\n\n invariant(product.defaultPriceId, `Default price not found for product ${productId}`);\n return product.prices.get(product.defaultPriceId)?.credits ?? 0;\n };\n\n private readonly getCreditExpiresIn = (productId: string, priceId?: string): number => {\n const product = this.products.get(productId);\n invariant(product, `Product not found for ${productId}`);\n if (priceId) {\n const price = product.prices.get(priceId as PriceId);\n invariant(price, `Price not found for ${priceId}`);\n return ms(price.expiresIn);\n }\n\n invariant(product.defaultPriceId, `Default price not found for product ${productId}`);\n return ms(product.prices.get(product.defaultPriceId)?.expiresIn ?? '0');\n };\n\n getCreditExpiresAt = (productId: string, priceId?: string): string => {\n const expiresIn = this.getCreditExpiresIn(productId, priceId);\n return new Date(Date.now() + expiresIn).toISOString();\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"config.mjs","names":[],"sources":["../../src/stripe/config.ts"],"sourcesContent":["import { invariant } from '@shware/utils';\nimport ms from 'ms';\nimport type { Metadata } from '../types';\n\nexport type PriceId = `price_${string}`;\n\n/**\n * Which Stripe object space the config addresses. Test and live prices are\n * disjoint, so a config is built for exactly one of them. Derive it from the\n * secret key (`sk_test_` vs `sk_live_`) rather than from the deployment\n * environment: the key is what actually decides which space API calls hit.\n */\nexport type StripeMode = 'live' | 'test';\n\nconst addMethod = Symbol('addMethod');\n\nclass Product<\n NS extends Lowercase<string> = Lowercase<string>,\n PE extends string = string,\n BP extends string = string,\n PL extends string = never,\n PI extends string = never,\n I extends PriceId = never,\n> {\n readonly id: string;\n readonly config: StripeConfig<NS, PE, BP, PL, PI>;\n readonly plan: PE | null;\n readonly billingPeriod: BP | null;\n readonly prices: Map<PriceId, Metadata>;\n\n defaultPriceId: PriceId | null;\n\n constructor(\n config: StripeConfig<NS, PE, BP, PL, PI>,\n id: string,\n plan: PE | null = null,\n billingPeriod: BP | null = null\n ) {\n this.id = id;\n this.config = config;\n this.plan = plan;\n this.billingPeriod = billingPeriod;\n this.prices = new Map();\n this.defaultPriceId = null;\n }\n\n /**\n * Non-default prices stay declared on purpose: they keep resolving for\n * grandfathered subscribers and serve as price-experiment arms. Never\n * create new checkouts with a legacy price unless that is intentional.\n */\n price = <K extends PriceId>(\n priceId: K extends I ? never : K,\n metadata: Metadata = { credits: 0, expiresIn: '0' }\n ): Product<NS, PE, BP, PL, PI, I | K> => {\n invariant(!this.prices.has(priceId), `Duplicate price ${priceId}`);\n this.prices.set(priceId, metadata);\n return this as Product<NS, PE, BP, PL, PI, I | K>;\n };\n\n /**\n * `test` is the sandbox twin of the default price. In test mode it is what\n * checkouts are created with, and it resolves to the default price's\n * entitlement, so credits are declared once and cannot drift between the\n * two modes. Legacy prices need no twin: a sandbox holds no grandfathered\n * subscribers. Live mode ignores it, so a test price reaching a live config\n * still fails as an unknown price instead of being granted an entitlement.\n */\n default = (defaultPriceId: I, twin?: { test: PriceId }) => {\n const metadata = this.prices.get(defaultPriceId);\n invariant(metadata, `Default price ${defaultPriceId} is not declared`);\n if (this.config.mode === 'test') {\n invariant(twin, `Test price not declared for default ${defaultPriceId} of ${this.id}`);\n invariant(!this.prices.has(twin.test), `Duplicate price ${twin.test}`);\n this.prices.set(twin.test, metadata);\n this.defaultPriceId = twin.test;\n } else {\n this.defaultPriceId = defaultPriceId;\n }\n this.config[addMethod](this);\n return this.config;\n };\n}\n\ntype Options = {\n mode: StripeMode;\n returnUrl: string;\n cancelUrl: string;\n successUrl: `${string}session_id={CHECKOUT_SESSION_ID}${string}`;\n cancellationCouponId: string;\n allowPromotionCodes: boolean;\n};\n\nexport class StripeConfig<\n NS extends Lowercase<string> = Lowercase<string>,\n PE extends string = string,\n BP extends string = string,\n PL extends string = never,\n PI extends string = never,\n> {\n private readonly products: Map<string, Product<NS, PE, BP, PL, PI>> = new Map();\n\n public readonly mode: StripeMode;\n public returnUrl: string;\n public cancelUrl: string;\n public successUrl: `${string}session_id={CHECKOUT_SESSION_ID}${string}`;\n public cancellationCouponId: string;\n public allowPromotionCodes: boolean;\n\n get productIds(): string[] {\n return Array.from(this.products.keys());\n }\n\n // runtime mirror of the type-level guards: generic accumulation only\n // protects a single fluent chain, not statement-style re-entry or casts\n [addMethod] = (product: Product<NS, PE, BP, PL, PI>) => {\n invariant(!this.products.has(product.id), `Duplicate product ${product.id}`);\n // A Stripe price belongs to one Stripe product, and getProductId must have one answer.\n for (const existing of this.products.values()) {\n for (const priceId of product.prices.keys()) {\n invariant(!existing.prices.has(priceId), `Duplicate price ${priceId} on ${existing.id}`);\n }\n }\n if (product.plan !== null) {\n for (const existing of this.products.values()) {\n invariant(\n existing.plan !== product.plan || existing.billingPeriod !== product.billingPeriod,\n `Duplicate plan ${product.plan}:${product.billingPeriod}`\n );\n }\n }\n this.products.set(product.id, product);\n return this;\n };\n\n private constructor(options: Options) {\n this.mode = options.mode;\n this.returnUrl = options.returnUrl;\n this.cancelUrl = options.cancelUrl;\n this.successUrl = options.successUrl;\n this.cancellationCouponId = options.cancellationCouponId;\n this.allowPromotionCodes = options.allowPromotionCodes;\n }\n\n static create = <NS extends Lowercase<string>, PE extends string, BP extends string = string>(\n options: Options\n ) => {\n return new StripeConfig<NS, PE, BP>(options);\n };\n\n /**\n * One-time products take no extra args; subscriptions require both plan and billingPeriod.\n * A plan+period pair may be declared on only one product — version its prices in that chain.\n */\n product: {\n <K extends `${NS}.${Lowercase<string>}`>(\n productId: K extends PI ? never : K\n ): Product<NS, PE, BP, PL, K | PI>;\n <K extends `${NS}.${Lowercase<string>}`, L extends PE, P extends BP>(\n productId: K extends PI ? never : K,\n plan: L,\n billingPeriod: `${L}:${P}` extends PL ? never : P\n ): Product<NS, PE, BP, `${L}:${P}` | PL, K | PI>;\n } = (productId: string, plan: PE | null = null, billingPeriod: BP | null = null) =>\n // oxlint-disable-next-line typescript/no-unnecessary-type-assertion -- widens to the overloads' accumulated generics\n new Product(this, productId, plan, billingPeriod) as never;\n\n getPlan = (productId: string): PE => {\n const product = this.products.get(productId);\n invariant(product, `Product not found for ${productId}`);\n invariant(product.plan, `Product ${productId} is not a subscription`);\n return product.plan;\n };\n\n getBillingPeriod = (productId: string): BP => {\n const product = this.products.get(productId);\n invariant(product, `Product not found for ${productId}`);\n invariant(product.billingPeriod, `Product ${productId} is not a subscription`);\n return product.billingPeriod;\n };\n\n getMode = (productId: string): 'payment' | 'subscription' => {\n const product = this.products.get(productId);\n invariant(product, `Product not found for ${productId}`);\n return product.plan === null ? 'payment' : 'subscription';\n };\n\n /**\n * Whether the config declares `productId` and sells it as a subscription, so `getPlan` and\n * `getBillingPeriod` hold for it. False, not a throw, for an id it does not know: a product that\n * left the config (renamed, retired) or one named by metadata set outside the app.\n */\n isSubscription = (productId: string): boolean => {\n const product = this.products.get(productId);\n return product !== undefined && product.plan !== null;\n };\n\n /** The price new checkouts are created with: the default, or its test twin in test mode. */\n getPriceId = (productId: string): PriceId => {\n const product = this.products.get(productId);\n invariant(product, `Product not found for ${productId}`);\n invariant(product.defaultPriceId, `Default price not found for product ${productId}`);\n return product.defaultPriceId;\n };\n\n /**\n * The product that declares `priceId` (a legacy price or the test twin included), or null for a\n * price this config does not know: one set up by hand in the dashboard, or a test price reaching\n * a live config. Reading the product from the price a Stripe object charged, rather than from\n * metadata that may already name the next plan, is what an invoice should be credited by.\n */\n getProductId = (priceId: string): string | null => {\n for (const product of this.products.values()) {\n if (product.prices.has(priceId as PriceId)) return product.id;\n }\n return null;\n };\n\n getCreditAmount = (productId: string, priceId?: string): number => {\n const product = this.products.get(productId);\n invariant(product, `Product not found for ${productId}`);\n if (priceId) {\n const price = product.prices.get(priceId as PriceId);\n invariant(price, `Price not found for ${priceId}`);\n return price.credits;\n }\n\n invariant(product.defaultPriceId, `Default price not found for product ${productId}`);\n return product.prices.get(product.defaultPriceId)?.credits ?? 0;\n };\n\n private readonly getCreditExpiresIn = (productId: string, priceId?: string): number => {\n const product = this.products.get(productId);\n invariant(product, `Product not found for ${productId}`);\n if (priceId) {\n const price = product.prices.get(priceId as PriceId);\n invariant(price, `Price not found for ${priceId}`);\n return ms(price.expiresIn);\n }\n\n invariant(product.defaultPriceId, `Default price not found for product ${productId}`);\n return ms(product.prices.get(product.defaultPriceId)?.expiresIn ?? '0');\n };\n\n getCreditExpiresAt = (productId: string, priceId?: string): string => {\n const expiresIn = this.getCreditExpiresIn(productId, priceId);\n return new Date(Date.now() + expiresIn).toISOString();\n };\n}\n"],"mappings":";;;AAcA,MAAM,YAAY,OAAO,WAAW;AAEpC,IAAM,UAAN,MAOE;CACA;CACA;CACA;CACA;CACA;CAEA;CAEA,YACE,QACA,IACA,OAAkB,MAClB,gBAA2B,MAC3B;EACA,KAAK,KAAK;EACV,KAAK,SAAS;EACd,KAAK,OAAO;EACZ,KAAK,gBAAgB;EACrB,KAAK,yBAAS,IAAI,IAAI;EACtB,KAAK,iBAAiB;CACxB;;;;;;CAOA,SACE,SACA,WAAqB;EAAE,SAAS;EAAG,WAAW;CAAI,MACX;EACvC,UAAU,CAAC,KAAK,OAAO,IAAI,OAAO,GAAG,mBAAmB,SAAS;EACjE,KAAK,OAAO,IAAI,SAAS,QAAQ;EACjC,OAAO;CACT;;;;;;;;;CAUA,WAAW,gBAAmB,SAA6B;EACzD,MAAM,WAAW,KAAK,OAAO,IAAI,cAAc;EAC/C,UAAU,UAAU,iBAAiB,eAAe,iBAAiB;EACrE,IAAI,KAAK,OAAO,SAAS,QAAQ;GAC/B,UAAU,MAAM,uCAAuC,eAAe,MAAM,KAAK,IAAI;GACrF,UAAU,CAAC,KAAK,OAAO,IAAI,KAAK,IAAI,GAAG,mBAAmB,KAAK,MAAM;GACrE,KAAK,OAAO,IAAI,KAAK,MAAM,QAAQ;GACnC,KAAK,iBAAiB,KAAK;EAC7B,OACE,KAAK,iBAAiB;EAExB,KAAK,OAAO,UAAU,CAAC,IAAI;EAC3B,OAAO,KAAK;CACd;AACF;AAWA,IAAa,eAAb,MAAa,aAMX;CACA,2BAAsE,IAAI,IAAI;CAE9E;CACA;CACA;CACA;CACA;CACA;CAEA,IAAI,aAAuB;EACzB,OAAO,MAAM,KAAK,KAAK,SAAS,KAAK,CAAC;CACxC;CAIA,CAAC,cAAc,YAAyC;EACtD,UAAU,CAAC,KAAK,SAAS,IAAI,QAAQ,EAAE,GAAG,qBAAqB,QAAQ,IAAI;EAE3E,KAAK,MAAM,YAAY,KAAK,SAAS,OAAO,GAC1C,KAAK,MAAM,WAAW,QAAQ,OAAO,KAAK,GACxC,UAAU,CAAC,SAAS,OAAO,IAAI,OAAO,GAAG,mBAAmB,QAAQ,MAAM,SAAS,IAAI;EAG3F,IAAI,QAAQ,SAAS,MACnB,KAAK,MAAM,YAAY,KAAK,SAAS,OAAO,GAC1C,UACE,SAAS,SAAS,QAAQ,QAAQ,SAAS,kBAAkB,QAAQ,eACrE,kBAAkB,QAAQ,KAAK,GAAG,QAAQ,eAC5C;EAGJ,KAAK,SAAS,IAAI,QAAQ,IAAI,OAAO;EACrC,OAAO;CACT;CAEA,YAAoB,SAAkB;EACpC,KAAK,OAAO,QAAQ;EACpB,KAAK,YAAY,QAAQ;EACzB,KAAK,YAAY,QAAQ;EACzB,KAAK,aAAa,QAAQ;EAC1B,KAAK,uBAAuB,QAAQ;EACpC,KAAK,sBAAsB,QAAQ;CACrC;CAEA,OAAO,UACL,YACG;EACH,OAAO,IAAI,aAAyB,OAAO;CAC7C;;;;;CAMA,WASK,WAAmB,OAAkB,MAAM,gBAA2B,SAEzE,IAAI,QAAQ,MAAM,WAAW,MAAM,aAAa;CAElD,WAAW,cAA0B;EACnC,MAAM,UAAU,KAAK,SAAS,IAAI,SAAS;EAC3C,UAAU,SAAS,yBAAyB,WAAW;EACvD,UAAU,QAAQ,MAAM,WAAW,UAAU,uBAAuB;EACpE,OAAO,QAAQ;CACjB;CAEA,oBAAoB,cAA0B;EAC5C,MAAM,UAAU,KAAK,SAAS,IAAI,SAAS;EAC3C,UAAU,SAAS,yBAAyB,WAAW;EACvD,UAAU,QAAQ,eAAe,WAAW,UAAU,uBAAuB;EAC7E,OAAO,QAAQ;CACjB;CAEA,WAAW,cAAkD;EAC3D,MAAM,UAAU,KAAK,SAAS,IAAI,SAAS;EAC3C,UAAU,SAAS,yBAAyB,WAAW;EACvD,OAAO,QAAQ,SAAS,OAAO,YAAY;CAC7C;;;;;;CAOA,kBAAkB,cAA+B;EAC/C,MAAM,UAAU,KAAK,SAAS,IAAI,SAAS;EAC3C,OAAO,YAAY,KAAA,KAAa,QAAQ,SAAS;CACnD;;CAGA,cAAc,cAA+B;EAC3C,MAAM,UAAU,KAAK,SAAS,IAAI,SAAS;EAC3C,UAAU,SAAS,yBAAyB,WAAW;EACvD,UAAU,QAAQ,gBAAgB,uCAAuC,WAAW;EACpF,OAAO,QAAQ;CACjB;;;;;;;CAQA,gBAAgB,YAAmC;EACjD,KAAK,MAAM,WAAW,KAAK,SAAS,OAAO,GACzC,IAAI,QAAQ,OAAO,IAAI,OAAkB,GAAG,OAAO,QAAQ;EAE7D,OAAO;CACT;CAEA,mBAAmB,WAAmB,YAA6B;EACjE,MAAM,UAAU,KAAK,SAAS,IAAI,SAAS;EAC3C,UAAU,SAAS,yBAAyB,WAAW;EACvD,IAAI,SAAS;GACX,MAAM,QAAQ,QAAQ,OAAO,IAAI,OAAkB;GACnD,UAAU,OAAO,uBAAuB,SAAS;GACjD,OAAO,MAAM;EACf;EAEA,UAAU,QAAQ,gBAAgB,uCAAuC,WAAW;EACpF,OAAO,QAAQ,OAAO,IAAI,QAAQ,cAAc,CAAC,EAAE,WAAW;CAChE;CAEA,sBAAuC,WAAmB,YAA6B;EACrF,MAAM,UAAU,KAAK,SAAS,IAAI,SAAS;EAC3C,UAAU,SAAS,yBAAyB,WAAW;EACvD,IAAI,SAAS;GACX,MAAM,QAAQ,QAAQ,OAAO,IAAI,OAAkB;GACnD,UAAU,OAAO,uBAAuB,SAAS;GACjD,OAAO,GAAG,MAAM,SAAS;EAC3B;EAEA,UAAU,QAAQ,gBAAgB,uCAAuC,WAAW;EACpF,OAAO,GAAG,QAAQ,OAAO,IAAI,QAAQ,cAAc,CAAC,EAAE,aAAa,GAAG;CACxE;CAEA,sBAAsB,WAAmB,YAA6B;EACpE,MAAM,YAAY,KAAK,mBAAmB,WAAW,OAAO;EAC5D,OAAO,IAAI,KAAK,KAAK,IAAI,IAAI,SAAS,CAAC,CAAC,YAAY;CACtD;AACF"}
|
package/dist/stripe/index.cjs
CHANGED
|
@@ -4,22 +4,27 @@ const require_stripe_decline = require("./decline.cjs");
|
|
|
4
4
|
const require_stripe_expandable = require("./expandable.cjs");
|
|
5
5
|
const require_stripe_handler = require("./handler.cjs");
|
|
6
6
|
const require_stripe_mapper = require("./mapper.cjs");
|
|
7
|
+
const require_stripe_subscription = require("./subscription.cjs");
|
|
8
|
+
const require_stripe_payment_method = require("./payment-method.cjs");
|
|
7
9
|
const require_stripe_thin_handler = require("./thin-handler.cjs");
|
|
8
10
|
const require_stripe_schema = require("./schema.cjs");
|
|
9
11
|
const require_stripe_metadata = require("./metadata.cjs");
|
|
10
12
|
const require_stripe_pending_checkout = require("./pending-checkout.cjs");
|
|
11
13
|
exports.DECLINE_REASONS = require_stripe_decline.DECLINE_REASONS;
|
|
14
|
+
exports.DELAYED_NOTIFICATION_PAYMENT_METHOD_TYPES = require_stripe_payment_method.DELAYED_NOTIFICATION_PAYMENT_METHOD_TYPES;
|
|
12
15
|
exports.METADATA_KEYS = require_stripe_metadata.METADATA_KEYS;
|
|
13
16
|
exports.StripeConfig = require_stripe_config.StripeConfig;
|
|
14
17
|
exports.StripeEventHandler = require_stripe_handler.StripeEventHandler;
|
|
15
18
|
exports.StripeThinEventHandler = require_stripe_thin_handler.StripeThinEventHandler;
|
|
16
19
|
exports.addPendingCheckout = require_stripe_pending_checkout.addPendingCheckout;
|
|
20
|
+
exports.cancellationComment = require_stripe_mapper.cancellationComment;
|
|
17
21
|
exports.cancellationDetailsSchema = require_stripe_schema.cancellationDetailsSchema;
|
|
18
22
|
exports.createCheckoutSessionSchema = require_stripe_schema.createCheckoutSessionSchema;
|
|
19
23
|
exports.expanded = require_stripe_expandable.expanded;
|
|
20
24
|
exports.getBeginCheckoutProperties = require_stripe_mapper.getBeginCheckoutProperties;
|
|
21
25
|
exports.getPendingCheckouts = require_stripe_pending_checkout.getPendingCheckouts;
|
|
22
26
|
exports.getPurchaseProperties = require_stripe_mapper.getPurchaseProperties;
|
|
27
|
+
exports.hasDelayedNotification = require_stripe_payment_method.hasDelayedNotification;
|
|
23
28
|
exports.idOf = require_stripe_expandable.idOf;
|
|
24
29
|
exports.mapCancellationDetails = require_stripe_mapper.mapCancellationDetails;
|
|
25
30
|
exports.mapCharge = require_stripe_mapper.mapCharge;
|
|
@@ -28,10 +33,12 @@ exports.mapDecline = require_stripe_decline.mapDecline;
|
|
|
28
33
|
exports.mapDisputeStatus = require_stripe_mapper.mapDisputeStatus;
|
|
29
34
|
exports.mapInvoice = require_stripe_mapper.mapInvoice;
|
|
30
35
|
exports.mapLineItem = require_stripe_mapper.mapLineItem;
|
|
36
|
+
exports.mapListPrice = require_stripe_subscription.mapListPrice;
|
|
31
37
|
exports.mapPaymentIntent = require_stripe_mapper.mapPaymentIntent;
|
|
32
38
|
exports.mapPaymentType = require_stripe_mapper.mapPaymentType;
|
|
33
39
|
exports.mapPrice = require_stripe_mapper.mapPrice;
|
|
34
40
|
exports.mapRefundStatus = require_stripe_mapper.mapRefundStatus;
|
|
41
|
+
exports.mapSubscription = require_stripe_subscription.mapSubscription;
|
|
35
42
|
exports.mapSubscriptionStatus = require_stripe_mapper.mapSubscriptionStatus;
|
|
36
43
|
exports.mapTime = require_stripe_mapper.mapTime;
|
|
37
44
|
exports.minorUnits = require_stripe_mapper.minorUnits;
|
package/dist/stripe/index.d.cts
CHANGED
|
@@ -3,10 +3,12 @@ import { PriceId, StripeConfig, StripeMode } from "./config.cjs";
|
|
|
3
3
|
import { CardFailure, DECLINE_REASONS, Decline, DeclineReason, mapDecline } from "./decline.cjs";
|
|
4
4
|
import { expanded, idOf } from "./expandable.cjs";
|
|
5
5
|
import { StripeEventHandler } from "./handler.cjs";
|
|
6
|
-
import { BeginCheckoutProperties, CheckoutSession, LineItem, Money, PaymentStatus, Price, ProductPrice, PurchaseProperties, getBeginCheckoutProperties, getPurchaseProperties, mapCancellationDetails, mapCharge, mapCheckoutSession, mapDisputeStatus, mapInvoice, mapLineItem, mapPaymentIntent, mapPaymentType, mapPrice, mapRefundStatus, mapSubscriptionStatus, mapTime, minorUnits, money, price } from "./mapper.cjs";
|
|
6
|
+
import { BeginCheckoutProperties, CheckoutSession, LineItem, Money, PaymentStatus, Price, ProductPrice, PurchaseProperties, cancellationComment, getBeginCheckoutProperties, getPurchaseProperties, mapCancellationDetails, mapCharge, mapCheckoutSession, mapDisputeStatus, mapInvoice, mapLineItem, mapPaymentIntent, mapPaymentType, mapPrice, mapRefundStatus, mapSubscriptionStatus, mapTime, minorUnits, money, price } from "./mapper.cjs";
|
|
7
|
+
import { StripeSubscriptionState, mapListPrice, mapSubscription } from "./subscription.cjs";
|
|
8
|
+
import { DELAYED_NOTIFICATION_PAYMENT_METHOD_TYPES, hasDelayedNotification } from "./payment-method.cjs";
|
|
7
9
|
import { StripeThinEventHandler, StripeThinEventNotificationOf, StripeThinEventType } from "./thin-handler.cjs";
|
|
8
10
|
import { AllowedOrigins, CreateCheckoutSessionDTO, CreateCheckoutSessionSchemaOptions, cancellationDetailsSchema, createCheckoutSessionSchema, returnUrlSchema } from "./schema.cjs";
|
|
9
11
|
import { METADATA_KEYS } from "./metadata.cjs";
|
|
10
12
|
import { addPendingCheckout, getPendingCheckouts, removePendingCheckout } from "./pending-checkout.cjs";
|
|
11
13
|
import { Stripe } from "stripe";
|
|
12
|
-
export { type AllowedOrigins, type BeginCheckoutProperties, type CancellationDetails, type CardFailure, type CheckoutSession, type CreateCheckoutSessionDTO, type CreateCheckoutSessionSchemaOptions, DECLINE_REASONS, type Decline, type DeclineReason, type LineItem, METADATA_KEYS, type Money, type PaymentStatus, type Price, type PriceId, type ProductPrice, type PurchaseProperties, type Stripe, StripeConfig, StripeEventHandler, type StripeMode, StripeThinEventHandler, type StripeThinEventNotificationOf, type StripeThinEventType, addPendingCheckout, cancellationDetailsSchema, createCheckoutSessionSchema, expanded, getBeginCheckoutProperties, getPendingCheckouts, getPurchaseProperties, idOf, mapCancellationDetails, mapCharge, mapCheckoutSession, mapDecline, mapDisputeStatus, mapInvoice, mapLineItem, mapPaymentIntent, mapPaymentType, mapPrice, mapRefundStatus, mapSubscriptionStatus, mapTime, minorUnits, money, price, removePendingCheckout, returnUrlSchema };
|
|
14
|
+
export { type AllowedOrigins, type BeginCheckoutProperties, type CancellationDetails, type CardFailure, type CheckoutSession, type CreateCheckoutSessionDTO, type CreateCheckoutSessionSchemaOptions, DECLINE_REASONS, DELAYED_NOTIFICATION_PAYMENT_METHOD_TYPES, type Decline, type DeclineReason, type LineItem, METADATA_KEYS, type Money, type PaymentStatus, type Price, type PriceId, type ProductPrice, type PurchaseProperties, type Stripe, StripeConfig, StripeEventHandler, type StripeMode, type StripeSubscriptionState, StripeThinEventHandler, type StripeThinEventNotificationOf, type StripeThinEventType, addPendingCheckout, cancellationComment, cancellationDetailsSchema, createCheckoutSessionSchema, expanded, getBeginCheckoutProperties, getPendingCheckouts, getPurchaseProperties, hasDelayedNotification, idOf, mapCancellationDetails, mapCharge, mapCheckoutSession, mapDecline, mapDisputeStatus, mapInvoice, mapLineItem, mapListPrice, mapPaymentIntent, mapPaymentType, mapPrice, mapRefundStatus, mapSubscription, mapSubscriptionStatus, mapTime, minorUnits, money, price, removePendingCheckout, returnUrlSchema };
|
package/dist/stripe/index.d.mts
CHANGED
|
@@ -3,10 +3,12 @@ import { PriceId, StripeConfig, StripeMode } from "./config.mjs";
|
|
|
3
3
|
import { CardFailure, DECLINE_REASONS, Decline, DeclineReason, mapDecline } from "./decline.mjs";
|
|
4
4
|
import { expanded, idOf } from "./expandable.mjs";
|
|
5
5
|
import { StripeEventHandler } from "./handler.mjs";
|
|
6
|
-
import { BeginCheckoutProperties, CheckoutSession, LineItem, Money, PaymentStatus, Price, ProductPrice, PurchaseProperties, getBeginCheckoutProperties, getPurchaseProperties, mapCancellationDetails, mapCharge, mapCheckoutSession, mapDisputeStatus, mapInvoice, mapLineItem, mapPaymentIntent, mapPaymentType, mapPrice, mapRefundStatus, mapSubscriptionStatus, mapTime, minorUnits, money, price } from "./mapper.mjs";
|
|
6
|
+
import { BeginCheckoutProperties, CheckoutSession, LineItem, Money, PaymentStatus, Price, ProductPrice, PurchaseProperties, cancellationComment, getBeginCheckoutProperties, getPurchaseProperties, mapCancellationDetails, mapCharge, mapCheckoutSession, mapDisputeStatus, mapInvoice, mapLineItem, mapPaymentIntent, mapPaymentType, mapPrice, mapRefundStatus, mapSubscriptionStatus, mapTime, minorUnits, money, price } from "./mapper.mjs";
|
|
7
|
+
import { StripeSubscriptionState, mapListPrice, mapSubscription } from "./subscription.mjs";
|
|
8
|
+
import { DELAYED_NOTIFICATION_PAYMENT_METHOD_TYPES, hasDelayedNotification } from "./payment-method.mjs";
|
|
7
9
|
import { StripeThinEventHandler, StripeThinEventNotificationOf, StripeThinEventType } from "./thin-handler.mjs";
|
|
8
10
|
import { AllowedOrigins, CreateCheckoutSessionDTO, CreateCheckoutSessionSchemaOptions, cancellationDetailsSchema, createCheckoutSessionSchema, returnUrlSchema } from "./schema.mjs";
|
|
9
11
|
import { METADATA_KEYS } from "./metadata.mjs";
|
|
10
12
|
import { addPendingCheckout, getPendingCheckouts, removePendingCheckout } from "./pending-checkout.mjs";
|
|
11
13
|
import { Stripe } from "stripe";
|
|
12
|
-
export { type AllowedOrigins, type BeginCheckoutProperties, type CancellationDetails, type CardFailure, type CheckoutSession, type CreateCheckoutSessionDTO, type CreateCheckoutSessionSchemaOptions, DECLINE_REASONS, type Decline, type DeclineReason, type LineItem, METADATA_KEYS, type Money, type PaymentStatus, type Price, type PriceId, type ProductPrice, type PurchaseProperties, type Stripe, StripeConfig, StripeEventHandler, type StripeMode, StripeThinEventHandler, type StripeThinEventNotificationOf, type StripeThinEventType, addPendingCheckout, cancellationDetailsSchema, createCheckoutSessionSchema, expanded, getBeginCheckoutProperties, getPendingCheckouts, getPurchaseProperties, idOf, mapCancellationDetails, mapCharge, mapCheckoutSession, mapDecline, mapDisputeStatus, mapInvoice, mapLineItem, mapPaymentIntent, mapPaymentType, mapPrice, mapRefundStatus, mapSubscriptionStatus, mapTime, minorUnits, money, price, removePendingCheckout, returnUrlSchema };
|
|
14
|
+
export { type AllowedOrigins, type BeginCheckoutProperties, type CancellationDetails, type CardFailure, type CheckoutSession, type CreateCheckoutSessionDTO, type CreateCheckoutSessionSchemaOptions, DECLINE_REASONS, DELAYED_NOTIFICATION_PAYMENT_METHOD_TYPES, type Decline, type DeclineReason, type LineItem, METADATA_KEYS, type Money, type PaymentStatus, type Price, type PriceId, type ProductPrice, type PurchaseProperties, type Stripe, StripeConfig, StripeEventHandler, type StripeMode, type StripeSubscriptionState, StripeThinEventHandler, type StripeThinEventNotificationOf, type StripeThinEventType, addPendingCheckout, cancellationComment, cancellationDetailsSchema, createCheckoutSessionSchema, expanded, getBeginCheckoutProperties, getPendingCheckouts, getPurchaseProperties, hasDelayedNotification, idOf, mapCancellationDetails, mapCharge, mapCheckoutSession, mapDecline, mapDisputeStatus, mapInvoice, mapLineItem, mapListPrice, mapPaymentIntent, mapPaymentType, mapPrice, mapRefundStatus, mapSubscription, mapSubscriptionStatus, mapTime, minorUnits, money, price, removePendingCheckout, returnUrlSchema };
|
package/dist/stripe/index.mjs
CHANGED
|
@@ -2,9 +2,11 @@ import { StripeConfig } from "./config.mjs";
|
|
|
2
2
|
import { DECLINE_REASONS, mapDecline } from "./decline.mjs";
|
|
3
3
|
import { expanded, idOf } from "./expandable.mjs";
|
|
4
4
|
import { StripeEventHandler } from "./handler.mjs";
|
|
5
|
-
import { getBeginCheckoutProperties, getPurchaseProperties, mapCancellationDetails, mapCharge, mapCheckoutSession, mapDisputeStatus, mapInvoice, mapLineItem, mapPaymentIntent, mapPaymentType, mapPrice, mapRefundStatus, mapSubscriptionStatus, mapTime, minorUnits, money, price } from "./mapper.mjs";
|
|
5
|
+
import { cancellationComment, getBeginCheckoutProperties, getPurchaseProperties, mapCancellationDetails, mapCharge, mapCheckoutSession, mapDisputeStatus, mapInvoice, mapLineItem, mapPaymentIntent, mapPaymentType, mapPrice, mapRefundStatus, mapSubscriptionStatus, mapTime, minorUnits, money, price } from "./mapper.mjs";
|
|
6
|
+
import { mapListPrice, mapSubscription } from "./subscription.mjs";
|
|
7
|
+
import { DELAYED_NOTIFICATION_PAYMENT_METHOD_TYPES, hasDelayedNotification } from "./payment-method.mjs";
|
|
6
8
|
import { StripeThinEventHandler } from "./thin-handler.mjs";
|
|
7
9
|
import { cancellationDetailsSchema, createCheckoutSessionSchema, returnUrlSchema } from "./schema.mjs";
|
|
8
10
|
import { METADATA_KEYS } from "./metadata.mjs";
|
|
9
11
|
import { addPendingCheckout, getPendingCheckouts, removePendingCheckout } from "./pending-checkout.mjs";
|
|
10
|
-
export { DECLINE_REASONS, METADATA_KEYS, StripeConfig, StripeEventHandler, StripeThinEventHandler, addPendingCheckout, cancellationDetailsSchema, createCheckoutSessionSchema, expanded, getBeginCheckoutProperties, getPendingCheckouts, getPurchaseProperties, idOf, mapCancellationDetails, mapCharge, mapCheckoutSession, mapDecline, mapDisputeStatus, mapInvoice, mapLineItem, mapPaymentIntent, mapPaymentType, mapPrice, mapRefundStatus, mapSubscriptionStatus, mapTime, minorUnits, money, price, removePendingCheckout, returnUrlSchema };
|
|
12
|
+
export { DECLINE_REASONS, DELAYED_NOTIFICATION_PAYMENT_METHOD_TYPES, METADATA_KEYS, StripeConfig, StripeEventHandler, StripeThinEventHandler, addPendingCheckout, cancellationComment, cancellationDetailsSchema, createCheckoutSessionSchema, expanded, getBeginCheckoutProperties, getPendingCheckouts, getPurchaseProperties, hasDelayedNotification, idOf, mapCancellationDetails, mapCharge, mapCheckoutSession, mapDecline, mapDisputeStatus, mapInvoice, mapLineItem, mapListPrice, mapPaymentIntent, mapPaymentType, mapPrice, mapRefundStatus, mapSubscription, mapSubscriptionStatus, mapTime, minorUnits, money, price, removePendingCheckout, returnUrlSchema };
|
package/dist/stripe/mapper.cjs
CHANGED
|
@@ -228,10 +228,28 @@ function getBeginCheckoutProperties(p) {
|
|
|
228
228
|
};
|
|
229
229
|
}
|
|
230
230
|
/**
|
|
231
|
+
* The `cancellation_details.comment` to send Stripe for a cancel dialog's answers. Stripe stores
|
|
232
|
+
* reason, feedback and comment, one field short of `resubscribeIntent`, so the intent travels as a
|
|
233
|
+
* `[maybe]` / `[no]` / `[yes]` prefix on the comment, which {@link mapCancellationDetails} takes
|
|
234
|
+
* back out.
|
|
235
|
+
*/
|
|
236
|
+
function cancellationComment({ comment, resubscribeIntent }) {
|
|
237
|
+
return `${resubscribeIntent ? `[${resubscribeIntent}]` : ""}${comment ?? ""}`;
|
|
238
|
+
}
|
|
239
|
+
function parseCancellationComment(comment) {
|
|
240
|
+
const [, intent, rest] = comment && /^\[(\w+)\](.*)$/s.exec(comment) || [];
|
|
241
|
+
if (!intent || !require_subscription_index.RESUBSCRIBE_INTENTS.includes(intent)) return { comment };
|
|
242
|
+
return {
|
|
243
|
+
comment: rest || null,
|
|
244
|
+
resubscribeIntent: intent
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
231
248
|
* Stripe's `cancellation_details` in the shared shape. The object is present on every
|
|
232
249
|
* subscription with all three fields null, so an active one maps to null rather than an empty
|
|
233
250
|
* record. `reason` says who acted: `cancellation_requested` is the customer (or us on their
|
|
234
|
-
* behalf), the payment reasons are Stripe ending it.
|
|
251
|
+
* behalf), the payment reasons are Stripe ending it. A comment written by
|
|
252
|
+
* {@link cancellationComment} is split back into the comment and `resubscribeIntent`.
|
|
235
253
|
*/
|
|
236
254
|
function mapCancellationDetails(subscription) {
|
|
237
255
|
const details = subscription.cancellation_details;
|
|
@@ -240,7 +258,7 @@ function mapCancellationDetails(subscription) {
|
|
|
240
258
|
initiator: details.reason === "payment_failed" || details.reason === "payment_disputed" ? "system" : "user",
|
|
241
259
|
reason: details.reason ?? null,
|
|
242
260
|
feedback: details.feedback ? require_subscription_index.CANCELLATION_FEEDBACKS.includes(details.feedback) ? details.feedback : "other" : null,
|
|
243
|
-
|
|
261
|
+
...parseCancellationComment(details.comment ?? null)
|
|
244
262
|
};
|
|
245
263
|
}
|
|
246
264
|
//#endregion
|
|
@@ -250,6 +268,7 @@ Object.defineProperty(exports, "ZERO_DECIMAL_CURRENCIES", {
|
|
|
250
268
|
return _shware_utils.ZERO_DECIMAL_CURRENCIES;
|
|
251
269
|
}
|
|
252
270
|
});
|
|
271
|
+
exports.cancellationComment = cancellationComment;
|
|
253
272
|
exports.getBeginCheckoutProperties = getBeginCheckoutProperties;
|
|
254
273
|
exports.getPurchaseProperties = getPurchaseProperties;
|
|
255
274
|
exports.mapCancellationDetails = mapCancellationDetails;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mapper.cjs","names":["DISPUTE_STATUSES","stripeMinorUnits","CANCELLATION_FEEDBACKS"],"sources":["../../src/stripe/mapper.ts"],"sourcesContent":["import { stripeMinorUnits } from '@shware/utils';\nimport type { Stripe } from 'stripe';\nimport {\n CANCELLATION_FEEDBACKS,\n type CancellationDetails,\n type CancellationFeedback,\n DISPUTE_STATUSES,\n type DisputeStatus,\n type PaymentType,\n type RefundStatus,\n type SubscriptionStatus,\n} from '../subscription/index';\n\nexport function mapTime<T extends number | null>(\n stripeTimestampSeconds: T\n): T extends number ? string : null {\n if (!stripeTimestampSeconds) return null as T extends number ? string : null;\n return new Date(stripeTimestampSeconds * 1000).toISOString() as T extends number ? string : null;\n}\n\nexport type Price = {\n id: string;\n type: Stripe.Price.Type;\n active: boolean;\n billing_scheme: Stripe.Price.BillingScheme;\n currency: Stripe.Price['currency'];\n unit_amount: number | null;\n unit_amount_decimal: string | null;\n recurring: Stripe.Price.Recurring | null;\n};\n\nexport function mapPrice(price: Stripe.Price): Price {\n return {\n id: price.id,\n type: price.type,\n active: price.active,\n billing_scheme: price.billing_scheme,\n currency: price.currency,\n unit_amount: price.unit_amount,\n unit_amount_decimal: price.unit_amount_decimal?.toString() ?? null,\n recurring: price.recurring,\n };\n}\n\nexport interface LineItem {\n id: string;\n currency: string;\n quantity: number | null;\n description: string | null;\n amount_tax: number;\n amount_total: number;\n amount_subtotal: number;\n amount_discount: number;\n price: Price | null;\n}\n\nexport function mapLineItem(item: Stripe.LineItem): LineItem {\n return {\n id: item.price\n ? typeof item.price.product === 'string'\n ? item.price.product\n : item.price.product.id\n : item.id,\n currency: item.currency,\n quantity: item.quantity,\n description: item.description,\n amount_tax: item.amount_tax,\n amount_total: item.amount_total,\n amount_subtotal: item.amount_subtotal,\n amount_discount: item.amount_discount,\n price: item.price ? mapPrice(item.price) : null,\n };\n}\n\n// Re-exported under our own name so consumers need not reach into stripe's namespace.\n// Open-ended since stripe 22.4: a status the API adds later is not a type error.\nexport type PaymentStatus = Stripe.Checkout.Session.PaymentStatus;\n\n// Declared rather than inferred from mapCheckoutSession: an inferred shape is\n// structural, so a consumer's declaration emit has to spell out payment_status\n// through stripe's internal module path — TS2883 in any package that reaches\n// stripe transitively instead of depending on it (see line_items/price too).\nexport interface CheckoutSession {\n id: string;\n url: string | null;\n coupon: string | undefined;\n livemode: boolean;\n expires_at: number;\n payment_status: PaymentStatus;\n currency: string | null;\n amount_total: number | null;\n line_items: LineItem[] | undefined;\n}\n\nexport function mapCheckoutSession(session: Stripe.Checkout.Session): CheckoutSession {\n let coupon: string | undefined = undefined;\n if (Array.isArray(session.discounts) && session.discounts.length !== 0) {\n const discount = session.discounts[0];\n if (discount.coupon && typeof discount.coupon === 'object') {\n coupon = discount.coupon.id;\n } else if (typeof discount.coupon === 'string') {\n coupon = discount.coupon;\n } else {\n coupon = undefined;\n }\n }\n\n return {\n id: session.id,\n url: session.url,\n coupon,\n livemode: session.livemode,\n expires_at: session.expires_at,\n payment_status: session.payment_status,\n currency: session.currency,\n amount_total: session.amount_total,\n line_items: session.line_items?.data.map(mapLineItem),\n };\n}\n\nexport function mapInvoice(i: Stripe.Invoice) {\n return {\n id: i.id,\n number: i.number,\n total: i.total,\n subtotal: i.subtotal,\n amount_due: i.amount_due,\n amount_paid: i.amount_paid,\n amount_remaining: i.amount_remaining,\n currency: i.currency,\n billing_reason: i.billing_reason,\n hosted_invoice_url: i.hosted_invoice_url,\n invoice_pdf: i.invoice_pdf,\n receipt_number: i.receipt_number,\n status: i.status,\n created: i.created,\n period_start: i.period_start,\n period_end: i.period_end,\n };\n}\n\nexport function mapPaymentIntent(intent: Stripe.PaymentIntent) {\n return {\n id: intent.id,\n amount: intent.amount,\n amount_capturable: intent.amount_capturable,\n amount_received: intent.amount_received,\n currency: intent.currency,\n client_secret: intent.client_secret,\n description: intent.description,\n status: intent.status,\n created: intent.created,\n };\n}\n\nexport function mapCharge(charge: Stripe.Charge) {\n return {\n id: charge.id,\n description: charge.description,\n currency: charge.currency,\n amount: charge.amount,\n amount_captured: charge.amount_captured,\n amount_refunded: charge.amount_refunded,\n receipt_email: charge.receipt_email,\n receipt_number: charge.receipt_number,\n receipt_url: charge.receipt_url,\n status: charge.status,\n created: charge.created,\n payment_intent:\n charge.payment_intent && typeof charge.payment_intent === 'object'\n ? mapPaymentIntent(charge.payment_intent)\n : undefined,\n };\n}\n\nexport type ProductPrice = {\n id: string;\n type: Stripe.Price.Type;\n unit_amount: number;\n currency: Stripe.Price['currency'];\n product: {\n id: Stripe.Product['id'];\n name: Stripe.Product['name'];\n description: Stripe.Product['description'];\n livemode: Stripe.Product['livemode'];\n };\n};\n\nexport function mapSubscriptionStatus(status: Stripe.Subscription.Status): SubscriptionStatus {\n switch (status) {\n case 'active':\n return 'ACTIVE';\n case 'canceled':\n return 'CANCELED';\n case 'incomplete':\n return 'INCOMPLETE';\n case 'incomplete_expired':\n return 'INCOMPLETE_EXPIRED';\n case 'past_due':\n return 'PAST_DUE';\n case 'paused':\n return 'PAUSED';\n case 'trialing':\n return 'TRIALING';\n case 'unpaid':\n return 'UNPAID';\n default: {\n console.error(`Invalid stripe status: ${String(status)}`);\n throw new Error(`Invalid stripe status: ${String(status)}`);\n }\n }\n}\n\n/**\n * What a paid subscription invoice was for, by its `billing_reason`. Only `subscription_cycle` is\n * a renewal: a plan change invoices its proration mid-period (`subscription_update`), and counting\n * that as a renewal would inflate the renewal rate by every upgrade.\n */\nexport function mapPaymentType(\n invoice: Pick<Stripe.Invoice, 'id' | 'billing_reason'>\n): Exclude<PaymentType, 'ONE_TIME'> {\n switch (invoice.billing_reason) {\n // Exactly once per subscription: its first invoice (after a trial, the first paid one is a cycle).\n case 'subscription_create':\n return 'SUBSCRIPTION_START';\n case 'subscription_cycle':\n return 'SUBSCRIPTION_RENEWAL';\n case 'subscription_update':\n return 'SUBSCRIPTION_UPDATE';\n // An invoice made by hand on the subscription (dashboard or API): off-cycle money, not a renewal.\n case 'manual':\n return 'SUBSCRIPTION_UPDATE';\n default:\n // subscription_threshold (metered billing) and the rest are not reasons flat-priced\n // subscriptions produce. Off-cycle is the safe reading: it leaves renewals alone.\n console.warn(`Invoice ${invoice.id} has billing_reason ${invoice.billing_reason}`);\n return 'SUBSCRIPTION_UPDATE';\n }\n}\n\n/** `refund.status` is an untyped string in stripe-node; these are the documented values. */\nconst REFUND_STATUS: Partial<Record<string, RefundStatus>> = {\n pending: 'PENDING',\n requires_action: 'PENDING',\n succeeded: 'SUCCEEDED',\n failed: 'FAILED',\n canceled: 'CANCELED',\n};\n\n/** A Stripe refund's status; a refund that has none yet is pending. Throws on an unknown one. */\nexport function mapRefundStatus(refund: Pick<Stripe.Refund, 'id' | 'status'>): RefundStatus {\n const status = REFUND_STATUS[refund.status ?? 'pending'];\n if (!status) throw new Error(`Unknown refund status ${refund.status}: ${refund.id}`);\n return status;\n}\n\n/** A Stripe dispute's status, upper-cased into {@link DISPUTE_STATUSES}. Throws on an unknown one. */\nexport function mapDisputeStatus(dispute: Pick<Stripe.Dispute, 'id' | 'status'>): DisputeStatus {\n const status = dispute.status.toUpperCase();\n const known = DISPUTE_STATUSES.find((item) => item === status);\n if (!known) throw new Error(`Unknown dispute status ${dispute.status}: ${dispute.id}`);\n return known;\n}\n\nexport { ZERO_DECIMAL_CURRENCIES } from '@shware/utils';\n\n/** How many Stripe API amount units make one unit of `currency` (1, 100 or 1000). */\nexport function minorUnits(currency: string) {\n return stripeMinorUnits(currency);\n}\n\nexport function price(value: number, currency: string) {\n return value / minorUnits(currency);\n}\n\nexport type Money = { amount: number; currency: string };\n\n/** A Stripe amount in minor units (cents, yen, fils) as an amount in `currency` itself. */\nexport function money(value: number, currency: string): Money {\n return { amount: price(value, currency), currency };\n}\n\nexport interface Item {\n item_id: string;\n item_name: string;\n affiliation?: 'Google Store' | (string & {});\n coupon?: string;\n discount?: number;\n index?: number;\n item_brand?: string;\n item_category?: string;\n item_category2?: string;\n item_category3?: string;\n item_category4?: string;\n item_category5?: string;\n item_list_id?: string;\n item_list_name?: string;\n item_variant?: string;\n location_id?: string;\n price?: number;\n quantity?: number;\n}\n\nexport interface PurchaseProperties {\n currency: string;\n value: number;\n transaction_id: string;\n coupon?: string;\n shipping?: number;\n tax?: number;\n items?: Item[];\n}\n\nexport interface BeginCheckoutProperties {\n currency: string;\n value: number;\n coupon?: string;\n items: Item[];\n}\n\n// Google Ads rejects conversion transaction IDs longer than 64 characters.\n// https://support.google.com/google-ads/answer/6386790\nconst GOOGLE_ADS_TRANSACTION_ID_MAX = 64;\n\n/**\n * GA4 purchase events feed Google Ads conversions, whose transaction IDs are\n * capped at 64 characters — but Stripe checkout session ids exceed that:\n * around 2021 they grew from ~32 chars (`cs_live_` + 24 random chars) to ~66\n * (`cs_live_a` + 57), with no announcement. Stripe treats id length/format as\n * opaque and backwards-compatible, guaranteeing only that ids never exceed\n * 255 chars, so they may grow again at any time.\n *\n * Dropping the fixed `cs_live_`/`cs_test_` prefix brings the id under 64\n * while staying reversible: prepend the prefix (livemode tells which) to\n * reconstruct the full session id for Stripe dashboard lookups. The payment\n * intent id (~27 chars) is not a substitute — it only exists on\n * `mode: 'payment'` sessions (subscription-mode sessions carry it on the\n * invoice instead), and its short length is just as unguaranteed as the\n * session id's was.\n *\n * Must stay deterministic: GA4/Google Ads dedupe purchases by\n * transaction_id, and the same session may be tracked from multiple paths.\n */\nfunction toTransactionId(sessionId: string): string {\n const stripped = sessionId.replace(/^cs_(live|test)_/, '');\n if (stripped.length > GOOGLE_ADS_TRANSACTION_ID_MAX) {\n console.warn(\n `transaction_id still exceeds ${GOOGLE_ADS_TRANSACTION_ID_MAX} chars after stripping ` +\n `the prefix — Stripe may have lengthened session ids again; truncating: ${sessionId}`\n );\n }\n return stripped.slice(0, GOOGLE_ADS_TRANSACTION_ID_MAX);\n}\n\nexport function getPurchaseProperties(session: CheckoutSession): PurchaseProperties {\n let value: number;\n let currency: string;\n if (!session.amount_total || !session.currency) {\n value = session.line_items?.reduce((acc, item) => acc + item.amount_total, 0) ?? 0;\n currency = session.line_items?.[0]?.currency ?? 'usd';\n } else {\n value = session.amount_total;\n currency = session.currency;\n }\n\n return {\n transaction_id: toTransactionId(session.id),\n value: price(value, currency),\n currency: currency.toUpperCase(),\n coupon: session.coupon,\n items: session.line_items?.map((item, index) => ({\n index,\n item_id: item.id,\n item_name: item.description ?? '',\n price: price(item.amount_total, item.currency),\n quantity: item.quantity ?? 1,\n discount: price(item.amount_discount, item.currency),\n })),\n };\n}\n\nexport function getBeginCheckoutProperties(p: ProductPrice): BeginCheckoutProperties {\n return {\n currency: p.currency.toUpperCase(),\n value: price(p.unit_amount, p.currency),\n items: [\n {\n item_id: p.product.id,\n item_name: p.product.name,\n price: price(p.unit_amount, p.currency),\n },\n ],\n };\n}\n\n/**\n * Stripe's `cancellation_details` in the shared shape. The object is present on every\n * subscription with all three fields null, so an active one maps to null rather than an empty\n * record. `reason` says who acted: `cancellation_requested` is the customer (or us on their\n * behalf), the payment reasons are Stripe ending it.\n */\nexport function mapCancellationDetails(\n subscription: Pick<Stripe.Subscription, 'cancellation_details'>\n): CancellationDetails | null {\n const details = subscription.cancellation_details;\n if (!details || (!details.reason && !details.feedback && !details.comment)) return null;\n return {\n initiator:\n details.reason === 'payment_failed' || details.reason === 'payment_disputed'\n ? 'system'\n : 'user',\n reason: details.reason ?? null,\n // Stripe's feedback vocabulary is ours; anything it adds later is reported as \"other\".\n feedback: details.feedback\n ? (CANCELLATION_FEEDBACKS as readonly string[]).includes(details.feedback)\n ? (details.feedback as CancellationFeedback)\n : 'other'\n : null,\n comment: details.comment ?? null,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mapper.cjs","names":["DISPUTE_STATUSES","stripeMinorUnits","RESUBSCRIBE_INTENTS","CANCELLATION_FEEDBACKS"],"sources":["../../src/stripe/mapper.ts"],"sourcesContent":["import { stripeMinorUnits } from '@shware/utils';\nimport type { Stripe } from 'stripe';\nimport {\n CANCELLATION_FEEDBACKS,\n type CancellationDetails,\n type CancellationFeedback,\n DISPUTE_STATUSES,\n type DisputeStatus,\n type PaymentType,\n RESUBSCRIBE_INTENTS,\n type RefundStatus,\n type ResubscribeIntent,\n type SubscriptionStatus,\n} from '../subscription/index';\n\nexport function mapTime<T extends number | null>(\n stripeTimestampSeconds: T\n): T extends number ? string : null {\n if (!stripeTimestampSeconds) return null as T extends number ? string : null;\n return new Date(stripeTimestampSeconds * 1000).toISOString() as T extends number ? string : null;\n}\n\nexport type Price = {\n id: string;\n type: Stripe.Price.Type;\n active: boolean;\n billing_scheme: Stripe.Price.BillingScheme;\n currency: Stripe.Price['currency'];\n unit_amount: number | null;\n unit_amount_decimal: string | null;\n recurring: Stripe.Price.Recurring | null;\n};\n\nexport function mapPrice(price: Stripe.Price): Price {\n return {\n id: price.id,\n type: price.type,\n active: price.active,\n billing_scheme: price.billing_scheme,\n currency: price.currency,\n unit_amount: price.unit_amount,\n unit_amount_decimal: price.unit_amount_decimal?.toString() ?? null,\n recurring: price.recurring,\n };\n}\n\nexport interface LineItem {\n id: string;\n currency: string;\n quantity: number | null;\n description: string | null;\n amount_tax: number;\n amount_total: number;\n amount_subtotal: number;\n amount_discount: number;\n price: Price | null;\n}\n\nexport function mapLineItem(item: Stripe.LineItem): LineItem {\n return {\n id: item.price\n ? typeof item.price.product === 'string'\n ? item.price.product\n : item.price.product.id\n : item.id,\n currency: item.currency,\n quantity: item.quantity,\n description: item.description,\n amount_tax: item.amount_tax,\n amount_total: item.amount_total,\n amount_subtotal: item.amount_subtotal,\n amount_discount: item.amount_discount,\n price: item.price ? mapPrice(item.price) : null,\n };\n}\n\n// Re-exported under our own name so consumers need not reach into stripe's namespace.\n// Open-ended since stripe 22.4: a status the API adds later is not a type error.\nexport type PaymentStatus = Stripe.Checkout.Session.PaymentStatus;\n\n// Declared rather than inferred from mapCheckoutSession: an inferred shape is\n// structural, so a consumer's declaration emit has to spell out payment_status\n// through stripe's internal module path — TS2883 in any package that reaches\n// stripe transitively instead of depending on it (see line_items/price too).\nexport interface CheckoutSession {\n id: string;\n url: string | null;\n coupon: string | undefined;\n livemode: boolean;\n expires_at: number;\n payment_status: PaymentStatus;\n currency: string | null;\n amount_total: number | null;\n line_items: LineItem[] | undefined;\n}\n\nexport function mapCheckoutSession(session: Stripe.Checkout.Session): CheckoutSession {\n let coupon: string | undefined = undefined;\n if (Array.isArray(session.discounts) && session.discounts.length !== 0) {\n const discount = session.discounts[0];\n if (discount.coupon && typeof discount.coupon === 'object') {\n coupon = discount.coupon.id;\n } else if (typeof discount.coupon === 'string') {\n coupon = discount.coupon;\n } else {\n coupon = undefined;\n }\n }\n\n return {\n id: session.id,\n url: session.url,\n coupon,\n livemode: session.livemode,\n expires_at: session.expires_at,\n payment_status: session.payment_status,\n currency: session.currency,\n amount_total: session.amount_total,\n line_items: session.line_items?.data.map(mapLineItem),\n };\n}\n\nexport function mapInvoice(i: Stripe.Invoice) {\n return {\n id: i.id,\n number: i.number,\n total: i.total,\n subtotal: i.subtotal,\n amount_due: i.amount_due,\n amount_paid: i.amount_paid,\n amount_remaining: i.amount_remaining,\n currency: i.currency,\n billing_reason: i.billing_reason,\n hosted_invoice_url: i.hosted_invoice_url,\n invoice_pdf: i.invoice_pdf,\n receipt_number: i.receipt_number,\n status: i.status,\n created: i.created,\n period_start: i.period_start,\n period_end: i.period_end,\n };\n}\n\nexport function mapPaymentIntent(intent: Stripe.PaymentIntent) {\n return {\n id: intent.id,\n amount: intent.amount,\n amount_capturable: intent.amount_capturable,\n amount_received: intent.amount_received,\n currency: intent.currency,\n client_secret: intent.client_secret,\n description: intent.description,\n status: intent.status,\n created: intent.created,\n };\n}\n\nexport function mapCharge(charge: Stripe.Charge) {\n return {\n id: charge.id,\n description: charge.description,\n currency: charge.currency,\n amount: charge.amount,\n amount_captured: charge.amount_captured,\n amount_refunded: charge.amount_refunded,\n receipt_email: charge.receipt_email,\n receipt_number: charge.receipt_number,\n receipt_url: charge.receipt_url,\n status: charge.status,\n created: charge.created,\n payment_intent:\n charge.payment_intent && typeof charge.payment_intent === 'object'\n ? mapPaymentIntent(charge.payment_intent)\n : undefined,\n };\n}\n\nexport type ProductPrice = {\n id: string;\n type: Stripe.Price.Type;\n unit_amount: number;\n currency: Stripe.Price['currency'];\n product: {\n id: Stripe.Product['id'];\n name: Stripe.Product['name'];\n description: Stripe.Product['description'];\n livemode: Stripe.Product['livemode'];\n };\n};\n\nexport function mapSubscriptionStatus(status: Stripe.Subscription.Status): SubscriptionStatus {\n switch (status) {\n case 'active':\n return 'ACTIVE';\n case 'canceled':\n return 'CANCELED';\n case 'incomplete':\n return 'INCOMPLETE';\n case 'incomplete_expired':\n return 'INCOMPLETE_EXPIRED';\n case 'past_due':\n return 'PAST_DUE';\n case 'paused':\n return 'PAUSED';\n case 'trialing':\n return 'TRIALING';\n case 'unpaid':\n return 'UNPAID';\n default: {\n console.error(`Invalid stripe status: ${String(status)}`);\n throw new Error(`Invalid stripe status: ${String(status)}`);\n }\n }\n}\n\n/**\n * What a paid subscription invoice was for, by its `billing_reason`. Only `subscription_cycle` is\n * a renewal: a plan change invoices its proration mid-period (`subscription_update`), and counting\n * that as a renewal would inflate the renewal rate by every upgrade.\n */\nexport function mapPaymentType(\n invoice: Pick<Stripe.Invoice, 'id' | 'billing_reason'>\n): Exclude<PaymentType, 'ONE_TIME'> {\n switch (invoice.billing_reason) {\n // Exactly once per subscription: its first invoice (after a trial, the first paid one is a cycle).\n case 'subscription_create':\n return 'SUBSCRIPTION_START';\n case 'subscription_cycle':\n return 'SUBSCRIPTION_RENEWAL';\n case 'subscription_update':\n return 'SUBSCRIPTION_UPDATE';\n // An invoice made by hand on the subscription (dashboard or API): off-cycle money, not a renewal.\n case 'manual':\n return 'SUBSCRIPTION_UPDATE';\n default:\n // subscription_threshold (metered billing) and the rest are not reasons flat-priced\n // subscriptions produce. Off-cycle is the safe reading: it leaves renewals alone.\n console.warn(`Invoice ${invoice.id} has billing_reason ${invoice.billing_reason}`);\n return 'SUBSCRIPTION_UPDATE';\n }\n}\n\n/** `refund.status` is an untyped string in stripe-node; these are the documented values. */\nconst REFUND_STATUS: Partial<Record<string, RefundStatus>> = {\n pending: 'PENDING',\n requires_action: 'PENDING',\n succeeded: 'SUCCEEDED',\n failed: 'FAILED',\n canceled: 'CANCELED',\n};\n\n/** A Stripe refund's status; a refund that has none yet is pending. Throws on an unknown one. */\nexport function mapRefundStatus(refund: Pick<Stripe.Refund, 'id' | 'status'>): RefundStatus {\n const status = REFUND_STATUS[refund.status ?? 'pending'];\n if (!status) throw new Error(`Unknown refund status ${refund.status}: ${refund.id}`);\n return status;\n}\n\n/** A Stripe dispute's status, upper-cased into {@link DISPUTE_STATUSES}. Throws on an unknown one. */\nexport function mapDisputeStatus(dispute: Pick<Stripe.Dispute, 'id' | 'status'>): DisputeStatus {\n const status = dispute.status.toUpperCase();\n const known = DISPUTE_STATUSES.find((item) => item === status);\n if (!known) throw new Error(`Unknown dispute status ${dispute.status}: ${dispute.id}`);\n return known;\n}\n\nexport { ZERO_DECIMAL_CURRENCIES } from '@shware/utils';\n\n/** How many Stripe API amount units make one unit of `currency` (1, 100 or 1000). */\nexport function minorUnits(currency: string) {\n return stripeMinorUnits(currency);\n}\n\nexport function price(value: number, currency: string) {\n return value / minorUnits(currency);\n}\n\nexport type Money = { amount: number; currency: string };\n\n/** A Stripe amount in minor units (cents, yen, fils) as an amount in `currency` itself. */\nexport function money(value: number, currency: string): Money {\n return { amount: price(value, currency), currency };\n}\n\nexport interface Item {\n item_id: string;\n item_name: string;\n affiliation?: 'Google Store' | (string & {});\n coupon?: string;\n discount?: number;\n index?: number;\n item_brand?: string;\n item_category?: string;\n item_category2?: string;\n item_category3?: string;\n item_category4?: string;\n item_category5?: string;\n item_list_id?: string;\n item_list_name?: string;\n item_variant?: string;\n location_id?: string;\n price?: number;\n quantity?: number;\n}\n\nexport interface PurchaseProperties {\n currency: string;\n value: number;\n transaction_id: string;\n coupon?: string;\n shipping?: number;\n tax?: number;\n items?: Item[];\n}\n\nexport interface BeginCheckoutProperties {\n currency: string;\n value: number;\n coupon?: string;\n items: Item[];\n}\n\n// Google Ads rejects conversion transaction IDs longer than 64 characters.\n// https://support.google.com/google-ads/answer/6386790\nconst GOOGLE_ADS_TRANSACTION_ID_MAX = 64;\n\n/**\n * GA4 purchase events feed Google Ads conversions, whose transaction IDs are\n * capped at 64 characters — but Stripe checkout session ids exceed that:\n * around 2021 they grew from ~32 chars (`cs_live_` + 24 random chars) to ~66\n * (`cs_live_a` + 57), with no announcement. Stripe treats id length/format as\n * opaque and backwards-compatible, guaranteeing only that ids never exceed\n * 255 chars, so they may grow again at any time.\n *\n * Dropping the fixed `cs_live_`/`cs_test_` prefix brings the id under 64\n * while staying reversible: prepend the prefix (livemode tells which) to\n * reconstruct the full session id for Stripe dashboard lookups. The payment\n * intent id (~27 chars) is not a substitute — it only exists on\n * `mode: 'payment'` sessions (subscription-mode sessions carry it on the\n * invoice instead), and its short length is just as unguaranteed as the\n * session id's was.\n *\n * Must stay deterministic: GA4/Google Ads dedupe purchases by\n * transaction_id, and the same session may be tracked from multiple paths.\n */\nfunction toTransactionId(sessionId: string): string {\n const stripped = sessionId.replace(/^cs_(live|test)_/, '');\n if (stripped.length > GOOGLE_ADS_TRANSACTION_ID_MAX) {\n console.warn(\n `transaction_id still exceeds ${GOOGLE_ADS_TRANSACTION_ID_MAX} chars after stripping ` +\n `the prefix — Stripe may have lengthened session ids again; truncating: ${sessionId}`\n );\n }\n return stripped.slice(0, GOOGLE_ADS_TRANSACTION_ID_MAX);\n}\n\nexport function getPurchaseProperties(session: CheckoutSession): PurchaseProperties {\n let value: number;\n let currency: string;\n if (!session.amount_total || !session.currency) {\n value = session.line_items?.reduce((acc, item) => acc + item.amount_total, 0) ?? 0;\n currency = session.line_items?.[0]?.currency ?? 'usd';\n } else {\n value = session.amount_total;\n currency = session.currency;\n }\n\n return {\n transaction_id: toTransactionId(session.id),\n value: price(value, currency),\n currency: currency.toUpperCase(),\n coupon: session.coupon,\n items: session.line_items?.map((item, index) => ({\n index,\n item_id: item.id,\n item_name: item.description ?? '',\n price: price(item.amount_total, item.currency),\n quantity: item.quantity ?? 1,\n discount: price(item.amount_discount, item.currency),\n })),\n };\n}\n\nexport function getBeginCheckoutProperties(p: ProductPrice): BeginCheckoutProperties {\n return {\n currency: p.currency.toUpperCase(),\n value: price(p.unit_amount, p.currency),\n items: [\n {\n item_id: p.product.id,\n item_name: p.product.name,\n price: price(p.unit_amount, p.currency),\n },\n ],\n };\n}\n\n/**\n * The `cancellation_details.comment` to send Stripe for a cancel dialog's answers. Stripe stores\n * reason, feedback and comment, one field short of `resubscribeIntent`, so the intent travels as a\n * `[maybe]` / `[no]` / `[yes]` prefix on the comment, which {@link mapCancellationDetails} takes\n * back out.\n */\nexport function cancellationComment({\n comment,\n resubscribeIntent,\n}: Pick<CancellationDetails, 'comment' | 'resubscribeIntent'>): string {\n return `${resubscribeIntent ? `[${resubscribeIntent}]` : ''}${comment ?? ''}`;\n}\n\nfunction parseCancellationComment(\n comment: string | null\n): Pick<CancellationDetails, 'comment' | 'resubscribeIntent'> {\n const [, intent, rest] = (comment && /^\\[(\\w+)\\](.*)$/s.exec(comment)) || [];\n if (!intent || !(RESUBSCRIBE_INTENTS as readonly string[]).includes(intent)) return { comment };\n return { comment: rest || null, resubscribeIntent: intent as ResubscribeIntent };\n}\n\n/**\n * Stripe's `cancellation_details` in the shared shape. The object is present on every\n * subscription with all three fields null, so an active one maps to null rather than an empty\n * record. `reason` says who acted: `cancellation_requested` is the customer (or us on their\n * behalf), the payment reasons are Stripe ending it. A comment written by\n * {@link cancellationComment} is split back into the comment and `resubscribeIntent`.\n */\nexport function mapCancellationDetails(\n subscription: Pick<Stripe.Subscription, 'cancellation_details'>\n): CancellationDetails | null {\n const details = subscription.cancellation_details;\n if (!details || (!details.reason && !details.feedback && !details.comment)) return null;\n return {\n initiator:\n details.reason === 'payment_failed' || details.reason === 'payment_disputed'\n ? 'system'\n : 'user',\n reason: details.reason ?? null,\n // Stripe's feedback vocabulary is ours; anything it adds later is reported as \"other\".\n feedback: details.feedback\n ? (CANCELLATION_FEEDBACKS as readonly string[]).includes(details.feedback)\n ? (details.feedback as CancellationFeedback)\n : 'other'\n : null,\n ...parseCancellationComment(details.comment ?? null),\n };\n}\n"],"mappings":"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"}
|
package/dist/stripe/mapper.d.cts
CHANGED
|
@@ -161,11 +161,19 @@ export interface BeginCheckoutProperties {
|
|
|
161
161
|
}
|
|
162
162
|
export declare function getPurchaseProperties(session: CheckoutSession): PurchaseProperties;
|
|
163
163
|
export declare function getBeginCheckoutProperties(p: ProductPrice): BeginCheckoutProperties;
|
|
164
|
+
/**
|
|
165
|
+
* The `cancellation_details.comment` to send Stripe for a cancel dialog's answers. Stripe stores
|
|
166
|
+
* reason, feedback and comment, one field short of `resubscribeIntent`, so the intent travels as a
|
|
167
|
+
* `[maybe]` / `[no]` / `[yes]` prefix on the comment, which {@link mapCancellationDetails} takes
|
|
168
|
+
* back out.
|
|
169
|
+
*/
|
|
170
|
+
export declare function cancellationComment({ comment, resubscribeIntent }: Pick<CancellationDetails, 'comment' | 'resubscribeIntent'>): string;
|
|
164
171
|
/**
|
|
165
172
|
* Stripe's `cancellation_details` in the shared shape. The object is present on every
|
|
166
173
|
* subscription with all three fields null, so an active one maps to null rather than an empty
|
|
167
174
|
* record. `reason` says who acted: `cancellation_requested` is the customer (or us on their
|
|
168
|
-
* behalf), the payment reasons are Stripe ending it.
|
|
175
|
+
* behalf), the payment reasons are Stripe ending it. A comment written by
|
|
176
|
+
* {@link cancellationComment} is split back into the comment and `resubscribeIntent`.
|
|
169
177
|
*/
|
|
170
178
|
export declare function mapCancellationDetails(subscription: Pick<Stripe.Subscription, 'cancellation_details'>): CancellationDetails | null;
|
|
171
179
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mapper.d.cts","names":[],"sources":["../../src/stripe/mapper.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"mapper.d.cts","names":[],"sources":["../../src/stripe/mapper.ts"],"mappings":";;;;wBAegB,QAAQ,yBACtB,wBAAwB,IACvB;YAKS;EACV;EACA,MAAM,OAAO,MAAM;EACnB;EACA,gBAAgB,OAAO,MAAM;EAC7B,UAAU,OAAO;EACjB;EACA;EACA,WAAW,OAAO,MAAM;;wBAGV,SAAS,OAAO,OAAO,QAAQ;iBAa9B;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,OAAO;;wBAGO,YAAY,MAAM,OAAO,WAAW;YAoBxC,gBAAgB,OAAO,SAAS,QAAQ;iBAMnC;EACf;EACA;EACA;EACA;EACA;EACA,gBAAgB;EAChB;EACA;EACA,YAAY;;wBAGE,mBAAmB,SAAS,OAAO,SAAS,UAAU;wBA0BtD,WAAW,GAAG,OAAO;EAEjC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,gBAAc,OAAA,QAAA;EACd;EACA;EACA;EACA,QAAM,OAAA,QAAA;EACN;EACA;EACA;;wBAIY,iBAAiB,QAAQ,OAAO;EAE5C;EACA;EACA;EACA;EACA;EACA;EACA;EACA,QAAM,OAAA,cAAA;EACN;;wBAIY,UAAU,QAAQ,OAAO;EAErC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,QAAM,OAAA,OAAA;EACN;EACA;;;;;;;;;;;;YAOQ;EACV;EACA,MAAM,OAAO,MAAM;EACnB;EACA,UAAU,OAAO;EACjB;IACE,IAAI,OAAO;IACX,MAAM,OAAO;IACb,aAAa,OAAO;IACpB,UAAU,OAAO;;;wBAIL,sBAAsB,QAAQ,OAAO,aAAa,SAAS;;;;;;wBA8B3D,eACd,SAAS,KAAK,OAAO,oCACpB,QAAQ;;wBA8BK,gBAAgB,QAAQ,KAAK,OAAO,2BAA2B;;wBAO/D,iBAAiB,SAAS,KAAK,OAAO,4BAA4B;;wBAUlE,WAAW;wBAIX,MAAM,eAAe;YAIzB;EAAU;EAAgB;;;wBAGtB,MAAM,eAAe,mBAAmB;iBAIvC;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;iBAGe;EACf;EACA;EACA;EACA;EACA;EACA;EACA,QAAQ;;iBAGO;EACf;EACA;EACA;EACA,OAAO;;wBAqCO,sBAAsB,SAAS,kBAAkB;wBA2BjD,2BAA2B,GAAG,eAAe;;;;;;;wBAoB7C,sBACd,SACA,qBACC,KAAK;;;;;;;;wBAmBQ,uBACd,cAAc,KAAK,OAAO,wCACzB"}
|
package/dist/stripe/mapper.d.mts
CHANGED
|
@@ -161,11 +161,19 @@ export interface BeginCheckoutProperties {
|
|
|
161
161
|
}
|
|
162
162
|
export declare function getPurchaseProperties(session: CheckoutSession): PurchaseProperties;
|
|
163
163
|
export declare function getBeginCheckoutProperties(p: ProductPrice): BeginCheckoutProperties;
|
|
164
|
+
/**
|
|
165
|
+
* The `cancellation_details.comment` to send Stripe for a cancel dialog's answers. Stripe stores
|
|
166
|
+
* reason, feedback and comment, one field short of `resubscribeIntent`, so the intent travels as a
|
|
167
|
+
* `[maybe]` / `[no]` / `[yes]` prefix on the comment, which {@link mapCancellationDetails} takes
|
|
168
|
+
* back out.
|
|
169
|
+
*/
|
|
170
|
+
export declare function cancellationComment({ comment, resubscribeIntent }: Pick<CancellationDetails, 'comment' | 'resubscribeIntent'>): string;
|
|
164
171
|
/**
|
|
165
172
|
* Stripe's `cancellation_details` in the shared shape. The object is present on every
|
|
166
173
|
* subscription with all three fields null, so an active one maps to null rather than an empty
|
|
167
174
|
* record. `reason` says who acted: `cancellation_requested` is the customer (or us on their
|
|
168
|
-
* behalf), the payment reasons are Stripe ending it.
|
|
175
|
+
* behalf), the payment reasons are Stripe ending it. A comment written by
|
|
176
|
+
* {@link cancellationComment} is split back into the comment and `resubscribeIntent`.
|
|
169
177
|
*/
|
|
170
178
|
export declare function mapCancellationDetails(subscription: Pick<Stripe.Subscription, 'cancellation_details'>): CancellationDetails | null;
|
|
171
179
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mapper.d.mts","names":[],"sources":["../../src/stripe/mapper.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"mapper.d.mts","names":[],"sources":["../../src/stripe/mapper.ts"],"mappings":";;;;wBAegB,QAAQ,yBACtB,wBAAwB,IACvB;YAKS;EACV;EACA,MAAM,OAAO,MAAM;EACnB;EACA,gBAAgB,OAAO,MAAM;EAC7B,UAAU,OAAO;EACjB;EACA;EACA,WAAW,OAAO,MAAM;;wBAGV,SAAS,OAAO,OAAO,QAAQ;iBAa9B;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,OAAO;;wBAGO,YAAY,MAAM,OAAO,WAAW;YAoBxC,gBAAgB,OAAO,SAAS,QAAQ;iBAMnC;EACf;EACA;EACA;EACA;EACA;EACA,gBAAgB;EAChB;EACA;EACA,YAAY;;wBAGE,mBAAmB,SAAS,OAAO,SAAS,UAAU;wBA0BtD,WAAW,GAAG,OAAO;EAEjC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,gBAAc,OAAA,QAAA;EACd;EACA;EACA;EACA,QAAM,OAAA,QAAA;EACN;EACA;EACA;;wBAIY,iBAAiB,QAAQ,OAAO;EAE5C;EACA;EACA;EACA;EACA;EACA;EACA;EACA,QAAM,OAAA,cAAA;EACN;;wBAIY,UAAU,QAAQ,OAAO;EAErC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,QAAM,OAAA,OAAA;EACN;EACA;;;;;;;;;;;;YAOQ;EACV;EACA,MAAM,OAAO,MAAM;EACnB;EACA,UAAU,OAAO;EACjB;IACE,IAAI,OAAO;IACX,MAAM,OAAO;IACb,aAAa,OAAO;IACpB,UAAU,OAAO;;;wBAIL,sBAAsB,QAAQ,OAAO,aAAa,SAAS;;;;;;wBA8B3D,eACd,SAAS,KAAK,OAAO,oCACpB,QAAQ;;wBA8BK,gBAAgB,QAAQ,KAAK,OAAO,2BAA2B;;wBAO/D,iBAAiB,SAAS,KAAK,OAAO,4BAA4B;;wBAUlE,WAAW;wBAIX,MAAM,eAAe;YAIzB;EAAU;EAAgB;;;wBAGtB,MAAM,eAAe,mBAAmB;iBAIvC;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;iBAGe;EACf;EACA;EACA;EACA;EACA;EACA;EACA,QAAQ;;iBAGO;EACf;EACA;EACA;EACA,OAAO;;wBAqCO,sBAAsB,SAAS,kBAAkB;wBA2BjD,2BAA2B,GAAG,eAAe;;;;;;;wBAoB7C,sBACd,SACA,qBACC,KAAK;;;;;;;;wBAmBQ,uBACd,cAAc,KAAK,OAAO,wCACzB"}
|
package/dist/stripe/mapper.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CANCELLATION_FEEDBACKS, DISPUTE_STATUSES } from "../subscription/index.mjs";
|
|
1
|
+
import { CANCELLATION_FEEDBACKS, DISPUTE_STATUSES, RESUBSCRIBE_INTENTS } from "../subscription/index.mjs";
|
|
2
2
|
import { ZERO_DECIMAL_CURRENCIES, stripeMinorUnits } from "@shware/utils";
|
|
3
3
|
//#region src/stripe/mapper.ts
|
|
4
4
|
function mapTime(stripeTimestampSeconds) {
|
|
@@ -227,10 +227,28 @@ function getBeginCheckoutProperties(p) {
|
|
|
227
227
|
};
|
|
228
228
|
}
|
|
229
229
|
/**
|
|
230
|
+
* The `cancellation_details.comment` to send Stripe for a cancel dialog's answers. Stripe stores
|
|
231
|
+
* reason, feedback and comment, one field short of `resubscribeIntent`, so the intent travels as a
|
|
232
|
+
* `[maybe]` / `[no]` / `[yes]` prefix on the comment, which {@link mapCancellationDetails} takes
|
|
233
|
+
* back out.
|
|
234
|
+
*/
|
|
235
|
+
function cancellationComment({ comment, resubscribeIntent }) {
|
|
236
|
+
return `${resubscribeIntent ? `[${resubscribeIntent}]` : ""}${comment ?? ""}`;
|
|
237
|
+
}
|
|
238
|
+
function parseCancellationComment(comment) {
|
|
239
|
+
const [, intent, rest] = comment && /^\[(\w+)\](.*)$/s.exec(comment) || [];
|
|
240
|
+
if (!intent || !RESUBSCRIBE_INTENTS.includes(intent)) return { comment };
|
|
241
|
+
return {
|
|
242
|
+
comment: rest || null,
|
|
243
|
+
resubscribeIntent: intent
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
230
247
|
* Stripe's `cancellation_details` in the shared shape. The object is present on every
|
|
231
248
|
* subscription with all three fields null, so an active one maps to null rather than an empty
|
|
232
249
|
* record. `reason` says who acted: `cancellation_requested` is the customer (or us on their
|
|
233
|
-
* behalf), the payment reasons are Stripe ending it.
|
|
250
|
+
* behalf), the payment reasons are Stripe ending it. A comment written by
|
|
251
|
+
* {@link cancellationComment} is split back into the comment and `resubscribeIntent`.
|
|
234
252
|
*/
|
|
235
253
|
function mapCancellationDetails(subscription) {
|
|
236
254
|
const details = subscription.cancellation_details;
|
|
@@ -239,10 +257,10 @@ function mapCancellationDetails(subscription) {
|
|
|
239
257
|
initiator: details.reason === "payment_failed" || details.reason === "payment_disputed" ? "system" : "user",
|
|
240
258
|
reason: details.reason ?? null,
|
|
241
259
|
feedback: details.feedback ? CANCELLATION_FEEDBACKS.includes(details.feedback) ? details.feedback : "other" : null,
|
|
242
|
-
|
|
260
|
+
...parseCancellationComment(details.comment ?? null)
|
|
243
261
|
};
|
|
244
262
|
}
|
|
245
263
|
//#endregion
|
|
246
|
-
export { ZERO_DECIMAL_CURRENCIES, getBeginCheckoutProperties, getPurchaseProperties, mapCancellationDetails, mapCharge, mapCheckoutSession, mapDisputeStatus, mapInvoice, mapLineItem, mapPaymentIntent, mapPaymentType, mapPrice, mapRefundStatus, mapSubscriptionStatus, mapTime, minorUnits, money, price };
|
|
264
|
+
export { ZERO_DECIMAL_CURRENCIES, cancellationComment, getBeginCheckoutProperties, getPurchaseProperties, mapCancellationDetails, mapCharge, mapCheckoutSession, mapDisputeStatus, mapInvoice, mapLineItem, mapPaymentIntent, mapPaymentType, mapPrice, mapRefundStatus, mapSubscriptionStatus, mapTime, minorUnits, money, price };
|
|
247
265
|
|
|
248
266
|
//# sourceMappingURL=mapper.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mapper.mjs","names":[],"sources":["../../src/stripe/mapper.ts"],"sourcesContent":["import { stripeMinorUnits } from '@shware/utils';\nimport type { Stripe } from 'stripe';\nimport {\n CANCELLATION_FEEDBACKS,\n type CancellationDetails,\n type CancellationFeedback,\n DISPUTE_STATUSES,\n type DisputeStatus,\n type PaymentType,\n type RefundStatus,\n type SubscriptionStatus,\n} from '../subscription/index';\n\nexport function mapTime<T extends number | null>(\n stripeTimestampSeconds: T\n): T extends number ? string : null {\n if (!stripeTimestampSeconds) return null as T extends number ? string : null;\n return new Date(stripeTimestampSeconds * 1000).toISOString() as T extends number ? string : null;\n}\n\nexport type Price = {\n id: string;\n type: Stripe.Price.Type;\n active: boolean;\n billing_scheme: Stripe.Price.BillingScheme;\n currency: Stripe.Price['currency'];\n unit_amount: number | null;\n unit_amount_decimal: string | null;\n recurring: Stripe.Price.Recurring | null;\n};\n\nexport function mapPrice(price: Stripe.Price): Price {\n return {\n id: price.id,\n type: price.type,\n active: price.active,\n billing_scheme: price.billing_scheme,\n currency: price.currency,\n unit_amount: price.unit_amount,\n unit_amount_decimal: price.unit_amount_decimal?.toString() ?? null,\n recurring: price.recurring,\n };\n}\n\nexport interface LineItem {\n id: string;\n currency: string;\n quantity: number | null;\n description: string | null;\n amount_tax: number;\n amount_total: number;\n amount_subtotal: number;\n amount_discount: number;\n price: Price | null;\n}\n\nexport function mapLineItem(item: Stripe.LineItem): LineItem {\n return {\n id: item.price\n ? typeof item.price.product === 'string'\n ? item.price.product\n : item.price.product.id\n : item.id,\n currency: item.currency,\n quantity: item.quantity,\n description: item.description,\n amount_tax: item.amount_tax,\n amount_total: item.amount_total,\n amount_subtotal: item.amount_subtotal,\n amount_discount: item.amount_discount,\n price: item.price ? mapPrice(item.price) : null,\n };\n}\n\n// Re-exported under our own name so consumers need not reach into stripe's namespace.\n// Open-ended since stripe 22.4: a status the API adds later is not a type error.\nexport type PaymentStatus = Stripe.Checkout.Session.PaymentStatus;\n\n// Declared rather than inferred from mapCheckoutSession: an inferred shape is\n// structural, so a consumer's declaration emit has to spell out payment_status\n// through stripe's internal module path — TS2883 in any package that reaches\n// stripe transitively instead of depending on it (see line_items/price too).\nexport interface CheckoutSession {\n id: string;\n url: string | null;\n coupon: string | undefined;\n livemode: boolean;\n expires_at: number;\n payment_status: PaymentStatus;\n currency: string | null;\n amount_total: number | null;\n line_items: LineItem[] | undefined;\n}\n\nexport function mapCheckoutSession(session: Stripe.Checkout.Session): CheckoutSession {\n let coupon: string | undefined = undefined;\n if (Array.isArray(session.discounts) && session.discounts.length !== 0) {\n const discount = session.discounts[0];\n if (discount.coupon && typeof discount.coupon === 'object') {\n coupon = discount.coupon.id;\n } else if (typeof discount.coupon === 'string') {\n coupon = discount.coupon;\n } else {\n coupon = undefined;\n }\n }\n\n return {\n id: session.id,\n url: session.url,\n coupon,\n livemode: session.livemode,\n expires_at: session.expires_at,\n payment_status: session.payment_status,\n currency: session.currency,\n amount_total: session.amount_total,\n line_items: session.line_items?.data.map(mapLineItem),\n };\n}\n\nexport function mapInvoice(i: Stripe.Invoice) {\n return {\n id: i.id,\n number: i.number,\n total: i.total,\n subtotal: i.subtotal,\n amount_due: i.amount_due,\n amount_paid: i.amount_paid,\n amount_remaining: i.amount_remaining,\n currency: i.currency,\n billing_reason: i.billing_reason,\n hosted_invoice_url: i.hosted_invoice_url,\n invoice_pdf: i.invoice_pdf,\n receipt_number: i.receipt_number,\n status: i.status,\n created: i.created,\n period_start: i.period_start,\n period_end: i.period_end,\n };\n}\n\nexport function mapPaymentIntent(intent: Stripe.PaymentIntent) {\n return {\n id: intent.id,\n amount: intent.amount,\n amount_capturable: intent.amount_capturable,\n amount_received: intent.amount_received,\n currency: intent.currency,\n client_secret: intent.client_secret,\n description: intent.description,\n status: intent.status,\n created: intent.created,\n };\n}\n\nexport function mapCharge(charge: Stripe.Charge) {\n return {\n id: charge.id,\n description: charge.description,\n currency: charge.currency,\n amount: charge.amount,\n amount_captured: charge.amount_captured,\n amount_refunded: charge.amount_refunded,\n receipt_email: charge.receipt_email,\n receipt_number: charge.receipt_number,\n receipt_url: charge.receipt_url,\n status: charge.status,\n created: charge.created,\n payment_intent:\n charge.payment_intent && typeof charge.payment_intent === 'object'\n ? mapPaymentIntent(charge.payment_intent)\n : undefined,\n };\n}\n\nexport type ProductPrice = {\n id: string;\n type: Stripe.Price.Type;\n unit_amount: number;\n currency: Stripe.Price['currency'];\n product: {\n id: Stripe.Product['id'];\n name: Stripe.Product['name'];\n description: Stripe.Product['description'];\n livemode: Stripe.Product['livemode'];\n };\n};\n\nexport function mapSubscriptionStatus(status: Stripe.Subscription.Status): SubscriptionStatus {\n switch (status) {\n case 'active':\n return 'ACTIVE';\n case 'canceled':\n return 'CANCELED';\n case 'incomplete':\n return 'INCOMPLETE';\n case 'incomplete_expired':\n return 'INCOMPLETE_EXPIRED';\n case 'past_due':\n return 'PAST_DUE';\n case 'paused':\n return 'PAUSED';\n case 'trialing':\n return 'TRIALING';\n case 'unpaid':\n return 'UNPAID';\n default: {\n console.error(`Invalid stripe status: ${String(status)}`);\n throw new Error(`Invalid stripe status: ${String(status)}`);\n }\n }\n}\n\n/**\n * What a paid subscription invoice was for, by its `billing_reason`. Only `subscription_cycle` is\n * a renewal: a plan change invoices its proration mid-period (`subscription_update`), and counting\n * that as a renewal would inflate the renewal rate by every upgrade.\n */\nexport function mapPaymentType(\n invoice: Pick<Stripe.Invoice, 'id' | 'billing_reason'>\n): Exclude<PaymentType, 'ONE_TIME'> {\n switch (invoice.billing_reason) {\n // Exactly once per subscription: its first invoice (after a trial, the first paid one is a cycle).\n case 'subscription_create':\n return 'SUBSCRIPTION_START';\n case 'subscription_cycle':\n return 'SUBSCRIPTION_RENEWAL';\n case 'subscription_update':\n return 'SUBSCRIPTION_UPDATE';\n // An invoice made by hand on the subscription (dashboard or API): off-cycle money, not a renewal.\n case 'manual':\n return 'SUBSCRIPTION_UPDATE';\n default:\n // subscription_threshold (metered billing) and the rest are not reasons flat-priced\n // subscriptions produce. Off-cycle is the safe reading: it leaves renewals alone.\n console.warn(`Invoice ${invoice.id} has billing_reason ${invoice.billing_reason}`);\n return 'SUBSCRIPTION_UPDATE';\n }\n}\n\n/** `refund.status` is an untyped string in stripe-node; these are the documented values. */\nconst REFUND_STATUS: Partial<Record<string, RefundStatus>> = {\n pending: 'PENDING',\n requires_action: 'PENDING',\n succeeded: 'SUCCEEDED',\n failed: 'FAILED',\n canceled: 'CANCELED',\n};\n\n/** A Stripe refund's status; a refund that has none yet is pending. Throws on an unknown one. */\nexport function mapRefundStatus(refund: Pick<Stripe.Refund, 'id' | 'status'>): RefundStatus {\n const status = REFUND_STATUS[refund.status ?? 'pending'];\n if (!status) throw new Error(`Unknown refund status ${refund.status}: ${refund.id}`);\n return status;\n}\n\n/** A Stripe dispute's status, upper-cased into {@link DISPUTE_STATUSES}. Throws on an unknown one. */\nexport function mapDisputeStatus(dispute: Pick<Stripe.Dispute, 'id' | 'status'>): DisputeStatus {\n const status = dispute.status.toUpperCase();\n const known = DISPUTE_STATUSES.find((item) => item === status);\n if (!known) throw new Error(`Unknown dispute status ${dispute.status}: ${dispute.id}`);\n return known;\n}\n\nexport { ZERO_DECIMAL_CURRENCIES } from '@shware/utils';\n\n/** How many Stripe API amount units make one unit of `currency` (1, 100 or 1000). */\nexport function minorUnits(currency: string) {\n return stripeMinorUnits(currency);\n}\n\nexport function price(value: number, currency: string) {\n return value / minorUnits(currency);\n}\n\nexport type Money = { amount: number; currency: string };\n\n/** A Stripe amount in minor units (cents, yen, fils) as an amount in `currency` itself. */\nexport function money(value: number, currency: string): Money {\n return { amount: price(value, currency), currency };\n}\n\nexport interface Item {\n item_id: string;\n item_name: string;\n affiliation?: 'Google Store' | (string & {});\n coupon?: string;\n discount?: number;\n index?: number;\n item_brand?: string;\n item_category?: string;\n item_category2?: string;\n item_category3?: string;\n item_category4?: string;\n item_category5?: string;\n item_list_id?: string;\n item_list_name?: string;\n item_variant?: string;\n location_id?: string;\n price?: number;\n quantity?: number;\n}\n\nexport interface PurchaseProperties {\n currency: string;\n value: number;\n transaction_id: string;\n coupon?: string;\n shipping?: number;\n tax?: number;\n items?: Item[];\n}\n\nexport interface BeginCheckoutProperties {\n currency: string;\n value: number;\n coupon?: string;\n items: Item[];\n}\n\n// Google Ads rejects conversion transaction IDs longer than 64 characters.\n// https://support.google.com/google-ads/answer/6386790\nconst GOOGLE_ADS_TRANSACTION_ID_MAX = 64;\n\n/**\n * GA4 purchase events feed Google Ads conversions, whose transaction IDs are\n * capped at 64 characters — but Stripe checkout session ids exceed that:\n * around 2021 they grew from ~32 chars (`cs_live_` + 24 random chars) to ~66\n * (`cs_live_a` + 57), with no announcement. Stripe treats id length/format as\n * opaque and backwards-compatible, guaranteeing only that ids never exceed\n * 255 chars, so they may grow again at any time.\n *\n * Dropping the fixed `cs_live_`/`cs_test_` prefix brings the id under 64\n * while staying reversible: prepend the prefix (livemode tells which) to\n * reconstruct the full session id for Stripe dashboard lookups. The payment\n * intent id (~27 chars) is not a substitute — it only exists on\n * `mode: 'payment'` sessions (subscription-mode sessions carry it on the\n * invoice instead), and its short length is just as unguaranteed as the\n * session id's was.\n *\n * Must stay deterministic: GA4/Google Ads dedupe purchases by\n * transaction_id, and the same session may be tracked from multiple paths.\n */\nfunction toTransactionId(sessionId: string): string {\n const stripped = sessionId.replace(/^cs_(live|test)_/, '');\n if (stripped.length > GOOGLE_ADS_TRANSACTION_ID_MAX) {\n console.warn(\n `transaction_id still exceeds ${GOOGLE_ADS_TRANSACTION_ID_MAX} chars after stripping ` +\n `the prefix — Stripe may have lengthened session ids again; truncating: ${sessionId}`\n );\n }\n return stripped.slice(0, GOOGLE_ADS_TRANSACTION_ID_MAX);\n}\n\nexport function getPurchaseProperties(session: CheckoutSession): PurchaseProperties {\n let value: number;\n let currency: string;\n if (!session.amount_total || !session.currency) {\n value = session.line_items?.reduce((acc, item) => acc + item.amount_total, 0) ?? 0;\n currency = session.line_items?.[0]?.currency ?? 'usd';\n } else {\n value = session.amount_total;\n currency = session.currency;\n }\n\n return {\n transaction_id: toTransactionId(session.id),\n value: price(value, currency),\n currency: currency.toUpperCase(),\n coupon: session.coupon,\n items: session.line_items?.map((item, index) => ({\n index,\n item_id: item.id,\n item_name: item.description ?? '',\n price: price(item.amount_total, item.currency),\n quantity: item.quantity ?? 1,\n discount: price(item.amount_discount, item.currency),\n })),\n };\n}\n\nexport function getBeginCheckoutProperties(p: ProductPrice): BeginCheckoutProperties {\n return {\n currency: p.currency.toUpperCase(),\n value: price(p.unit_amount, p.currency),\n items: [\n {\n item_id: p.product.id,\n item_name: p.product.name,\n price: price(p.unit_amount, p.currency),\n },\n ],\n };\n}\n\n/**\n * Stripe's `cancellation_details` in the shared shape. The object is present on every\n * subscription with all three fields null, so an active one maps to null rather than an empty\n * record. `reason` says who acted: `cancellation_requested` is the customer (or us on their\n * behalf), the payment reasons are Stripe ending it.\n */\nexport function mapCancellationDetails(\n subscription: Pick<Stripe.Subscription, 'cancellation_details'>\n): CancellationDetails | null {\n const details = subscription.cancellation_details;\n if (!details || (!details.reason && !details.feedback && !details.comment)) return null;\n return {\n initiator:\n details.reason === 'payment_failed' || details.reason === 'payment_disputed'\n ? 'system'\n : 'user',\n reason: details.reason ?? null,\n // Stripe's feedback vocabulary is ours; anything it adds later is reported as \"other\".\n feedback: details.feedback\n ? (CANCELLATION_FEEDBACKS as readonly string[]).includes(details.feedback)\n ? (details.feedback as CancellationFeedback)\n : 'other'\n : null,\n comment: details.comment ?? null,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mapper.mjs","names":[],"sources":["../../src/stripe/mapper.ts"],"sourcesContent":["import { stripeMinorUnits } from '@shware/utils';\nimport type { Stripe } from 'stripe';\nimport {\n CANCELLATION_FEEDBACKS,\n type CancellationDetails,\n type CancellationFeedback,\n DISPUTE_STATUSES,\n type DisputeStatus,\n type PaymentType,\n RESUBSCRIBE_INTENTS,\n type RefundStatus,\n type ResubscribeIntent,\n type SubscriptionStatus,\n} from '../subscription/index';\n\nexport function mapTime<T extends number | null>(\n stripeTimestampSeconds: T\n): T extends number ? string : null {\n if (!stripeTimestampSeconds) return null as T extends number ? string : null;\n return new Date(stripeTimestampSeconds * 1000).toISOString() as T extends number ? string : null;\n}\n\nexport type Price = {\n id: string;\n type: Stripe.Price.Type;\n active: boolean;\n billing_scheme: Stripe.Price.BillingScheme;\n currency: Stripe.Price['currency'];\n unit_amount: number | null;\n unit_amount_decimal: string | null;\n recurring: Stripe.Price.Recurring | null;\n};\n\nexport function mapPrice(price: Stripe.Price): Price {\n return {\n id: price.id,\n type: price.type,\n active: price.active,\n billing_scheme: price.billing_scheme,\n currency: price.currency,\n unit_amount: price.unit_amount,\n unit_amount_decimal: price.unit_amount_decimal?.toString() ?? null,\n recurring: price.recurring,\n };\n}\n\nexport interface LineItem {\n id: string;\n currency: string;\n quantity: number | null;\n description: string | null;\n amount_tax: number;\n amount_total: number;\n amount_subtotal: number;\n amount_discount: number;\n price: Price | null;\n}\n\nexport function mapLineItem(item: Stripe.LineItem): LineItem {\n return {\n id: item.price\n ? typeof item.price.product === 'string'\n ? item.price.product\n : item.price.product.id\n : item.id,\n currency: item.currency,\n quantity: item.quantity,\n description: item.description,\n amount_tax: item.amount_tax,\n amount_total: item.amount_total,\n amount_subtotal: item.amount_subtotal,\n amount_discount: item.amount_discount,\n price: item.price ? mapPrice(item.price) : null,\n };\n}\n\n// Re-exported under our own name so consumers need not reach into stripe's namespace.\n// Open-ended since stripe 22.4: a status the API adds later is not a type error.\nexport type PaymentStatus = Stripe.Checkout.Session.PaymentStatus;\n\n// Declared rather than inferred from mapCheckoutSession: an inferred shape is\n// structural, so a consumer's declaration emit has to spell out payment_status\n// through stripe's internal module path — TS2883 in any package that reaches\n// stripe transitively instead of depending on it (see line_items/price too).\nexport interface CheckoutSession {\n id: string;\n url: string | null;\n coupon: string | undefined;\n livemode: boolean;\n expires_at: number;\n payment_status: PaymentStatus;\n currency: string | null;\n amount_total: number | null;\n line_items: LineItem[] | undefined;\n}\n\nexport function mapCheckoutSession(session: Stripe.Checkout.Session): CheckoutSession {\n let coupon: string | undefined = undefined;\n if (Array.isArray(session.discounts) && session.discounts.length !== 0) {\n const discount = session.discounts[0];\n if (discount.coupon && typeof discount.coupon === 'object') {\n coupon = discount.coupon.id;\n } else if (typeof discount.coupon === 'string') {\n coupon = discount.coupon;\n } else {\n coupon = undefined;\n }\n }\n\n return {\n id: session.id,\n url: session.url,\n coupon,\n livemode: session.livemode,\n expires_at: session.expires_at,\n payment_status: session.payment_status,\n currency: session.currency,\n amount_total: session.amount_total,\n line_items: session.line_items?.data.map(mapLineItem),\n };\n}\n\nexport function mapInvoice(i: Stripe.Invoice) {\n return {\n id: i.id,\n number: i.number,\n total: i.total,\n subtotal: i.subtotal,\n amount_due: i.amount_due,\n amount_paid: i.amount_paid,\n amount_remaining: i.amount_remaining,\n currency: i.currency,\n billing_reason: i.billing_reason,\n hosted_invoice_url: i.hosted_invoice_url,\n invoice_pdf: i.invoice_pdf,\n receipt_number: i.receipt_number,\n status: i.status,\n created: i.created,\n period_start: i.period_start,\n period_end: i.period_end,\n };\n}\n\nexport function mapPaymentIntent(intent: Stripe.PaymentIntent) {\n return {\n id: intent.id,\n amount: intent.amount,\n amount_capturable: intent.amount_capturable,\n amount_received: intent.amount_received,\n currency: intent.currency,\n client_secret: intent.client_secret,\n description: intent.description,\n status: intent.status,\n created: intent.created,\n };\n}\n\nexport function mapCharge(charge: Stripe.Charge) {\n return {\n id: charge.id,\n description: charge.description,\n currency: charge.currency,\n amount: charge.amount,\n amount_captured: charge.amount_captured,\n amount_refunded: charge.amount_refunded,\n receipt_email: charge.receipt_email,\n receipt_number: charge.receipt_number,\n receipt_url: charge.receipt_url,\n status: charge.status,\n created: charge.created,\n payment_intent:\n charge.payment_intent && typeof charge.payment_intent === 'object'\n ? mapPaymentIntent(charge.payment_intent)\n : undefined,\n };\n}\n\nexport type ProductPrice = {\n id: string;\n type: Stripe.Price.Type;\n unit_amount: number;\n currency: Stripe.Price['currency'];\n product: {\n id: Stripe.Product['id'];\n name: Stripe.Product['name'];\n description: Stripe.Product['description'];\n livemode: Stripe.Product['livemode'];\n };\n};\n\nexport function mapSubscriptionStatus(status: Stripe.Subscription.Status): SubscriptionStatus {\n switch (status) {\n case 'active':\n return 'ACTIVE';\n case 'canceled':\n return 'CANCELED';\n case 'incomplete':\n return 'INCOMPLETE';\n case 'incomplete_expired':\n return 'INCOMPLETE_EXPIRED';\n case 'past_due':\n return 'PAST_DUE';\n case 'paused':\n return 'PAUSED';\n case 'trialing':\n return 'TRIALING';\n case 'unpaid':\n return 'UNPAID';\n default: {\n console.error(`Invalid stripe status: ${String(status)}`);\n throw new Error(`Invalid stripe status: ${String(status)}`);\n }\n }\n}\n\n/**\n * What a paid subscription invoice was for, by its `billing_reason`. Only `subscription_cycle` is\n * a renewal: a plan change invoices its proration mid-period (`subscription_update`), and counting\n * that as a renewal would inflate the renewal rate by every upgrade.\n */\nexport function mapPaymentType(\n invoice: Pick<Stripe.Invoice, 'id' | 'billing_reason'>\n): Exclude<PaymentType, 'ONE_TIME'> {\n switch (invoice.billing_reason) {\n // Exactly once per subscription: its first invoice (after a trial, the first paid one is a cycle).\n case 'subscription_create':\n return 'SUBSCRIPTION_START';\n case 'subscription_cycle':\n return 'SUBSCRIPTION_RENEWAL';\n case 'subscription_update':\n return 'SUBSCRIPTION_UPDATE';\n // An invoice made by hand on the subscription (dashboard or API): off-cycle money, not a renewal.\n case 'manual':\n return 'SUBSCRIPTION_UPDATE';\n default:\n // subscription_threshold (metered billing) and the rest are not reasons flat-priced\n // subscriptions produce. Off-cycle is the safe reading: it leaves renewals alone.\n console.warn(`Invoice ${invoice.id} has billing_reason ${invoice.billing_reason}`);\n return 'SUBSCRIPTION_UPDATE';\n }\n}\n\n/** `refund.status` is an untyped string in stripe-node; these are the documented values. */\nconst REFUND_STATUS: Partial<Record<string, RefundStatus>> = {\n pending: 'PENDING',\n requires_action: 'PENDING',\n succeeded: 'SUCCEEDED',\n failed: 'FAILED',\n canceled: 'CANCELED',\n};\n\n/** A Stripe refund's status; a refund that has none yet is pending. Throws on an unknown one. */\nexport function mapRefundStatus(refund: Pick<Stripe.Refund, 'id' | 'status'>): RefundStatus {\n const status = REFUND_STATUS[refund.status ?? 'pending'];\n if (!status) throw new Error(`Unknown refund status ${refund.status}: ${refund.id}`);\n return status;\n}\n\n/** A Stripe dispute's status, upper-cased into {@link DISPUTE_STATUSES}. Throws on an unknown one. */\nexport function mapDisputeStatus(dispute: Pick<Stripe.Dispute, 'id' | 'status'>): DisputeStatus {\n const status = dispute.status.toUpperCase();\n const known = DISPUTE_STATUSES.find((item) => item === status);\n if (!known) throw new Error(`Unknown dispute status ${dispute.status}: ${dispute.id}`);\n return known;\n}\n\nexport { ZERO_DECIMAL_CURRENCIES } from '@shware/utils';\n\n/** How many Stripe API amount units make one unit of `currency` (1, 100 or 1000). */\nexport function minorUnits(currency: string) {\n return stripeMinorUnits(currency);\n}\n\nexport function price(value: number, currency: string) {\n return value / minorUnits(currency);\n}\n\nexport type Money = { amount: number; currency: string };\n\n/** A Stripe amount in minor units (cents, yen, fils) as an amount in `currency` itself. */\nexport function money(value: number, currency: string): Money {\n return { amount: price(value, currency), currency };\n}\n\nexport interface Item {\n item_id: string;\n item_name: string;\n affiliation?: 'Google Store' | (string & {});\n coupon?: string;\n discount?: number;\n index?: number;\n item_brand?: string;\n item_category?: string;\n item_category2?: string;\n item_category3?: string;\n item_category4?: string;\n item_category5?: string;\n item_list_id?: string;\n item_list_name?: string;\n item_variant?: string;\n location_id?: string;\n price?: number;\n quantity?: number;\n}\n\nexport interface PurchaseProperties {\n currency: string;\n value: number;\n transaction_id: string;\n coupon?: string;\n shipping?: number;\n tax?: number;\n items?: Item[];\n}\n\nexport interface BeginCheckoutProperties {\n currency: string;\n value: number;\n coupon?: string;\n items: Item[];\n}\n\n// Google Ads rejects conversion transaction IDs longer than 64 characters.\n// https://support.google.com/google-ads/answer/6386790\nconst GOOGLE_ADS_TRANSACTION_ID_MAX = 64;\n\n/**\n * GA4 purchase events feed Google Ads conversions, whose transaction IDs are\n * capped at 64 characters — but Stripe checkout session ids exceed that:\n * around 2021 they grew from ~32 chars (`cs_live_` + 24 random chars) to ~66\n * (`cs_live_a` + 57), with no announcement. Stripe treats id length/format as\n * opaque and backwards-compatible, guaranteeing only that ids never exceed\n * 255 chars, so they may grow again at any time.\n *\n * Dropping the fixed `cs_live_`/`cs_test_` prefix brings the id under 64\n * while staying reversible: prepend the prefix (livemode tells which) to\n * reconstruct the full session id for Stripe dashboard lookups. The payment\n * intent id (~27 chars) is not a substitute — it only exists on\n * `mode: 'payment'` sessions (subscription-mode sessions carry it on the\n * invoice instead), and its short length is just as unguaranteed as the\n * session id's was.\n *\n * Must stay deterministic: GA4/Google Ads dedupe purchases by\n * transaction_id, and the same session may be tracked from multiple paths.\n */\nfunction toTransactionId(sessionId: string): string {\n const stripped = sessionId.replace(/^cs_(live|test)_/, '');\n if (stripped.length > GOOGLE_ADS_TRANSACTION_ID_MAX) {\n console.warn(\n `transaction_id still exceeds ${GOOGLE_ADS_TRANSACTION_ID_MAX} chars after stripping ` +\n `the prefix — Stripe may have lengthened session ids again; truncating: ${sessionId}`\n );\n }\n return stripped.slice(0, GOOGLE_ADS_TRANSACTION_ID_MAX);\n}\n\nexport function getPurchaseProperties(session: CheckoutSession): PurchaseProperties {\n let value: number;\n let currency: string;\n if (!session.amount_total || !session.currency) {\n value = session.line_items?.reduce((acc, item) => acc + item.amount_total, 0) ?? 0;\n currency = session.line_items?.[0]?.currency ?? 'usd';\n } else {\n value = session.amount_total;\n currency = session.currency;\n }\n\n return {\n transaction_id: toTransactionId(session.id),\n value: price(value, currency),\n currency: currency.toUpperCase(),\n coupon: session.coupon,\n items: session.line_items?.map((item, index) => ({\n index,\n item_id: item.id,\n item_name: item.description ?? '',\n price: price(item.amount_total, item.currency),\n quantity: item.quantity ?? 1,\n discount: price(item.amount_discount, item.currency),\n })),\n };\n}\n\nexport function getBeginCheckoutProperties(p: ProductPrice): BeginCheckoutProperties {\n return {\n currency: p.currency.toUpperCase(),\n value: price(p.unit_amount, p.currency),\n items: [\n {\n item_id: p.product.id,\n item_name: p.product.name,\n price: price(p.unit_amount, p.currency),\n },\n ],\n };\n}\n\n/**\n * The `cancellation_details.comment` to send Stripe for a cancel dialog's answers. Stripe stores\n * reason, feedback and comment, one field short of `resubscribeIntent`, so the intent travels as a\n * `[maybe]` / `[no]` / `[yes]` prefix on the comment, which {@link mapCancellationDetails} takes\n * back out.\n */\nexport function cancellationComment({\n comment,\n resubscribeIntent,\n}: Pick<CancellationDetails, 'comment' | 'resubscribeIntent'>): string {\n return `${resubscribeIntent ? `[${resubscribeIntent}]` : ''}${comment ?? ''}`;\n}\n\nfunction parseCancellationComment(\n comment: string | null\n): Pick<CancellationDetails, 'comment' | 'resubscribeIntent'> {\n const [, intent, rest] = (comment && /^\\[(\\w+)\\](.*)$/s.exec(comment)) || [];\n if (!intent || !(RESUBSCRIBE_INTENTS as readonly string[]).includes(intent)) return { comment };\n return { comment: rest || null, resubscribeIntent: intent as ResubscribeIntent };\n}\n\n/**\n * Stripe's `cancellation_details` in the shared shape. The object is present on every\n * subscription with all three fields null, so an active one maps to null rather than an empty\n * record. `reason` says who acted: `cancellation_requested` is the customer (or us on their\n * behalf), the payment reasons are Stripe ending it. A comment written by\n * {@link cancellationComment} is split back into the comment and `resubscribeIntent`.\n */\nexport function mapCancellationDetails(\n subscription: Pick<Stripe.Subscription, 'cancellation_details'>\n): CancellationDetails | null {\n const details = subscription.cancellation_details;\n if (!details || (!details.reason && !details.feedback && !details.comment)) return null;\n return {\n initiator:\n details.reason === 'payment_failed' || details.reason === 'payment_disputed'\n ? 'system'\n : 'user',\n reason: details.reason ?? null,\n // Stripe's feedback vocabulary is ours; anything it adds later is reported as \"other\".\n feedback: details.feedback\n ? (CANCELLATION_FEEDBACKS as readonly string[]).includes(details.feedback)\n ? (details.feedback as CancellationFeedback)\n : 'other'\n : null,\n ...parseCancellationComment(details.comment ?? null),\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region src/stripe/payment-method.ts
|
|
3
|
+
/**
|
|
4
|
+
* Reusable payment methods whose outcome arrives days after the charge is attempted: Stripe's
|
|
5
|
+
* "payment methods with delayed notification" (https://docs.stripe.com/payments/payment-methods#payment-notification).
|
|
6
|
+
* A charge to one sits in `processing` until the bank answers, and can still fail then.
|
|
7
|
+
*
|
|
8
|
+
* Only types that can be saved and charged again (a subscription's or customer's default) are
|
|
9
|
+
* listed: the bank debits. One-off vouchers and transfers (Boleto, OXXO, Konbini, bank transfers)
|
|
10
|
+
* also notify late, but never come back as a saved payment method. A deny-list, so instant methods
|
|
11
|
+
* (cards, wallets, Link, PayPal, ...) need no entry; a delayed type Stripe adds later has to be
|
|
12
|
+
* added here.
|
|
13
|
+
*/
|
|
14
|
+
const DELAYED_NOTIFICATION_PAYMENT_METHOD_TYPES = /* @__PURE__ */ new Set([
|
|
15
|
+
"sepa_debit",
|
|
16
|
+
"us_bank_account",
|
|
17
|
+
"bacs_debit",
|
|
18
|
+
"au_becs_debit",
|
|
19
|
+
"acss_debit",
|
|
20
|
+
"nz_bank_account"
|
|
21
|
+
]);
|
|
22
|
+
/**
|
|
23
|
+
* Whether a charge to this payment method type is only known to have succeeded days later. Flows
|
|
24
|
+
* that need the money now (a Stripe pending update, which supports no such method and expires
|
|
25
|
+
* after ~23 hours) should refuse it up front rather than report a late failure.
|
|
26
|
+
*/
|
|
27
|
+
function hasDelayedNotification(type) {
|
|
28
|
+
return DELAYED_NOTIFICATION_PAYMENT_METHOD_TYPES.has(type);
|
|
29
|
+
}
|
|
30
|
+
//#endregion
|
|
31
|
+
exports.DELAYED_NOTIFICATION_PAYMENT_METHOD_TYPES = DELAYED_NOTIFICATION_PAYMENT_METHOD_TYPES;
|
|
32
|
+
exports.hasDelayedNotification = hasDelayedNotification;
|
|
33
|
+
|
|
34
|
+
//# sourceMappingURL=payment-method.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payment-method.cjs","names":[],"sources":["../../src/stripe/payment-method.ts"],"sourcesContent":["import type { Stripe } from 'stripe';\n\n/**\n * Reusable payment methods whose outcome arrives days after the charge is attempted: Stripe's\n * \"payment methods with delayed notification\" (https://docs.stripe.com/payments/payment-methods#payment-notification).\n * A charge to one sits in `processing` until the bank answers, and can still fail then.\n *\n * Only types that can be saved and charged again (a subscription's or customer's default) are\n * listed: the bank debits. One-off vouchers and transfers (Boleto, OXXO, Konbini, bank transfers)\n * also notify late, but never come back as a saved payment method. A deny-list, so instant methods\n * (cards, wallets, Link, PayPal, ...) need no entry; a delayed type Stripe adds later has to be\n * added here.\n */\nexport const DELAYED_NOTIFICATION_PAYMENT_METHOD_TYPES: ReadonlySet<Stripe.PaymentMethod.Type> =\n new Set([\n 'sepa_debit',\n 'us_bank_account',\n 'bacs_debit',\n 'au_becs_debit',\n 'acss_debit',\n 'nz_bank_account',\n ]);\n\n/**\n * Whether a charge to this payment method type is only known to have succeeded days later. Flows\n * that need the money now (a Stripe pending update, which supports no such method and expires\n * after ~23 hours) should refuse it up front rather than report a late failure.\n */\nexport function hasDelayedNotification(type: Stripe.PaymentMethod.Type): boolean {\n return DELAYED_NOTIFICATION_PAYMENT_METHOD_TYPES.has(type);\n}\n"],"mappings":";;;;;;;;;;;;;AAaA,MAAa,4DACX,IAAI,IAAI;CACN;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;;;;;;AAOH,SAAgB,uBAAuB,MAA0C;CAC/E,OAAO,0CAA0C,IAAI,IAAI;AAC3D"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Stripe } from "stripe";
|
|
2
|
+
//#region src/stripe/payment-method.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Reusable payment methods whose outcome arrives days after the charge is attempted: Stripe's
|
|
5
|
+
* "payment methods with delayed notification" (https://docs.stripe.com/payments/payment-methods#payment-notification).
|
|
6
|
+
* A charge to one sits in `processing` until the bank answers, and can still fail then.
|
|
7
|
+
*
|
|
8
|
+
* Only types that can be saved and charged again (a subscription's or customer's default) are
|
|
9
|
+
* listed: the bank debits. One-off vouchers and transfers (Boleto, OXXO, Konbini, bank transfers)
|
|
10
|
+
* also notify late, but never come back as a saved payment method. A deny-list, so instant methods
|
|
11
|
+
* (cards, wallets, Link, PayPal, ...) need no entry; a delayed type Stripe adds later has to be
|
|
12
|
+
* added here.
|
|
13
|
+
*/
|
|
14
|
+
export declare const DELAYED_NOTIFICATION_PAYMENT_METHOD_TYPES: ReadonlySet<Stripe.PaymentMethod.Type>;
|
|
15
|
+
/**
|
|
16
|
+
* Whether a charge to this payment method type is only known to have succeeded days later. Flows
|
|
17
|
+
* that need the money now (a Stripe pending update, which supports no such method and expires
|
|
18
|
+
* after ~23 hours) should refuse it up front rather than report a late failure.
|
|
19
|
+
*/
|
|
20
|
+
export declare function hasDelayedNotification(type: Stripe.PaymentMethod.Type): boolean;
|
|
21
|
+
//#endregion
|
|
22
|
+
//# sourceMappingURL=payment-method.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payment-method.d.cts","names":[],"sources":["../../src/stripe/payment-method.ts"],"mappings":";;;;;;;;;;;;;qBAaa,2CAA2C,YAAY,OAAO,cAAc;;;;;;wBAezE,uBAAuB,MAAM,OAAO,cAAc"}
|