@shware/purchase 4.0.0 → 4.0.2
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/config.cjs.map +1 -1
- package/dist/app-store/config.d.cts +5 -5
- package/dist/app-store/config.d.cts.map +1 -1
- package/dist/app-store/config.d.mts +5 -5
- package/dist/app-store/config.d.mts.map +1 -1
- package/dist/app-store/config.mjs.map +1 -1
- package/dist/app-store/handler.cjs +35 -0
- package/dist/app-store/handler.cjs.map +1 -0
- package/dist/app-store/handler.d.cts +24 -0
- package/dist/app-store/handler.d.cts.map +1 -0
- package/dist/app-store/handler.d.mts +24 -0
- package/dist/app-store/handler.d.mts.map +1 -0
- package/dist/app-store/handler.mjs +34 -0
- package/dist/app-store/handler.mjs.map +1 -0
- package/dist/app-store/index.cjs +2 -0
- package/dist/app-store/index.d.cts +2 -1
- package/dist/app-store/index.d.mts +2 -1
- package/dist/app-store/index.mjs +2 -1
- package/dist/google-play/config.cjs.map +1 -1
- package/dist/google-play/config.d.cts +5 -5
- package/dist/google-play/config.d.cts.map +1 -1
- package/dist/google-play/config.d.mts +5 -5
- package/dist/google-play/config.d.mts.map +1 -1
- package/dist/google-play/config.mjs.map +1 -1
- package/dist/stripe/config.cjs.map +1 -1
- package/dist/stripe/config.d.cts +2 -2
- package/dist/stripe/config.d.cts.map +1 -1
- package/dist/stripe/config.d.mts +2 -2
- package/dist/stripe/config.d.mts.map +1 -1
- package/dist/stripe/config.mjs.map +1 -1
- package/dist/stripe/mapper.cjs +26 -1
- package/dist/stripe/mapper.cjs.map +1 -1
- package/dist/stripe/mapper.d.cts.map +1 -1
- package/dist/stripe/mapper.d.mts.map +1 -1
- package/dist/stripe/mapper.mjs +26 -1
- package/dist/stripe/mapper.mjs.map +1 -1
- package/dist/stripe/schema.d.cts.map +1 -1
- package/dist/stripe/schema.d.mts.map +1 -1
- package/package.json +7 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.cjs","names":[],"sources":["../../src/app-store/config.ts"],"sourcesContent":["import { invariant } from '@shware/utils';\nimport ms from 'ms';\nimport type { Metadata } from '../types';\n\nconst addMethod = Symbol('addMethod');\n\nclass Subscription<\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 string = never,\n> {\n readonly plan: PE;\n readonly billingPeriod: BP;\n readonly config: AppStoreConfig<NS, PE, BP, PL, PI>;\n readonly products: Map<string, Metadata>;\n\n defaultProductId: I | null;\n\n constructor(config: AppStoreConfig<NS, PE, BP, PL, PI>, plan: PE, billingPeriod: BP) {\n this.plan = plan;\n this.billingPeriod = billingPeriod;\n this.config = config;\n this.products = new Map();\n this.defaultProductId = null;\n }\n\n /**\n * Non-default products stay declared on purpose: they keep resolving for\n * grandfathered subscribers and serve as price-experiment arms. Never\n * sell a legacy product to new users unless that is intentional.\n */\n product = <K extends `${NS}.${Lowercase<string>}`>(\n productId: K extends PI ? never : K,\n metadata: Metadata = { credits: 0, expiresIn: '0' }\n ): Subscription<NS, PE, BP, PL, PI | K, I | K> => {\n invariant(!this.products.has(productId), `Duplicate product ${productId}`);\n this.products.set(productId, metadata);\n return this as Subscription<NS, PE, BP, PL, PI | K, I | K>;\n };\n\n default = (defaultProductId: I) => {\n invariant(\n this.products.has(defaultProductId),\n `Default product ${defaultProductId} is not declared`\n );\n this.defaultProductId = defaultProductId;\n this.config[addMethod](this as never);\n return this.config;\n };\n}\n\ntype Options = { appId: `${number}`; bundleId: string };\n\nexport class AppStoreConfig<\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 readonly appId: string;\n readonly bundleId: string;\n\n private products: Map<string, Metadata | null>;\n private consumables: Set<string>;\n private subscriptions: Map<string, Subscription<NS, PE, BP, PL, PI>>;\n private nonConsumables: Set<string>;\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] = (subscription: Subscription<NS, PE, BP, PL, PI>) => {\n const key = `${subscription.plan}:${subscription.billingPeriod}`;\n invariant(!this.subscriptions.has(key), `Duplicate plan ${key}`);\n subscription.products.forEach((metadata, productId) => {\n invariant(!this.products.has(productId), `Duplicate product ${productId}`);\n this.products.set(productId, metadata);\n });\n this.subscriptions.set(key, subscription);\n return this;\n };\n\n private constructor(options: Options) {\n this.appId = options.appId;\n this.bundleId = options.bundleId;\n this.products = new Map();\n this.consumables = new Set();\n this.subscriptions = new Map();\n this.nonConsumables = new Set();\n }\n\n static create = <NS extends Lowercase<string>, PE extends string, BP extends string = string>(\n options: Options\n ) => {\n return new AppStoreConfig<NS, PE, BP>(options);\n };\n\n /** The same plan may be declared once per billing period; duplicate pairs are rejected. */\n subscription = <K extends PE, P extends BP>(\n plan: K,\n billingPeriod: `${K}:${P}` extends PL ? never : P\n ) => {\n // oxlint-disable-next-line typescript/no-unnecessary-type-assertion -- tsc requires the cast (tsgolint false positive)\n return new Subscription<NS, PE, BP, `${K}:${P}` | PL, PI>(this as never, plan, billingPeriod);\n };\n\n consumable = <K extends `${NS}.${Lowercase<string>}`>(\n productId: K extends PI ? never : K,\n metadata: Metadata = { credits: 0, expiresIn: '0' }\n ) => {\n invariant(!this.products.has(productId), `Duplicate product ${productId}`);\n this.consumables.add(productId);\n this.products.set(productId, metadata);\n return this as AppStoreConfig<NS, PE, BP, PL, PI | K>;\n };\n\n nonConsumable = <K extends `${NS}.${Lowercase<string>}`>(productId: K extends PI ? never : K) => {\n invariant(!this.products.has(productId), `Duplicate product ${productId}`);\n this.nonConsumables.add(productId);\n this.products.set(productId, null);\n return this as AppStoreConfig<NS, PE, BP, PL, PI | K>;\n };\n\n /** Product id that new subscribers should purchase for the plan and billing period. */\n getDefaultProductId = (plan: PE, billingPeriod: BP): string => {\n const subscription = this.subscriptions.get(`${plan}:${billingPeriod}`);\n invariant(subscription, `Subscription not found for ${plan}:${billingPeriod}`);\n invariant(\n subscription.defaultProductId,\n `Default product not found for ${plan}:${billingPeriod}`\n );\n return subscription.defaultProductId;\n };\n\n getPlan = (productId: string): PE => {\n for (const subscription of this.subscriptions.values()) {\n if (subscription.products.has(productId)) return subscription.plan;\n }\n throw new Error(`Plan not found for product ${productId}`);\n };\n\n getBillingPeriod = (productId: string): BP => {\n for (const subscription of this.subscriptions.values()) {\n if (subscription.products.has(productId)) return subscription.billingPeriod;\n }\n throw new Error(`Billing period not found for product ${productId}`);\n };\n\n getMode = (productId: string): 'payment' | 'subscription' => {\n if (this.consumables.has(productId) || this.nonConsumables.has(productId)) return 'payment';\n for (const subscription of this.subscriptions.values()) {\n if (subscription.products.has(productId)) return 'subscription';\n }\n throw new Error(`Mode not found for product ${productId}`);\n };\n\n getCreditAmount = (productId: string): number => {\n const config = this.products.get(productId);\n invariant(config !== undefined, `Product not found for ${productId}`);\n invariant(config !== null, `Credits not available for non-consumable product ${productId}`);\n return config.credits;\n };\n\n private getCreditExpiresIn = (productId: string): number => {\n const config = this.products.get(productId);\n invariant(config, `Product not found for ${productId}`);\n return ms(config.expiresIn);\n };\n\n getCreditExpiresAt = (productId: string): string => {\n const expiresIn = this.getCreditExpiresIn(productId);\n return new Date(Date.now() + expiresIn).toISOString();\n };\n}\n"],"mappings":"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
|
|
1
|
+
{"version":3,"file":"config.cjs","names":[],"sources":["../../src/app-store/config.ts"],"sourcesContent":["import { invariant } from '@shware/utils';\nimport ms from 'ms';\nimport type { Metadata } from '../types';\n\nconst addMethod = Symbol('addMethod');\n\nclass Subscription<\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 string = never,\n> {\n readonly plan: PE;\n readonly billingPeriod: BP;\n readonly config: AppStoreConfig<NS, PE, BP, PL, PI>;\n readonly products: Map<string, Metadata>;\n\n defaultProductId: I | null;\n\n constructor(config: AppStoreConfig<NS, PE, BP, PL, PI>, plan: PE, billingPeriod: BP) {\n this.plan = plan;\n this.billingPeriod = billingPeriod;\n this.config = config;\n this.products = new Map();\n this.defaultProductId = null;\n }\n\n /**\n * Non-default products stay declared on purpose: they keep resolving for\n * grandfathered subscribers and serve as price-experiment arms. Never\n * sell a legacy product to new users unless that is intentional.\n */\n product = <K extends `${NS}.${Lowercase<string>}`>(\n productId: K extends PI ? never : K,\n metadata: Metadata = { credits: 0, expiresIn: '0' }\n ): Subscription<NS, PE, BP, PL, PI | K, I | K> => {\n invariant(!this.products.has(productId), `Duplicate product ${productId}`);\n this.products.set(productId, metadata);\n return this as Subscription<NS, PE, BP, PL, PI | K, I | K>;\n };\n\n default = (defaultProductId: I) => {\n invariant(\n this.products.has(defaultProductId),\n `Default product ${defaultProductId} is not declared`\n );\n this.defaultProductId = defaultProductId;\n this.config[addMethod](this as never);\n return this.config;\n };\n}\n\ntype Options = { appId: `${number}`; bundleId: string };\n\nexport class AppStoreConfig<\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 readonly appId: string;\n readonly bundleId: string;\n\n private readonly products: Map<string, Metadata | null>;\n private readonly consumables: Set<string>;\n private readonly subscriptions: Map<string, Subscription<NS, PE, BP, PL, PI>>;\n private readonly nonConsumables: Set<string>;\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] = (subscription: Subscription<NS, PE, BP, PL, PI>) => {\n const key = `${subscription.plan}:${subscription.billingPeriod}`;\n invariant(!this.subscriptions.has(key), `Duplicate plan ${key}`);\n subscription.products.forEach((metadata, productId) => {\n invariant(!this.products.has(productId), `Duplicate product ${productId}`);\n this.products.set(productId, metadata);\n });\n this.subscriptions.set(key, subscription);\n return this;\n };\n\n private constructor(options: Options) {\n this.appId = options.appId;\n this.bundleId = options.bundleId;\n this.products = new Map();\n this.consumables = new Set();\n this.subscriptions = new Map();\n this.nonConsumables = new Set();\n }\n\n static create = <NS extends Lowercase<string>, PE extends string, BP extends string = string>(\n options: Options\n ) => {\n return new AppStoreConfig<NS, PE, BP>(options);\n };\n\n /** The same plan may be declared once per billing period; duplicate pairs are rejected. */\n subscription = <K extends PE, P extends BP>(\n plan: K,\n billingPeriod: `${K}:${P}` extends PL ? never : P\n ) => {\n // oxlint-disable-next-line typescript/no-unnecessary-type-assertion -- tsc requires the cast (tsgolint false positive)\n return new Subscription<NS, PE, BP, `${K}:${P}` | PL, PI>(this as never, plan, billingPeriod);\n };\n\n consumable = <K extends `${NS}.${Lowercase<string>}`>(\n productId: K extends PI ? never : K,\n metadata: Metadata = { credits: 0, expiresIn: '0' }\n ) => {\n invariant(!this.products.has(productId), `Duplicate product ${productId}`);\n this.consumables.add(productId);\n this.products.set(productId, metadata);\n return this as AppStoreConfig<NS, PE, BP, PL, PI | K>;\n };\n\n nonConsumable = <K extends `${NS}.${Lowercase<string>}`>(productId: K extends PI ? never : K) => {\n invariant(!this.products.has(productId), `Duplicate product ${productId}`);\n this.nonConsumables.add(productId);\n this.products.set(productId, null);\n return this as AppStoreConfig<NS, PE, BP, PL, PI | K>;\n };\n\n /** Product id that new subscribers should purchase for the plan and billing period. */\n getDefaultProductId = (plan: PE, billingPeriod: BP): string => {\n const subscription = this.subscriptions.get(`${plan}:${billingPeriod}`);\n invariant(subscription, `Subscription not found for ${plan}:${billingPeriod}`);\n invariant(\n subscription.defaultProductId,\n `Default product not found for ${plan}:${billingPeriod}`\n );\n return subscription.defaultProductId;\n };\n\n getPlan = (productId: string): PE => {\n for (const subscription of this.subscriptions.values()) {\n if (subscription.products.has(productId)) return subscription.plan;\n }\n throw new Error(`Plan not found for product ${productId}`);\n };\n\n getBillingPeriod = (productId: string): BP => {\n for (const subscription of this.subscriptions.values()) {\n if (subscription.products.has(productId)) return subscription.billingPeriod;\n }\n throw new Error(`Billing period not found for product ${productId}`);\n };\n\n getMode = (productId: string): 'payment' | 'subscription' => {\n if (this.consumables.has(productId) || this.nonConsumables.has(productId)) return 'payment';\n for (const subscription of this.subscriptions.values()) {\n if (subscription.products.has(productId)) return 'subscription';\n }\n throw new Error(`Mode not found for product ${productId}`);\n };\n\n getCreditAmount = (productId: string): number => {\n const config = this.products.get(productId);\n invariant(config !== undefined, `Product not found for ${productId}`);\n invariant(config !== null, `Credits not available for non-consumable product ${productId}`);\n return config.credits;\n };\n\n private readonly getCreditExpiresIn = (productId: string): number => {\n const config = this.products.get(productId);\n invariant(config, `Product not found for ${productId}`);\n return ms(config.expiresIn);\n };\n\n getCreditExpiresAt = (productId: string): string => {\n const expiresIn = this.getCreditExpiresIn(productId);\n return new Date(Date.now() + expiresIn).toISOString();\n };\n}\n"],"mappings":"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"}
|
|
@@ -23,10 +23,10 @@ type Options = {
|
|
|
23
23
|
declare class AppStoreConfig<NS extends Lowercase<string> = Lowercase<string>, PE extends string = string, BP extends string = string, PL extends string = never, PI extends string = never> {
|
|
24
24
|
readonly appId: string;
|
|
25
25
|
readonly bundleId: string;
|
|
26
|
-
private products;
|
|
27
|
-
private consumables;
|
|
28
|
-
private subscriptions;
|
|
29
|
-
private nonConsumables;
|
|
26
|
+
private readonly products;
|
|
27
|
+
private readonly consumables;
|
|
28
|
+
private readonly subscriptions;
|
|
29
|
+
private readonly nonConsumables;
|
|
30
30
|
get productIds(): string[];
|
|
31
31
|
[addMethod]: (subscription: Subscription<NS, PE, BP, PL, PI>) => this;
|
|
32
32
|
private constructor();
|
|
@@ -41,7 +41,7 @@ declare class AppStoreConfig<NS extends Lowercase<string> = Lowercase<string>, P
|
|
|
41
41
|
getBillingPeriod: (productId: string) => BP;
|
|
42
42
|
getMode: (productId: string) => 'payment' | 'subscription';
|
|
43
43
|
getCreditAmount: (productId: string) => number;
|
|
44
|
-
private getCreditExpiresIn;
|
|
44
|
+
private readonly getCreditExpiresIn;
|
|
45
45
|
getCreditExpiresAt: (productId: string) => string;
|
|
46
46
|
}
|
|
47
47
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.cts","names":[],"sources":["../../src/app-store/config.ts"],"mappings":";;cAIM;cAEA,aACJ,WAAW,oBAAoB,mBAC/B,4BACA,4BACA,2BACA,2BACA;WAES,MAAM;WACN,eAAe;WACf,QAAQ,eAAe,IAAI,IAAI,IAAI,IAAI;WACvC,UAAU,YAAY;EAE/B,kBAAkB;EAElB,YAAY,QAAQ,eAAe,IAAI,IAAI,IAAI,IAAI,KAAK,MAAM,IAAI,eAAe;;;;;;EAajF,UAAW,aAAa,MAAM,qBAAmB,WACpC,UAAU,aAAa,GAAC,WACzB,aACT,aAAa,IAAI,IAAI,IAAI,IAAI,KAAK,GAAG,IAAI;EAM5C,UAAO,kBAAsB,MAAC,eAAA,IAAA,IAAA,IAAA,IAAA;;KAW3B;EAAY;EAAoB;;cAExB,eACX,WAAW,oBAAoB,mBAC/B,4BACA,4BACA,2BACA;WAES;WACA;
|
|
1
|
+
{"version":3,"file":"config.d.cts","names":[],"sources":["../../src/app-store/config.ts"],"mappings":";;cAIM;cAEA,aACJ,WAAW,oBAAoB,mBAC/B,4BACA,4BACA,2BACA,2BACA;WAES,MAAM;WACN,eAAe;WACf,QAAQ,eAAe,IAAI,IAAI,IAAI,IAAI;WACvC,UAAU,YAAY;EAE/B,kBAAkB;EAElB,YAAY,QAAQ,eAAe,IAAI,IAAI,IAAI,IAAI,KAAK,MAAM,IAAI,eAAe;;;;;;EAajF,UAAW,aAAa,MAAM,qBAAmB,WACpC,UAAU,aAAa,GAAC,WACzB,aACT,aAAa,IAAI,IAAI,IAAI,IAAI,KAAK,GAAG,IAAI;EAM5C,UAAO,kBAAsB,MAAC,eAAA,IAAA,IAAA,IAAA,IAAA;;KAW3B;EAAY;EAAoB;;cAExB,eACX,WAAW,oBAAoB,mBAC/B,4BACA,4BACA,2BACA;WAES;WACA;mBAEQ;mBACA;mBACA;mBACA;MAEb;GAMH,aAAU,cAAkB,aAAa,IAAI,IAAI,IAAI,IAAI;UAWnD;SASA,SAAU,aAAW,mBAAmB,qBAAmB,8BAA0B,SACjF,YAAO,eAAA,MAAA,MAAA;;EAMlB,eAAgB,UAAU,IAAI,UAAU,IAAE,MAClC,GAAC,kBACW,KAAK,YAAY,aAAa,MAAC,aAAA,IAAA,IAAA,IAAA,QAAA,KAAA,KAAA;EAMnD,aAAc,aAAa,MAAM,qBAAmB,WACvC,UAAU,aAAa,GAAC,WACzB,aAKK,eAAe,IAAI,IAAI,IAAI,IAAI,KAAK;EAGrD,gBAAiB,aAAa,MAAM,qBAAmB,WAAa,UAAU,aAAa,MAI1E,eAAe,IAAI,IAAI,IAAI,IAAI,KAAK;;EAIrD,sBAAmB,MAAU,IAAE,eAAiB;EAUhD,UAAO,sBAAwB;EAO/B,mBAAgB,sBAAwB;EAOxC,UAAO;EAQP,kBAAe;mBAOE;EAMjB,qBAAkB"}
|
|
@@ -23,10 +23,10 @@ type Options = {
|
|
|
23
23
|
declare class AppStoreConfig<NS extends Lowercase<string> = Lowercase<string>, PE extends string = string, BP extends string = string, PL extends string = never, PI extends string = never> {
|
|
24
24
|
readonly appId: string;
|
|
25
25
|
readonly bundleId: string;
|
|
26
|
-
private products;
|
|
27
|
-
private consumables;
|
|
28
|
-
private subscriptions;
|
|
29
|
-
private nonConsumables;
|
|
26
|
+
private readonly products;
|
|
27
|
+
private readonly consumables;
|
|
28
|
+
private readonly subscriptions;
|
|
29
|
+
private readonly nonConsumables;
|
|
30
30
|
get productIds(): string[];
|
|
31
31
|
[addMethod]: (subscription: Subscription<NS, PE, BP, PL, PI>) => this;
|
|
32
32
|
private constructor();
|
|
@@ -41,7 +41,7 @@ declare class AppStoreConfig<NS extends Lowercase<string> = Lowercase<string>, P
|
|
|
41
41
|
getBillingPeriod: (productId: string) => BP;
|
|
42
42
|
getMode: (productId: string) => 'payment' | 'subscription';
|
|
43
43
|
getCreditAmount: (productId: string) => number;
|
|
44
|
-
private getCreditExpiresIn;
|
|
44
|
+
private readonly getCreditExpiresIn;
|
|
45
45
|
getCreditExpiresAt: (productId: string) => string;
|
|
46
46
|
}
|
|
47
47
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.mts","names":[],"sources":["../../src/app-store/config.ts"],"mappings":";;cAIM;cAEA,aACJ,WAAW,oBAAoB,mBAC/B,4BACA,4BACA,2BACA,2BACA;WAES,MAAM;WACN,eAAe;WACf,QAAQ,eAAe,IAAI,IAAI,IAAI,IAAI;WACvC,UAAU,YAAY;EAE/B,kBAAkB;EAElB,YAAY,QAAQ,eAAe,IAAI,IAAI,IAAI,IAAI,KAAK,MAAM,IAAI,eAAe;;;;;;EAajF,UAAW,aAAa,MAAM,qBAAmB,WACpC,UAAU,aAAa,GAAC,WACzB,aACT,aAAa,IAAI,IAAI,IAAI,IAAI,KAAK,GAAG,IAAI;EAM5C,UAAO,kBAAsB,MAAC,eAAA,IAAA,IAAA,IAAA,IAAA;;KAW3B;EAAY;EAAoB;;cAExB,eACX,WAAW,oBAAoB,mBAC/B,4BACA,4BACA,2BACA;WAES;WACA;
|
|
1
|
+
{"version":3,"file":"config.d.mts","names":[],"sources":["../../src/app-store/config.ts"],"mappings":";;cAIM;cAEA,aACJ,WAAW,oBAAoB,mBAC/B,4BACA,4BACA,2BACA,2BACA;WAES,MAAM;WACN,eAAe;WACf,QAAQ,eAAe,IAAI,IAAI,IAAI,IAAI;WACvC,UAAU,YAAY;EAE/B,kBAAkB;EAElB,YAAY,QAAQ,eAAe,IAAI,IAAI,IAAI,IAAI,KAAK,MAAM,IAAI,eAAe;;;;;;EAajF,UAAW,aAAa,MAAM,qBAAmB,WACpC,UAAU,aAAa,GAAC,WACzB,aACT,aAAa,IAAI,IAAI,IAAI,IAAI,KAAK,GAAG,IAAI;EAM5C,UAAO,kBAAsB,MAAC,eAAA,IAAA,IAAA,IAAA,IAAA;;KAW3B;EAAY;EAAoB;;cAExB,eACX,WAAW,oBAAoB,mBAC/B,4BACA,4BACA,2BACA;WAES;WACA;mBAEQ;mBACA;mBACA;mBACA;MAEb;GAMH,aAAU,cAAkB,aAAa,IAAI,IAAI,IAAI,IAAI;UAWnD;SASA,SAAU,aAAW,mBAAmB,qBAAmB,8BAA0B,SACjF,YAAO,eAAA,MAAA,MAAA;;EAMlB,eAAgB,UAAU,IAAI,UAAU,IAAE,MAClC,GAAC,kBACW,KAAK,YAAY,aAAa,MAAC,aAAA,IAAA,IAAA,IAAA,QAAA,KAAA,KAAA;EAMnD,aAAc,aAAa,MAAM,qBAAmB,WACvC,UAAU,aAAa,GAAC,WACzB,aAKK,eAAe,IAAI,IAAI,IAAI,IAAI,KAAK;EAGrD,gBAAiB,aAAa,MAAM,qBAAmB,WAAa,UAAU,aAAa,MAI1E,eAAe,IAAI,IAAI,IAAI,IAAI,KAAK;;EAIrD,sBAAmB,MAAU,IAAE,eAAiB;EAUhD,UAAO,sBAAwB;EAO/B,mBAAgB,sBAAwB;EAOxC,UAAO;EAQP,kBAAe;mBAOE;EAMjB,qBAAkB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.mjs","names":[],"sources":["../../src/app-store/config.ts"],"sourcesContent":["import { invariant } from '@shware/utils';\nimport ms from 'ms';\nimport type { Metadata } from '../types';\n\nconst addMethod = Symbol('addMethod');\n\nclass Subscription<\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 string = never,\n> {\n readonly plan: PE;\n readonly billingPeriod: BP;\n readonly config: AppStoreConfig<NS, PE, BP, PL, PI>;\n readonly products: Map<string, Metadata>;\n\n defaultProductId: I | null;\n\n constructor(config: AppStoreConfig<NS, PE, BP, PL, PI>, plan: PE, billingPeriod: BP) {\n this.plan = plan;\n this.billingPeriod = billingPeriod;\n this.config = config;\n this.products = new Map();\n this.defaultProductId = null;\n }\n\n /**\n * Non-default products stay declared on purpose: they keep resolving for\n * grandfathered subscribers and serve as price-experiment arms. Never\n * sell a legacy product to new users unless that is intentional.\n */\n product = <K extends `${NS}.${Lowercase<string>}`>(\n productId: K extends PI ? never : K,\n metadata: Metadata = { credits: 0, expiresIn: '0' }\n ): Subscription<NS, PE, BP, PL, PI | K, I | K> => {\n invariant(!this.products.has(productId), `Duplicate product ${productId}`);\n this.products.set(productId, metadata);\n return this as Subscription<NS, PE, BP, PL, PI | K, I | K>;\n };\n\n default = (defaultProductId: I) => {\n invariant(\n this.products.has(defaultProductId),\n `Default product ${defaultProductId} is not declared`\n );\n this.defaultProductId = defaultProductId;\n this.config[addMethod](this as never);\n return this.config;\n };\n}\n\ntype Options = { appId: `${number}`; bundleId: string };\n\nexport class AppStoreConfig<\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 readonly appId: string;\n readonly bundleId: string;\n\n private products: Map<string, Metadata | null>;\n private consumables: Set<string>;\n private subscriptions: Map<string, Subscription<NS, PE, BP, PL, PI>>;\n private nonConsumables: Set<string>;\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] = (subscription: Subscription<NS, PE, BP, PL, PI>) => {\n const key = `${subscription.plan}:${subscription.billingPeriod}`;\n invariant(!this.subscriptions.has(key), `Duplicate plan ${key}`);\n subscription.products.forEach((metadata, productId) => {\n invariant(!this.products.has(productId), `Duplicate product ${productId}`);\n this.products.set(productId, metadata);\n });\n this.subscriptions.set(key, subscription);\n return this;\n };\n\n private constructor(options: Options) {\n this.appId = options.appId;\n this.bundleId = options.bundleId;\n this.products = new Map();\n this.consumables = new Set();\n this.subscriptions = new Map();\n this.nonConsumables = new Set();\n }\n\n static create = <NS extends Lowercase<string>, PE extends string, BP extends string = string>(\n options: Options\n ) => {\n return new AppStoreConfig<NS, PE, BP>(options);\n };\n\n /** The same plan may be declared once per billing period; duplicate pairs are rejected. */\n subscription = <K extends PE, P extends BP>(\n plan: K,\n billingPeriod: `${K}:${P}` extends PL ? never : P\n ) => {\n // oxlint-disable-next-line typescript/no-unnecessary-type-assertion -- tsc requires the cast (tsgolint false positive)\n return new Subscription<NS, PE, BP, `${K}:${P}` | PL, PI>(this as never, plan, billingPeriod);\n };\n\n consumable = <K extends `${NS}.${Lowercase<string>}`>(\n productId: K extends PI ? never : K,\n metadata: Metadata = { credits: 0, expiresIn: '0' }\n ) => {\n invariant(!this.products.has(productId), `Duplicate product ${productId}`);\n this.consumables.add(productId);\n this.products.set(productId, metadata);\n return this as AppStoreConfig<NS, PE, BP, PL, PI | K>;\n };\n\n nonConsumable = <K extends `${NS}.${Lowercase<string>}`>(productId: K extends PI ? never : K) => {\n invariant(!this.products.has(productId), `Duplicate product ${productId}`);\n this.nonConsumables.add(productId);\n this.products.set(productId, null);\n return this as AppStoreConfig<NS, PE, BP, PL, PI | K>;\n };\n\n /** Product id that new subscribers should purchase for the plan and billing period. */\n getDefaultProductId = (plan: PE, billingPeriod: BP): string => {\n const subscription = this.subscriptions.get(`${plan}:${billingPeriod}`);\n invariant(subscription, `Subscription not found for ${plan}:${billingPeriod}`);\n invariant(\n subscription.defaultProductId,\n `Default product not found for ${plan}:${billingPeriod}`\n );\n return subscription.defaultProductId;\n };\n\n getPlan = (productId: string): PE => {\n for (const subscription of this.subscriptions.values()) {\n if (subscription.products.has(productId)) return subscription.plan;\n }\n throw new Error(`Plan not found for product ${productId}`);\n };\n\n getBillingPeriod = (productId: string): BP => {\n for (const subscription of this.subscriptions.values()) {\n if (subscription.products.has(productId)) return subscription.billingPeriod;\n }\n throw new Error(`Billing period not found for product ${productId}`);\n };\n\n getMode = (productId: string): 'payment' | 'subscription' => {\n if (this.consumables.has(productId) || this.nonConsumables.has(productId)) return 'payment';\n for (const subscription of this.subscriptions.values()) {\n if (subscription.products.has(productId)) return 'subscription';\n }\n throw new Error(`Mode not found for product ${productId}`);\n };\n\n getCreditAmount = (productId: string): number => {\n const config = this.products.get(productId);\n invariant(config !== undefined, `Product not found for ${productId}`);\n invariant(config !== null, `Credits not available for non-consumable product ${productId}`);\n return config.credits;\n };\n\n private getCreditExpiresIn = (productId: string): number => {\n const config = this.products.get(productId);\n invariant(config, `Product not found for ${productId}`);\n return ms(config.expiresIn);\n };\n\n getCreditExpiresAt = (productId: string): string => {\n const expiresIn = this.getCreditExpiresIn(productId);\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/app-store/config.ts"],"sourcesContent":["import { invariant } from '@shware/utils';\nimport ms from 'ms';\nimport type { Metadata } from '../types';\n\nconst addMethod = Symbol('addMethod');\n\nclass Subscription<\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 string = never,\n> {\n readonly plan: PE;\n readonly billingPeriod: BP;\n readonly config: AppStoreConfig<NS, PE, BP, PL, PI>;\n readonly products: Map<string, Metadata>;\n\n defaultProductId: I | null;\n\n constructor(config: AppStoreConfig<NS, PE, BP, PL, PI>, plan: PE, billingPeriod: BP) {\n this.plan = plan;\n this.billingPeriod = billingPeriod;\n this.config = config;\n this.products = new Map();\n this.defaultProductId = null;\n }\n\n /**\n * Non-default products stay declared on purpose: they keep resolving for\n * grandfathered subscribers and serve as price-experiment arms. Never\n * sell a legacy product to new users unless that is intentional.\n */\n product = <K extends `${NS}.${Lowercase<string>}`>(\n productId: K extends PI ? never : K,\n metadata: Metadata = { credits: 0, expiresIn: '0' }\n ): Subscription<NS, PE, BP, PL, PI | K, I | K> => {\n invariant(!this.products.has(productId), `Duplicate product ${productId}`);\n this.products.set(productId, metadata);\n return this as Subscription<NS, PE, BP, PL, PI | K, I | K>;\n };\n\n default = (defaultProductId: I) => {\n invariant(\n this.products.has(defaultProductId),\n `Default product ${defaultProductId} is not declared`\n );\n this.defaultProductId = defaultProductId;\n this.config[addMethod](this as never);\n return this.config;\n };\n}\n\ntype Options = { appId: `${number}`; bundleId: string };\n\nexport class AppStoreConfig<\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 readonly appId: string;\n readonly bundleId: string;\n\n private readonly products: Map<string, Metadata | null>;\n private readonly consumables: Set<string>;\n private readonly subscriptions: Map<string, Subscription<NS, PE, BP, PL, PI>>;\n private readonly nonConsumables: Set<string>;\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] = (subscription: Subscription<NS, PE, BP, PL, PI>) => {\n const key = `${subscription.plan}:${subscription.billingPeriod}`;\n invariant(!this.subscriptions.has(key), `Duplicate plan ${key}`);\n subscription.products.forEach((metadata, productId) => {\n invariant(!this.products.has(productId), `Duplicate product ${productId}`);\n this.products.set(productId, metadata);\n });\n this.subscriptions.set(key, subscription);\n return this;\n };\n\n private constructor(options: Options) {\n this.appId = options.appId;\n this.bundleId = options.bundleId;\n this.products = new Map();\n this.consumables = new Set();\n this.subscriptions = new Map();\n this.nonConsumables = new Set();\n }\n\n static create = <NS extends Lowercase<string>, PE extends string, BP extends string = string>(\n options: Options\n ) => {\n return new AppStoreConfig<NS, PE, BP>(options);\n };\n\n /** The same plan may be declared once per billing period; duplicate pairs are rejected. */\n subscription = <K extends PE, P extends BP>(\n plan: K,\n billingPeriod: `${K}:${P}` extends PL ? never : P\n ) => {\n // oxlint-disable-next-line typescript/no-unnecessary-type-assertion -- tsc requires the cast (tsgolint false positive)\n return new Subscription<NS, PE, BP, `${K}:${P}` | PL, PI>(this as never, plan, billingPeriod);\n };\n\n consumable = <K extends `${NS}.${Lowercase<string>}`>(\n productId: K extends PI ? never : K,\n metadata: Metadata = { credits: 0, expiresIn: '0' }\n ) => {\n invariant(!this.products.has(productId), `Duplicate product ${productId}`);\n this.consumables.add(productId);\n this.products.set(productId, metadata);\n return this as AppStoreConfig<NS, PE, BP, PL, PI | K>;\n };\n\n nonConsumable = <K extends `${NS}.${Lowercase<string>}`>(productId: K extends PI ? never : K) => {\n invariant(!this.products.has(productId), `Duplicate product ${productId}`);\n this.nonConsumables.add(productId);\n this.products.set(productId, null);\n return this as AppStoreConfig<NS, PE, BP, PL, PI | K>;\n };\n\n /** Product id that new subscribers should purchase for the plan and billing period. */\n getDefaultProductId = (plan: PE, billingPeriod: BP): string => {\n const subscription = this.subscriptions.get(`${plan}:${billingPeriod}`);\n invariant(subscription, `Subscription not found for ${plan}:${billingPeriod}`);\n invariant(\n subscription.defaultProductId,\n `Default product not found for ${plan}:${billingPeriod}`\n );\n return subscription.defaultProductId;\n };\n\n getPlan = (productId: string): PE => {\n for (const subscription of this.subscriptions.values()) {\n if (subscription.products.has(productId)) return subscription.plan;\n }\n throw new Error(`Plan not found for product ${productId}`);\n };\n\n getBillingPeriod = (productId: string): BP => {\n for (const subscription of this.subscriptions.values()) {\n if (subscription.products.has(productId)) return subscription.billingPeriod;\n }\n throw new Error(`Billing period not found for product ${productId}`);\n };\n\n getMode = (productId: string): 'payment' | 'subscription' => {\n if (this.consumables.has(productId) || this.nonConsumables.has(productId)) return 'payment';\n for (const subscription of this.subscriptions.values()) {\n if (subscription.products.has(productId)) return 'subscription';\n }\n throw new Error(`Mode not found for product ${productId}`);\n };\n\n getCreditAmount = (productId: string): number => {\n const config = this.products.get(productId);\n invariant(config !== undefined, `Product not found for ${productId}`);\n invariant(config !== null, `Credits not available for non-consumable product ${productId}`);\n return config.credits;\n };\n\n private readonly getCreditExpiresIn = (productId: string): number => {\n const config = this.products.get(productId);\n invariant(config, `Product not found for ${productId}`);\n return ms(config.expiresIn);\n };\n\n getCreditExpiresAt = (productId: string): string => {\n const expiresIn = this.getCreditExpiresIn(productId);\n return new Date(Date.now() + expiresIn).toISOString();\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region src/app-store/handler.ts
|
|
3
|
+
/**
|
|
4
|
+
* App Store Server Notification dispatcher keyed by notificationType, the app-store counterpart
|
|
5
|
+
* of StripeEventHandler. The caller verifies and decodes the notification (payload, transaction,
|
|
6
|
+
* renewalInfo) first and passes the decoded event in. Subtype branching stays inside each
|
|
7
|
+
* handler: Apple subtypes share the handler's derived context and are usually a few lines each.
|
|
8
|
+
*/
|
|
9
|
+
var AppStoreNotificationHandler = class AppStoreNotificationHandler {
|
|
10
|
+
handlers;
|
|
11
|
+
constructor() {
|
|
12
|
+
this.handlers = /* @__PURE__ */ new Map();
|
|
13
|
+
}
|
|
14
|
+
static create() {
|
|
15
|
+
return new AppStoreNotificationHandler();
|
|
16
|
+
}
|
|
17
|
+
on = (type, handler) => {
|
|
18
|
+
if (!this.handlers.has(type)) this.handlers.set(type, []);
|
|
19
|
+
this.handlers.get(type)?.push(handler);
|
|
20
|
+
return this;
|
|
21
|
+
};
|
|
22
|
+
process = async (event) => {
|
|
23
|
+
const type = event.payload.notificationType;
|
|
24
|
+
const handlers = type ? this.handlers.get(type) : void 0;
|
|
25
|
+
if (!handlers) {
|
|
26
|
+
console.error(`No app store notification handler found for type: ${type}`);
|
|
27
|
+
throw new Error(`No app store notification handler found for type: ${type}`);
|
|
28
|
+
}
|
|
29
|
+
await Promise.all(handlers.map((handler) => handler(event)));
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
//#endregion
|
|
33
|
+
exports.AppStoreNotificationHandler = AppStoreNotificationHandler;
|
|
34
|
+
|
|
35
|
+
//# sourceMappingURL=handler.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.cjs","names":[],"sources":["../../src/app-store/handler.ts"],"sourcesContent":["import type {\n JWSRenewalInfoDecodedPayload,\n JWSTransactionDecodedPayload,\n NotificationTypeV2,\n ResponseBodyV2DecodedPayload,\n} from '@apple/app-store-server-library';\n\nexport interface AppStoreNotificationEvent {\n payload: ResponseBodyV2DecodedPayload;\n transaction: JWSTransactionDecodedPayload | null;\n renewalInfo: JWSRenewalInfoDecodedPayload | null;\n}\n\ntype NotificationHandlerFn = (event: AppStoreNotificationEvent) => Promise<void>;\n\n/**\n * App Store Server Notification dispatcher keyed by notificationType, the app-store counterpart\n * of StripeEventHandler. The caller verifies and decodes the notification (payload, transaction,\n * renewalInfo) first and passes the decoded event in. Subtype branching stays inside each\n * handler: Apple subtypes share the handler's derived context and are usually a few lines each.\n */\nexport class AppStoreNotificationHandler {\n private readonly handlers: Map<NotificationTypeV2, NotificationHandlerFn[]>;\n\n private constructor() {\n this.handlers = new Map();\n }\n\n static create() {\n return new AppStoreNotificationHandler();\n }\n\n on = (type: NotificationTypeV2, handler: NotificationHandlerFn) => {\n if (!this.handlers.has(type)) {\n this.handlers.set(type, []);\n }\n\n this.handlers.get(type)?.push(handler);\n return this;\n };\n\n process = async (event: AppStoreNotificationEvent): Promise<void> => {\n const type = event.payload.notificationType as NotificationTypeV2 | undefined;\n const handlers = type ? this.handlers.get(type) : undefined;\n if (!handlers) {\n console.error(`No app store notification handler found for type: ${type}`);\n throw new Error(`No app store notification handler found for type: ${type}`);\n }\n await Promise.all(handlers.map((handler) => handler(event)));\n };\n}\n"],"mappings":";;;;;;;;AAqBA,IAAa,8BAAb,MAAa,4BAA4B;CACvC;CAEA,cAAsB;EACpB,KAAK,2BAAW,IAAI,IAAI;CAC1B;CAEA,OAAO,SAAS;EACd,OAAO,IAAI,4BAA4B;CACzC;CAEA,MAAM,MAA0B,YAAmC;EACjE,IAAI,CAAC,KAAK,SAAS,IAAI,IAAI,GACzB,KAAK,SAAS,IAAI,MAAM,CAAC,CAAC;EAG5B,KAAK,SAAS,IAAI,IAAI,CAAC,EAAE,KAAK,OAAO;EACrC,OAAO;CACT;CAEA,UAAU,OAAO,UAAoD;EACnE,MAAM,OAAO,MAAM,QAAQ;EAC3B,MAAM,WAAW,OAAO,KAAK,SAAS,IAAI,IAAI,IAAI,KAAA;EAClD,IAAI,CAAC,UAAU;GACb,QAAQ,MAAM,qDAAqD,MAAM;GACzE,MAAM,IAAI,MAAM,qDAAqD,MAAM;EAC7E;EACA,MAAM,QAAQ,IAAI,SAAS,KAAK,YAAY,QAAQ,KAAK,CAAC,CAAC;CAC7D;AACF"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { JWSRenewalInfoDecodedPayload, JWSTransactionDecodedPayload, NotificationTypeV2, ResponseBodyV2DecodedPayload } from "@apple/app-store-server-library";
|
|
2
|
+
//#region src/app-store/handler.d.ts
|
|
3
|
+
interface AppStoreNotificationEvent {
|
|
4
|
+
payload: ResponseBodyV2DecodedPayload;
|
|
5
|
+
transaction: JWSTransactionDecodedPayload | null;
|
|
6
|
+
renewalInfo: JWSRenewalInfoDecodedPayload | null;
|
|
7
|
+
}
|
|
8
|
+
type NotificationHandlerFn = (event: AppStoreNotificationEvent) => Promise<void>;
|
|
9
|
+
/**
|
|
10
|
+
* App Store Server Notification dispatcher keyed by notificationType, the app-store counterpart
|
|
11
|
+
* of StripeEventHandler. The caller verifies and decodes the notification (payload, transaction,
|
|
12
|
+
* renewalInfo) first and passes the decoded event in. Subtype branching stays inside each
|
|
13
|
+
* handler: Apple subtypes share the handler's derived context and are usually a few lines each.
|
|
14
|
+
*/
|
|
15
|
+
declare class AppStoreNotificationHandler {
|
|
16
|
+
private readonly handlers;
|
|
17
|
+
private constructor();
|
|
18
|
+
static create(): AppStoreNotificationHandler;
|
|
19
|
+
on: (type: NotificationTypeV2, handler: NotificationHandlerFn) => this;
|
|
20
|
+
process: (event: AppStoreNotificationEvent) => Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
export { AppStoreNotificationEvent, AppStoreNotificationHandler };
|
|
24
|
+
//# sourceMappingURL=handler.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.d.cts","names":[],"sources":["../../src/app-store/handler.ts"],"mappings":";;UAOiB;EACf,SAAS;EACT,aAAa;EACb,aAAa;;KAGV,yBAAyB,OAAO,8BAA8B;;;;;;;cAQtD;mBACM;UAEV;SAIA,UAAM;EAIb,KAAE,MAAU,oBAAkB,SAAW;EASzC,UAAO,OAAiB,8BAA4B"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { JWSRenewalInfoDecodedPayload, JWSTransactionDecodedPayload, NotificationTypeV2, ResponseBodyV2DecodedPayload } from "@apple/app-store-server-library";
|
|
2
|
+
//#region src/app-store/handler.d.ts
|
|
3
|
+
interface AppStoreNotificationEvent {
|
|
4
|
+
payload: ResponseBodyV2DecodedPayload;
|
|
5
|
+
transaction: JWSTransactionDecodedPayload | null;
|
|
6
|
+
renewalInfo: JWSRenewalInfoDecodedPayload | null;
|
|
7
|
+
}
|
|
8
|
+
type NotificationHandlerFn = (event: AppStoreNotificationEvent) => Promise<void>;
|
|
9
|
+
/**
|
|
10
|
+
* App Store Server Notification dispatcher keyed by notificationType, the app-store counterpart
|
|
11
|
+
* of StripeEventHandler. The caller verifies and decodes the notification (payload, transaction,
|
|
12
|
+
* renewalInfo) first and passes the decoded event in. Subtype branching stays inside each
|
|
13
|
+
* handler: Apple subtypes share the handler's derived context and are usually a few lines each.
|
|
14
|
+
*/
|
|
15
|
+
declare class AppStoreNotificationHandler {
|
|
16
|
+
private readonly handlers;
|
|
17
|
+
private constructor();
|
|
18
|
+
static create(): AppStoreNotificationHandler;
|
|
19
|
+
on: (type: NotificationTypeV2, handler: NotificationHandlerFn) => this;
|
|
20
|
+
process: (event: AppStoreNotificationEvent) => Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
export { AppStoreNotificationEvent, AppStoreNotificationHandler };
|
|
24
|
+
//# sourceMappingURL=handler.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.d.mts","names":[],"sources":["../../src/app-store/handler.ts"],"mappings":";;UAOiB;EACf,SAAS;EACT,aAAa;EACb,aAAa;;KAGV,yBAAyB,OAAO,8BAA8B;;;;;;;cAQtD;mBACM;UAEV;SAIA,UAAM;EAIb,KAAE,MAAU,oBAAkB,SAAW;EASzC,UAAO,OAAiB,8BAA4B"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
//#region src/app-store/handler.ts
|
|
2
|
+
/**
|
|
3
|
+
* App Store Server Notification dispatcher keyed by notificationType, the app-store counterpart
|
|
4
|
+
* of StripeEventHandler. The caller verifies and decodes the notification (payload, transaction,
|
|
5
|
+
* renewalInfo) first and passes the decoded event in. Subtype branching stays inside each
|
|
6
|
+
* handler: Apple subtypes share the handler's derived context and are usually a few lines each.
|
|
7
|
+
*/
|
|
8
|
+
var AppStoreNotificationHandler = class AppStoreNotificationHandler {
|
|
9
|
+
handlers;
|
|
10
|
+
constructor() {
|
|
11
|
+
this.handlers = /* @__PURE__ */ new Map();
|
|
12
|
+
}
|
|
13
|
+
static create() {
|
|
14
|
+
return new AppStoreNotificationHandler();
|
|
15
|
+
}
|
|
16
|
+
on = (type, handler) => {
|
|
17
|
+
if (!this.handlers.has(type)) this.handlers.set(type, []);
|
|
18
|
+
this.handlers.get(type)?.push(handler);
|
|
19
|
+
return this;
|
|
20
|
+
};
|
|
21
|
+
process = async (event) => {
|
|
22
|
+
const type = event.payload.notificationType;
|
|
23
|
+
const handlers = type ? this.handlers.get(type) : void 0;
|
|
24
|
+
if (!handlers) {
|
|
25
|
+
console.error(`No app store notification handler found for type: ${type}`);
|
|
26
|
+
throw new Error(`No app store notification handler found for type: ${type}`);
|
|
27
|
+
}
|
|
28
|
+
await Promise.all(handlers.map((handler) => handler(event)));
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
//#endregion
|
|
32
|
+
export { AppStoreNotificationHandler };
|
|
33
|
+
|
|
34
|
+
//# sourceMappingURL=handler.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.mjs","names":[],"sources":["../../src/app-store/handler.ts"],"sourcesContent":["import type {\n JWSRenewalInfoDecodedPayload,\n JWSTransactionDecodedPayload,\n NotificationTypeV2,\n ResponseBodyV2DecodedPayload,\n} from '@apple/app-store-server-library';\n\nexport interface AppStoreNotificationEvent {\n payload: ResponseBodyV2DecodedPayload;\n transaction: JWSTransactionDecodedPayload | null;\n renewalInfo: JWSRenewalInfoDecodedPayload | null;\n}\n\ntype NotificationHandlerFn = (event: AppStoreNotificationEvent) => Promise<void>;\n\n/**\n * App Store Server Notification dispatcher keyed by notificationType, the app-store counterpart\n * of StripeEventHandler. The caller verifies and decodes the notification (payload, transaction,\n * renewalInfo) first and passes the decoded event in. Subtype branching stays inside each\n * handler: Apple subtypes share the handler's derived context and are usually a few lines each.\n */\nexport class AppStoreNotificationHandler {\n private readonly handlers: Map<NotificationTypeV2, NotificationHandlerFn[]>;\n\n private constructor() {\n this.handlers = new Map();\n }\n\n static create() {\n return new AppStoreNotificationHandler();\n }\n\n on = (type: NotificationTypeV2, handler: NotificationHandlerFn) => {\n if (!this.handlers.has(type)) {\n this.handlers.set(type, []);\n }\n\n this.handlers.get(type)?.push(handler);\n return this;\n };\n\n process = async (event: AppStoreNotificationEvent): Promise<void> => {\n const type = event.payload.notificationType as NotificationTypeV2 | undefined;\n const handlers = type ? this.handlers.get(type) : undefined;\n if (!handlers) {\n console.error(`No app store notification handler found for type: ${type}`);\n throw new Error(`No app store notification handler found for type: ${type}`);\n }\n await Promise.all(handlers.map((handler) => handler(event)));\n };\n}\n"],"mappings":";;;;;;;AAqBA,IAAa,8BAAb,MAAa,4BAA4B;CACvC;CAEA,cAAsB;EACpB,KAAK,2BAAW,IAAI,IAAI;CAC1B;CAEA,OAAO,SAAS;EACd,OAAO,IAAI,4BAA4B;CACzC;CAEA,MAAM,MAA0B,YAAmC;EACjE,IAAI,CAAC,KAAK,SAAS,IAAI,IAAI,GACzB,KAAK,SAAS,IAAI,MAAM,CAAC,CAAC;EAG5B,KAAK,SAAS,IAAI,IAAI,CAAC,EAAE,KAAK,OAAO;EACrC,OAAO;CACT;CAEA,UAAU,OAAO,UAAoD;EACnE,MAAM,OAAO,MAAM,QAAQ;EAC3B,MAAM,WAAW,OAAO,KAAK,SAAS,IAAI,IAAI,IAAI,KAAA;EAClD,IAAI,CAAC,UAAU;GACb,QAAQ,MAAM,qDAAqD,MAAM;GACzE,MAAM,IAAI,MAAM,qDAAqD,MAAM;EAC7E;EACA,MAAM,QAAQ,IAAI,SAAS,KAAK,YAAY,QAAQ,KAAK,CAAC,CAAC;CAC7D;AACF"}
|
package/dist/app-store/index.cjs
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_app_store_config = require("./config.cjs");
|
|
3
|
+
const require_app_store_handler = require("./handler.cjs");
|
|
3
4
|
exports.AppStoreConfig = require_app_store_config.AppStoreConfig;
|
|
5
|
+
exports.AppStoreNotificationHandler = require_app_store_handler.AppStoreNotificationHandler;
|
package/dist/app-store/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.cjs","names":[],"sources":["../../src/google-play/config.ts"],"sourcesContent":["import { invariant } from '@shware/utils';\nimport ms from 'ms';\nimport type { IsUnique, Metadata } from '../types';\n\nconst addMethod = Symbol('addMethod');\n\nclass Subscription<\n NS extends Lowercase<string> = Lowercase<string>,\n PE extends string = string,\n BP extends string = string,\n PI extends string = never,\n I extends Lowercase<string> = never,\n> {\n readonly productId: string;\n readonly config: GooglePlayConfig<NS, PE, BP, PI>;\n readonly plans: Map<string, Metadata & { plan?: PE; billingPeriod?: BP }>;\n readonly defaultPlans: Set<string>;\n\n constructor(config: GooglePlayConfig<NS, PE, BP, PI>, productId: string) {\n this.config = config;\n this.productId = productId;\n this.plans = new Map();\n this.defaultPlans = new Set();\n }\n\n /**\n * Non-default base plans stay declared on purpose: they keep resolving for\n * grandfathered subscribers and serve as price-experiment arms. Never sell\n * a legacy base plan to new users unless that is intentional.\n */\n basePlan = <K extends Lowercase<string>>(\n planId: K extends I ? never : K,\n plan: PE,\n billingPeriod: BP,\n metadata: Metadata = { credits: 0, expiresIn: '0' }\n ): Subscription<NS, PE, BP, PI, I | K> => {\n invariant(!this.plans.has(planId), `Duplicate base plan ${planId}`);\n this.plans.set(planId, { ...metadata, plan, billingPeriod });\n return this;\n };\n\n default = <const T extends readonly [I, ...I[]]>(\n planIds: T & (IsUnique<T> extends true ? unknown : never)\n ) => {\n planIds.forEach((planId) => {\n invariant(this.plans.has(planId), `Default base plan ${planId} is not declared`);\n this.defaultPlans.add(planId);\n });\n this.config[addMethod](this);\n return this.config;\n };\n}\n\ntype Options = { package: string; audience: string };\n\nexport class GooglePlayConfig<\n NS extends Lowercase<string> = Lowercase<string>,\n PE extends string = string,\n BP extends string = string,\n PI extends string = never,\n> {\n readonly package: string;\n readonly audience: string;\n private onetimes: Set<string>;\n private subscriptions: Map<string, Subscription<NS, PE, BP, PI>>;\n private products: Map<string, (Metadata & { plan?: PE; billingPeriod?: BP }) | null>;\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] = (subscription: Subscription<NS, PE, BP, PI>) => {\n invariant(\n !this.subscriptions.has(subscription.productId),\n `Duplicate subscription ${subscription.productId}`\n );\n subscription.plans.forEach((metadata, planId) => {\n const id = this.getId(subscription.productId, planId);\n invariant(!this.products.has(id), `Duplicate product ${id}`);\n this.products.set(id, metadata);\n });\n this.subscriptions.set(subscription.productId, subscription);\n return this;\n };\n\n private getId = (productId: string, planId?: string) => {\n return planId ? `${productId}:${planId}` : productId;\n };\n\n private constructor(options: Options) {\n this.package = options.package;\n this.audience = options.audience;\n this.products = new Map();\n this.onetimes = new Set();\n this.subscriptions = new Map();\n }\n\n static create = <\n NS extends Lowercase<string>,\n PE extends string = string,\n BP extends string = string,\n >(\n options: Options\n ) => {\n return new GooglePlayConfig<NS, PE, BP>(options);\n };\n\n subscription = <K extends `${NS}.${Lowercase<string>}`>(productId: K extends PI ? never : K) => {\n return new Subscription<NS, PE, BP, PI | K>(this, productId);\n };\n\n onetime = <K extends `${NS}.${Lowercase<string>}`>(\n productId: K extends PI ? never : K,\n metadata: Metadata = { credits: 0, expiresIn: '0' }\n ) => {\n invariant(!this.products.has(productId), `Duplicate product ${productId}`);\n this.onetimes.add(productId);\n this.products.set(productId, metadata);\n return this as GooglePlayConfig<NS, PE, BP, PI | K>;\n };\n\n /** Base plan ids that new subscribers should purchase for the product. */\n getDefaultPlanIds = (productId: string): string[] => {\n const subscription = this.subscriptions.get(productId);\n invariant(subscription, `Subscription not found for ${productId}`);\n return Array.from(subscription.defaultPlans);\n };\n\n getPlan = (productId: string, planId: string): PE => {\n const id = this.getId(productId, planId);\n const metadata = this.products.get(id);\n invariant(metadata, `Product not found for ${id}`);\n invariant(metadata.plan, `Plan not found for ${id}`);\n return metadata.plan;\n };\n\n getBillingPeriod = (productId: string, planId: string): BP => {\n const id = this.getId(productId, planId);\n const metadata = this.products.get(id);\n invariant(metadata, `Product not found for ${id}`);\n invariant(metadata.billingPeriod, `Billing period not found for ${id}`);\n return metadata.billingPeriod;\n };\n\n getMode = (productId: string): 'payment' | 'subscription' => {\n if (this.onetimes.has(productId)) return 'payment';\n if (this.subscriptions.has(productId)) return 'subscription';\n throw new Error(`Mode not found for product ${productId}`);\n };\n\n getCreditAmount = (productId: string, planId?: string): number => {\n const id = this.getId(productId, planId);\n const metadata = this.products.get(id);\n invariant(metadata, `Product not found for ${id}`);\n return metadata.credits;\n };\n\n private getCreditExpiresIn = (productId: string, planId?: string): number => {\n const id = this.getId(productId, planId);\n const metadata = this.products.get(id);\n invariant(metadata, `Product not found for ${id}`);\n return ms(metadata.expiresIn);\n };\n\n getCreditExpiresAt = (productId: string, planId?: string): string => {\n const expiresIn = this.getCreditExpiresIn(productId, planId);\n return new Date(Date.now() + expiresIn).toISOString();\n };\n}\n"],"mappings":";;;;;;AAIA,MAAM,YAAY,OAAO,WAAW;AAEpC,IAAM,eAAN,MAME;CACA;CACA;CACA;CACA;CAEA,YAAY,QAA0C,WAAmB;EACvE,KAAK,SAAS;EACd,KAAK,YAAY;EACjB,KAAK,wBAAQ,IAAI,IAAI;EACrB,KAAK,+BAAe,IAAI,IAAI;CAC9B;;;;;;CAOA,YACE,QACA,MACA,eACA,WAAqB;EAAE,SAAS;EAAG,WAAW;CAAI,MACV;EACxC,CAAA,GAAA,cAAA,UAAA,CAAU,CAAC,KAAK,MAAM,IAAI,MAAM,GAAG,uBAAuB,QAAQ;EAClE,KAAK,MAAM,IAAI,QAAQ;GAAE,GAAG;GAAU;GAAM;EAAc,CAAC;EAC3D,OAAO;CACT;CAEA,WACE,YACG;EACH,QAAQ,SAAS,WAAW;GAC1B,CAAA,GAAA,cAAA,UAAA,CAAU,KAAK,MAAM,IAAI,MAAM,GAAG,qBAAqB,OAAO,iBAAiB;GAC/E,KAAK,aAAa,IAAI,MAAM;EAC9B,CAAC;EACD,KAAK,OAAO,UAAU,CAAC,IAAI;EAC3B,OAAO,KAAK;CACd;AACF;AAIA,IAAa,mBAAb,MAAa,iBAKX;CACA;CACA;CACA;CACA;CACA;CAIA,CAAC,cAAc,iBAA+C;EAC5D,CAAA,GAAA,cAAA,UAAA,CACE,CAAC,KAAK,cAAc,IAAI,aAAa,SAAS,GAC9C,0BAA0B,aAAa,WACzC;EACA,aAAa,MAAM,SAAS,UAAU,WAAW;GAC/C,MAAM,KAAK,KAAK,MAAM,aAAa,WAAW,MAAM;GACpD,CAAA,GAAA,cAAA,UAAA,CAAU,CAAC,KAAK,SAAS,IAAI,EAAE,GAAG,qBAAqB,IAAI;GAC3D,KAAK,SAAS,IAAI,IAAI,QAAQ;EAChC,CAAC;EACD,KAAK,cAAc,IAAI,aAAa,WAAW,YAAY;EAC3D,OAAO;CACT;CAEA,
|
|
1
|
+
{"version":3,"file":"config.cjs","names":[],"sources":["../../src/google-play/config.ts"],"sourcesContent":["import { invariant } from '@shware/utils';\nimport ms from 'ms';\nimport type { IsUnique, Metadata } from '../types';\n\nconst addMethod = Symbol('addMethod');\n\nclass Subscription<\n NS extends Lowercase<string> = Lowercase<string>,\n PE extends string = string,\n BP extends string = string,\n PI extends string = never,\n I extends Lowercase<string> = never,\n> {\n readonly productId: string;\n readonly config: GooglePlayConfig<NS, PE, BP, PI>;\n readonly plans: Map<string, Metadata & { plan?: PE; billingPeriod?: BP }>;\n readonly defaultPlans: Set<string>;\n\n constructor(config: GooglePlayConfig<NS, PE, BP, PI>, productId: string) {\n this.config = config;\n this.productId = productId;\n this.plans = new Map();\n this.defaultPlans = new Set();\n }\n\n /**\n * Non-default base plans stay declared on purpose: they keep resolving for\n * grandfathered subscribers and serve as price-experiment arms. Never sell\n * a legacy base plan to new users unless that is intentional.\n */\n basePlan = <K extends Lowercase<string>>(\n planId: K extends I ? never : K,\n plan: PE,\n billingPeriod: BP,\n metadata: Metadata = { credits: 0, expiresIn: '0' }\n ): Subscription<NS, PE, BP, PI, I | K> => {\n invariant(!this.plans.has(planId), `Duplicate base plan ${planId}`);\n this.plans.set(planId, { ...metadata, plan, billingPeriod });\n return this;\n };\n\n default = <const T extends readonly [I, ...I[]]>(\n planIds: T & (IsUnique<T> extends true ? unknown : never)\n ) => {\n planIds.forEach((planId) => {\n invariant(this.plans.has(planId), `Default base plan ${planId} is not declared`);\n this.defaultPlans.add(planId);\n });\n this.config[addMethod](this);\n return this.config;\n };\n}\n\ntype Options = { package: string; audience: string };\n\nexport class GooglePlayConfig<\n NS extends Lowercase<string> = Lowercase<string>,\n PE extends string = string,\n BP extends string = string,\n PI extends string = never,\n> {\n readonly package: string;\n readonly audience: string;\n private readonly onetimes: Set<string>;\n private readonly subscriptions: Map<string, Subscription<NS, PE, BP, PI>>;\n private readonly products: Map<string, (Metadata & { plan?: PE; billingPeriod?: BP }) | null>;\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] = (subscription: Subscription<NS, PE, BP, PI>) => {\n invariant(\n !this.subscriptions.has(subscription.productId),\n `Duplicate subscription ${subscription.productId}`\n );\n subscription.plans.forEach((metadata, planId) => {\n const id = this.getId(subscription.productId, planId);\n invariant(!this.products.has(id), `Duplicate product ${id}`);\n this.products.set(id, metadata);\n });\n this.subscriptions.set(subscription.productId, subscription);\n return this;\n };\n\n private readonly getId = (productId: string, planId?: string) => {\n return planId ? `${productId}:${planId}` : productId;\n };\n\n private constructor(options: Options) {\n this.package = options.package;\n this.audience = options.audience;\n this.products = new Map();\n this.onetimes = new Set();\n this.subscriptions = new Map();\n }\n\n static create = <\n NS extends Lowercase<string>,\n PE extends string = string,\n BP extends string = string,\n >(\n options: Options\n ) => {\n return new GooglePlayConfig<NS, PE, BP>(options);\n };\n\n subscription = <K extends `${NS}.${Lowercase<string>}`>(productId: K extends PI ? never : K) => {\n return new Subscription<NS, PE, BP, PI | K>(this, productId);\n };\n\n onetime = <K extends `${NS}.${Lowercase<string>}`>(\n productId: K extends PI ? never : K,\n metadata: Metadata = { credits: 0, expiresIn: '0' }\n ) => {\n invariant(!this.products.has(productId), `Duplicate product ${productId}`);\n this.onetimes.add(productId);\n this.products.set(productId, metadata);\n return this as GooglePlayConfig<NS, PE, BP, PI | K>;\n };\n\n /** Base plan ids that new subscribers should purchase for the product. */\n getDefaultPlanIds = (productId: string): string[] => {\n const subscription = this.subscriptions.get(productId);\n invariant(subscription, `Subscription not found for ${productId}`);\n return Array.from(subscription.defaultPlans);\n };\n\n getPlan = (productId: string, planId: string): PE => {\n const id = this.getId(productId, planId);\n const metadata = this.products.get(id);\n invariant(metadata, `Product not found for ${id}`);\n invariant(metadata.plan, `Plan not found for ${id}`);\n return metadata.plan;\n };\n\n getBillingPeriod = (productId: string, planId: string): BP => {\n const id = this.getId(productId, planId);\n const metadata = this.products.get(id);\n invariant(metadata, `Product not found for ${id}`);\n invariant(metadata.billingPeriod, `Billing period not found for ${id}`);\n return metadata.billingPeriod;\n };\n\n getMode = (productId: string): 'payment' | 'subscription' => {\n if (this.onetimes.has(productId)) return 'payment';\n if (this.subscriptions.has(productId)) return 'subscription';\n throw new Error(`Mode not found for product ${productId}`);\n };\n\n getCreditAmount = (productId: string, planId?: string): number => {\n const id = this.getId(productId, planId);\n const metadata = this.products.get(id);\n invariant(metadata, `Product not found for ${id}`);\n return metadata.credits;\n };\n\n private readonly getCreditExpiresIn = (productId: string, planId?: string): number => {\n const id = this.getId(productId, planId);\n const metadata = this.products.get(id);\n invariant(metadata, `Product not found for ${id}`);\n return ms(metadata.expiresIn);\n };\n\n getCreditExpiresAt = (productId: string, planId?: string): string => {\n const expiresIn = this.getCreditExpiresIn(productId, planId);\n return new Date(Date.now() + expiresIn).toISOString();\n };\n}\n"],"mappings":"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"}
|
|
@@ -25,11 +25,11 @@ type Options = {
|
|
|
25
25
|
declare class GooglePlayConfig<NS extends Lowercase<string> = Lowercase<string>, PE extends string = string, BP extends string = string, PI extends string = never> {
|
|
26
26
|
readonly package: string;
|
|
27
27
|
readonly audience: string;
|
|
28
|
-
private onetimes;
|
|
29
|
-
private subscriptions;
|
|
30
|
-
private products;
|
|
28
|
+
private readonly onetimes;
|
|
29
|
+
private readonly subscriptions;
|
|
30
|
+
private readonly products;
|
|
31
31
|
[addMethod]: (subscription: Subscription<NS, PE, BP, PI>) => this;
|
|
32
|
-
private getId;
|
|
32
|
+
private readonly getId;
|
|
33
33
|
private constructor();
|
|
34
34
|
static create: <NS_1 extends Lowercase<string>, PE_1 extends string = string, BP_1 extends string = string>(options: Options) => GooglePlayConfig<NS_1, PE_1, BP_1, never>;
|
|
35
35
|
subscription: <K extends `${NS}.${Lowercase<string>}`>(productId: K extends PI ? never : K) => Subscription<NS, PE, BP, K | PI, never>;
|
|
@@ -40,7 +40,7 @@ declare class GooglePlayConfig<NS extends Lowercase<string> = Lowercase<string>,
|
|
|
40
40
|
getBillingPeriod: (productId: string, planId: string) => BP;
|
|
41
41
|
getMode: (productId: string) => 'payment' | 'subscription';
|
|
42
42
|
getCreditAmount: (productId: string, planId?: string) => number;
|
|
43
|
-
private getCreditExpiresIn;
|
|
43
|
+
private readonly getCreditExpiresIn;
|
|
44
44
|
getCreditExpiresAt: (productId: string, planId?: string) => string;
|
|
45
45
|
}
|
|
46
46
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.cts","names":[],"sources":["../../src/google-play/config.ts"],"mappings":";;cAIM;cAEA,aACJ,WAAW,oBAAoB,mBAC/B,4BACA,4BACA,2BACA,UAAU;WAED;WACA,QAAQ,iBAAiB,IAAI,IAAI,IAAI;WACrC,OAAO,YAAY;IAAa,OAAO;IAAI,gBAAgB;;WAC3D,cAAc;EAEvB,YAAY,QAAQ,iBAAiB,IAAI,IAAI,IAAI,KAAK;;;;;;EAYtD,WAAY,UAAU,mBAAiB,QAC7B,UAAU,YAAY,GAAC,MACzB,IAAE,eACO,IAAE,WACP,aACT,aAAa,IAAI,IAAI,IAAI,IAAI,IAAI;EAMpC,gBAAiB,oBAAoB,MAAM,MAAI,SACpC,KAAK,SAAS,uCAAkC,iBAAA,IAAA,IAAA,IAAA;;KAWxD;EAAY;EAAiB;;cAErB,iBACX,WAAW,oBAAoB,mBAC/B,4BACA,4BACA;WAES;WACA;
|
|
1
|
+
{"version":3,"file":"config.d.cts","names":[],"sources":["../../src/google-play/config.ts"],"mappings":";;cAIM;cAEA,aACJ,WAAW,oBAAoB,mBAC/B,4BACA,4BACA,2BACA,UAAU;WAED;WACA,QAAQ,iBAAiB,IAAI,IAAI,IAAI;WACrC,OAAO,YAAY;IAAa,OAAO;IAAI,gBAAgB;;WAC3D,cAAc;EAEvB,YAAY,QAAQ,iBAAiB,IAAI,IAAI,IAAI,KAAK;;;;;;EAYtD,WAAY,UAAU,mBAAiB,QAC7B,UAAU,YAAY,GAAC,MACzB,IAAE,eACO,IAAE,WACP,aACT,aAAa,IAAI,IAAI,IAAI,IAAI,IAAI;EAMpC,gBAAiB,oBAAoB,MAAM,MAAI,SACpC,KAAK,SAAS,uCAAkC,iBAAA,IAAA,IAAA,IAAA;;KAWxD;EAAY;EAAiB;;cAErB,iBACX,WAAW,oBAAoB,mBAC/B,4BACA,4BACA;WAES;WACA;mBACQ;mBACA;mBACA;GAIhB,aAAU,cAAkB,aAAa,IAAI,IAAI,IAAI;mBAcrC;UAIV;SAQA,SACL,aAAW,mBACX,8BACA,8BAA0B,SAEjB,YAAO,iBAAA,MAAA,MAAA;EAKlB,eAAgB,aAAa,MAAM,qBAAmB,WAAa,UAAU,aAAa,MAAC,aAAA,IAAA,IAAA,IAAA,IAAA;EAI3F,UAAW,aAAa,MAAM,qBAAmB,WACpC,UAAU,aAAa,GAAC,WACzB,aAKK,iBAAiB,IAAI,IAAI,IAAI,KAAK;;EAInD,oBAAiB;EAMjB,UAAO,mBAAqB,mBAAmB;EAQ/C,mBAAgB,mBAAqB,mBAAmB;EAQxD,UAAO;EAMP,kBAAe,mBAAqB;mBAOnB;EAOjB,qBAAkB,mBAAqB"}
|
|
@@ -25,11 +25,11 @@ type Options = {
|
|
|
25
25
|
declare class GooglePlayConfig<NS extends Lowercase<string> = Lowercase<string>, PE extends string = string, BP extends string = string, PI extends string = never> {
|
|
26
26
|
readonly package: string;
|
|
27
27
|
readonly audience: string;
|
|
28
|
-
private onetimes;
|
|
29
|
-
private subscriptions;
|
|
30
|
-
private products;
|
|
28
|
+
private readonly onetimes;
|
|
29
|
+
private readonly subscriptions;
|
|
30
|
+
private readonly products;
|
|
31
31
|
[addMethod]: (subscription: Subscription<NS, PE, BP, PI>) => this;
|
|
32
|
-
private getId;
|
|
32
|
+
private readonly getId;
|
|
33
33
|
private constructor();
|
|
34
34
|
static create: <NS_1 extends Lowercase<string>, PE_1 extends string = string, BP_1 extends string = string>(options: Options) => GooglePlayConfig<NS_1, PE_1, BP_1, never>;
|
|
35
35
|
subscription: <K extends `${NS}.${Lowercase<string>}`>(productId: K extends PI ? never : K) => Subscription<NS, PE, BP, K | PI, never>;
|
|
@@ -40,7 +40,7 @@ declare class GooglePlayConfig<NS extends Lowercase<string> = Lowercase<string>,
|
|
|
40
40
|
getBillingPeriod: (productId: string, planId: string) => BP;
|
|
41
41
|
getMode: (productId: string) => 'payment' | 'subscription';
|
|
42
42
|
getCreditAmount: (productId: string, planId?: string) => number;
|
|
43
|
-
private getCreditExpiresIn;
|
|
43
|
+
private readonly getCreditExpiresIn;
|
|
44
44
|
getCreditExpiresAt: (productId: string, planId?: string) => string;
|
|
45
45
|
}
|
|
46
46
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.mts","names":[],"sources":["../../src/google-play/config.ts"],"mappings":";;cAIM;cAEA,aACJ,WAAW,oBAAoB,mBAC/B,4BACA,4BACA,2BACA,UAAU;WAED;WACA,QAAQ,iBAAiB,IAAI,IAAI,IAAI;WACrC,OAAO,YAAY;IAAa,OAAO;IAAI,gBAAgB;;WAC3D,cAAc;EAEvB,YAAY,QAAQ,iBAAiB,IAAI,IAAI,IAAI,KAAK;;;;;;EAYtD,WAAY,UAAU,mBAAiB,QAC7B,UAAU,YAAY,GAAC,MACzB,IAAE,eACO,IAAE,WACP,aACT,aAAa,IAAI,IAAI,IAAI,IAAI,IAAI;EAMpC,gBAAiB,oBAAoB,MAAM,MAAI,SACpC,KAAK,SAAS,uCAAkC,iBAAA,IAAA,IAAA,IAAA;;KAWxD;EAAY;EAAiB;;cAErB,iBACX,WAAW,oBAAoB,mBAC/B,4BACA,4BACA;WAES;WACA;
|
|
1
|
+
{"version":3,"file":"config.d.mts","names":[],"sources":["../../src/google-play/config.ts"],"mappings":";;cAIM;cAEA,aACJ,WAAW,oBAAoB,mBAC/B,4BACA,4BACA,2BACA,UAAU;WAED;WACA,QAAQ,iBAAiB,IAAI,IAAI,IAAI;WACrC,OAAO,YAAY;IAAa,OAAO;IAAI,gBAAgB;;WAC3D,cAAc;EAEvB,YAAY,QAAQ,iBAAiB,IAAI,IAAI,IAAI,KAAK;;;;;;EAYtD,WAAY,UAAU,mBAAiB,QAC7B,UAAU,YAAY,GAAC,MACzB,IAAE,eACO,IAAE,WACP,aACT,aAAa,IAAI,IAAI,IAAI,IAAI,IAAI;EAMpC,gBAAiB,oBAAoB,MAAM,MAAI,SACpC,KAAK,SAAS,uCAAkC,iBAAA,IAAA,IAAA,IAAA;;KAWxD;EAAY;EAAiB;;cAErB,iBACX,WAAW,oBAAoB,mBAC/B,4BACA,4BACA;WAES;WACA;mBACQ;mBACA;mBACA;GAIhB,aAAU,cAAkB,aAAa,IAAI,IAAI,IAAI;mBAcrC;UAIV;SAQA,SACL,aAAW,mBACX,8BACA,8BAA0B,SAEjB,YAAO,iBAAA,MAAA,MAAA;EAKlB,eAAgB,aAAa,MAAM,qBAAmB,WAAa,UAAU,aAAa,MAAC,aAAA,IAAA,IAAA,IAAA,IAAA;EAI3F,UAAW,aAAa,MAAM,qBAAmB,WACpC,UAAU,aAAa,GAAC,WACzB,aAKK,iBAAiB,IAAI,IAAI,IAAI,KAAK;;EAInD,oBAAiB;EAMjB,UAAO,mBAAqB,mBAAmB;EAQ/C,mBAAgB,mBAAqB,mBAAmB;EAQxD,UAAO;EAMP,kBAAe,mBAAqB;mBAOnB;EAOjB,qBAAkB,mBAAqB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.mjs","names":[],"sources":["../../src/google-play/config.ts"],"sourcesContent":["import { invariant } from '@shware/utils';\nimport ms from 'ms';\nimport type { IsUnique, Metadata } from '../types';\n\nconst addMethod = Symbol('addMethod');\n\nclass Subscription<\n NS extends Lowercase<string> = Lowercase<string>,\n PE extends string = string,\n BP extends string = string,\n PI extends string = never,\n I extends Lowercase<string> = never,\n> {\n readonly productId: string;\n readonly config: GooglePlayConfig<NS, PE, BP, PI>;\n readonly plans: Map<string, Metadata & { plan?: PE; billingPeriod?: BP }>;\n readonly defaultPlans: Set<string>;\n\n constructor(config: GooglePlayConfig<NS, PE, BP, PI>, productId: string) {\n this.config = config;\n this.productId = productId;\n this.plans = new Map();\n this.defaultPlans = new Set();\n }\n\n /**\n * Non-default base plans stay declared on purpose: they keep resolving for\n * grandfathered subscribers and serve as price-experiment arms. Never sell\n * a legacy base plan to new users unless that is intentional.\n */\n basePlan = <K extends Lowercase<string>>(\n planId: K extends I ? never : K,\n plan: PE,\n billingPeriod: BP,\n metadata: Metadata = { credits: 0, expiresIn: '0' }\n ): Subscription<NS, PE, BP, PI, I | K> => {\n invariant(!this.plans.has(planId), `Duplicate base plan ${planId}`);\n this.plans.set(planId, { ...metadata, plan, billingPeriod });\n return this;\n };\n\n default = <const T extends readonly [I, ...I[]]>(\n planIds: T & (IsUnique<T> extends true ? unknown : never)\n ) => {\n planIds.forEach((planId) => {\n invariant(this.plans.has(planId), `Default base plan ${planId} is not declared`);\n this.defaultPlans.add(planId);\n });\n this.config[addMethod](this);\n return this.config;\n };\n}\n\ntype Options = { package: string; audience: string };\n\nexport class GooglePlayConfig<\n NS extends Lowercase<string> = Lowercase<string>,\n PE extends string = string,\n BP extends string = string,\n PI extends string = never,\n> {\n readonly package: string;\n readonly audience: string;\n private onetimes: Set<string>;\n private subscriptions: Map<string, Subscription<NS, PE, BP, PI>>;\n private products: Map<string, (Metadata & { plan?: PE; billingPeriod?: BP }) | null>;\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] = (subscription: Subscription<NS, PE, BP, PI>) => {\n invariant(\n !this.subscriptions.has(subscription.productId),\n `Duplicate subscription ${subscription.productId}`\n );\n subscription.plans.forEach((metadata, planId) => {\n const id = this.getId(subscription.productId, planId);\n invariant(!this.products.has(id), `Duplicate product ${id}`);\n this.products.set(id, metadata);\n });\n this.subscriptions.set(subscription.productId, subscription);\n return this;\n };\n\n private getId = (productId: string, planId?: string) => {\n return planId ? `${productId}:${planId}` : productId;\n };\n\n private constructor(options: Options) {\n this.package = options.package;\n this.audience = options.audience;\n this.products = new Map();\n this.onetimes = new Set();\n this.subscriptions = new Map();\n }\n\n static create = <\n NS extends Lowercase<string>,\n PE extends string = string,\n BP extends string = string,\n >(\n options: Options\n ) => {\n return new GooglePlayConfig<NS, PE, BP>(options);\n };\n\n subscription = <K extends `${NS}.${Lowercase<string>}`>(productId: K extends PI ? never : K) => {\n return new Subscription<NS, PE, BP, PI | K>(this, productId);\n };\n\n onetime = <K extends `${NS}.${Lowercase<string>}`>(\n productId: K extends PI ? never : K,\n metadata: Metadata = { credits: 0, expiresIn: '0' }\n ) => {\n invariant(!this.products.has(productId), `Duplicate product ${productId}`);\n this.onetimes.add(productId);\n this.products.set(productId, metadata);\n return this as GooglePlayConfig<NS, PE, BP, PI | K>;\n };\n\n /** Base plan ids that new subscribers should purchase for the product. */\n getDefaultPlanIds = (productId: string): string[] => {\n const subscription = this.subscriptions.get(productId);\n invariant(subscription, `Subscription not found for ${productId}`);\n return Array.from(subscription.defaultPlans);\n };\n\n getPlan = (productId: string, planId: string): PE => {\n const id = this.getId(productId, planId);\n const metadata = this.products.get(id);\n invariant(metadata, `Product not found for ${id}`);\n invariant(metadata.plan, `Plan not found for ${id}`);\n return metadata.plan;\n };\n\n getBillingPeriod = (productId: string, planId: string): BP => {\n const id = this.getId(productId, planId);\n const metadata = this.products.get(id);\n invariant(metadata, `Product not found for ${id}`);\n invariant(metadata.billingPeriod, `Billing period not found for ${id}`);\n return metadata.billingPeriod;\n };\n\n getMode = (productId: string): 'payment' | 'subscription' => {\n if (this.onetimes.has(productId)) return 'payment';\n if (this.subscriptions.has(productId)) return 'subscription';\n throw new Error(`Mode not found for product ${productId}`);\n };\n\n getCreditAmount = (productId: string, planId?: string): number => {\n const id = this.getId(productId, planId);\n const metadata = this.products.get(id);\n invariant(metadata, `Product not found for ${id}`);\n return metadata.credits;\n };\n\n private getCreditExpiresIn = (productId: string, planId?: string): number => {\n const id = this.getId(productId, planId);\n const metadata = this.products.get(id);\n invariant(metadata, `Product not found for ${id}`);\n return ms(metadata.expiresIn);\n };\n\n getCreditExpiresAt = (productId: string, planId?: string): string => {\n const expiresIn = this.getCreditExpiresIn(productId, planId);\n return new Date(Date.now() + expiresIn).toISOString();\n };\n}\n"],"mappings":";;;AAIA,MAAM,YAAY,OAAO,WAAW;AAEpC,IAAM,eAAN,MAME;CACA;CACA;CACA;CACA;CAEA,YAAY,QAA0C,WAAmB;EACvE,KAAK,SAAS;EACd,KAAK,YAAY;EACjB,KAAK,wBAAQ,IAAI,IAAI;EACrB,KAAK,+BAAe,IAAI,IAAI;CAC9B;;;;;;CAOA,YACE,QACA,MACA,eACA,WAAqB;EAAE,SAAS;EAAG,WAAW;CAAI,MACV;EACxC,UAAU,CAAC,KAAK,MAAM,IAAI,MAAM,GAAG,uBAAuB,QAAQ;EAClE,KAAK,MAAM,IAAI,QAAQ;GAAE,GAAG;GAAU;GAAM;EAAc,CAAC;EAC3D,OAAO;CACT;CAEA,WACE,YACG;EACH,QAAQ,SAAS,WAAW;GAC1B,UAAU,KAAK,MAAM,IAAI,MAAM,GAAG,qBAAqB,OAAO,iBAAiB;GAC/E,KAAK,aAAa,IAAI,MAAM;EAC9B,CAAC;EACD,KAAK,OAAO,UAAU,CAAC,IAAI;EAC3B,OAAO,KAAK;CACd;AACF;AAIA,IAAa,mBAAb,MAAa,iBAKX;CACA;CACA;CACA;CACA;CACA;CAIA,CAAC,cAAc,iBAA+C;EAC5D,UACE,CAAC,KAAK,cAAc,IAAI,aAAa,SAAS,GAC9C,0BAA0B,aAAa,WACzC;EACA,aAAa,MAAM,SAAS,UAAU,WAAW;GAC/C,MAAM,KAAK,KAAK,MAAM,aAAa,WAAW,MAAM;GACpD,UAAU,CAAC,KAAK,SAAS,IAAI,EAAE,GAAG,qBAAqB,IAAI;GAC3D,KAAK,SAAS,IAAI,IAAI,QAAQ;EAChC,CAAC;EACD,KAAK,cAAc,IAAI,aAAa,WAAW,YAAY;EAC3D,OAAO;CACT;CAEA,
|
|
1
|
+
{"version":3,"file":"config.mjs","names":[],"sources":["../../src/google-play/config.ts"],"sourcesContent":["import { invariant } from '@shware/utils';\nimport ms from 'ms';\nimport type { IsUnique, Metadata } from '../types';\n\nconst addMethod = Symbol('addMethod');\n\nclass Subscription<\n NS extends Lowercase<string> = Lowercase<string>,\n PE extends string = string,\n BP extends string = string,\n PI extends string = never,\n I extends Lowercase<string> = never,\n> {\n readonly productId: string;\n readonly config: GooglePlayConfig<NS, PE, BP, PI>;\n readonly plans: Map<string, Metadata & { plan?: PE; billingPeriod?: BP }>;\n readonly defaultPlans: Set<string>;\n\n constructor(config: GooglePlayConfig<NS, PE, BP, PI>, productId: string) {\n this.config = config;\n this.productId = productId;\n this.plans = new Map();\n this.defaultPlans = new Set();\n }\n\n /**\n * Non-default base plans stay declared on purpose: they keep resolving for\n * grandfathered subscribers and serve as price-experiment arms. Never sell\n * a legacy base plan to new users unless that is intentional.\n */\n basePlan = <K extends Lowercase<string>>(\n planId: K extends I ? never : K,\n plan: PE,\n billingPeriod: BP,\n metadata: Metadata = { credits: 0, expiresIn: '0' }\n ): Subscription<NS, PE, BP, PI, I | K> => {\n invariant(!this.plans.has(planId), `Duplicate base plan ${planId}`);\n this.plans.set(planId, { ...metadata, plan, billingPeriod });\n return this;\n };\n\n default = <const T extends readonly [I, ...I[]]>(\n planIds: T & (IsUnique<T> extends true ? unknown : never)\n ) => {\n planIds.forEach((planId) => {\n invariant(this.plans.has(planId), `Default base plan ${planId} is not declared`);\n this.defaultPlans.add(planId);\n });\n this.config[addMethod](this);\n return this.config;\n };\n}\n\ntype Options = { package: string; audience: string };\n\nexport class GooglePlayConfig<\n NS extends Lowercase<string> = Lowercase<string>,\n PE extends string = string,\n BP extends string = string,\n PI extends string = never,\n> {\n readonly package: string;\n readonly audience: string;\n private readonly onetimes: Set<string>;\n private readonly subscriptions: Map<string, Subscription<NS, PE, BP, PI>>;\n private readonly products: Map<string, (Metadata & { plan?: PE; billingPeriod?: BP }) | null>;\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] = (subscription: Subscription<NS, PE, BP, PI>) => {\n invariant(\n !this.subscriptions.has(subscription.productId),\n `Duplicate subscription ${subscription.productId}`\n );\n subscription.plans.forEach((metadata, planId) => {\n const id = this.getId(subscription.productId, planId);\n invariant(!this.products.has(id), `Duplicate product ${id}`);\n this.products.set(id, metadata);\n });\n this.subscriptions.set(subscription.productId, subscription);\n return this;\n };\n\n private readonly getId = (productId: string, planId?: string) => {\n return planId ? `${productId}:${planId}` : productId;\n };\n\n private constructor(options: Options) {\n this.package = options.package;\n this.audience = options.audience;\n this.products = new Map();\n this.onetimes = new Set();\n this.subscriptions = new Map();\n }\n\n static create = <\n NS extends Lowercase<string>,\n PE extends string = string,\n BP extends string = string,\n >(\n options: Options\n ) => {\n return new GooglePlayConfig<NS, PE, BP>(options);\n };\n\n subscription = <K extends `${NS}.${Lowercase<string>}`>(productId: K extends PI ? never : K) => {\n return new Subscription<NS, PE, BP, PI | K>(this, productId);\n };\n\n onetime = <K extends `${NS}.${Lowercase<string>}`>(\n productId: K extends PI ? never : K,\n metadata: Metadata = { credits: 0, expiresIn: '0' }\n ) => {\n invariant(!this.products.has(productId), `Duplicate product ${productId}`);\n this.onetimes.add(productId);\n this.products.set(productId, metadata);\n return this as GooglePlayConfig<NS, PE, BP, PI | K>;\n };\n\n /** Base plan ids that new subscribers should purchase for the product. */\n getDefaultPlanIds = (productId: string): string[] => {\n const subscription = this.subscriptions.get(productId);\n invariant(subscription, `Subscription not found for ${productId}`);\n return Array.from(subscription.defaultPlans);\n };\n\n getPlan = (productId: string, planId: string): PE => {\n const id = this.getId(productId, planId);\n const metadata = this.products.get(id);\n invariant(metadata, `Product not found for ${id}`);\n invariant(metadata.plan, `Plan not found for ${id}`);\n return metadata.plan;\n };\n\n getBillingPeriod = (productId: string, planId: string): BP => {\n const id = this.getId(productId, planId);\n const metadata = this.products.get(id);\n invariant(metadata, `Product not found for ${id}`);\n invariant(metadata.billingPeriod, `Billing period not found for ${id}`);\n return metadata.billingPeriod;\n };\n\n getMode = (productId: string): 'payment' | 'subscription' => {\n if (this.onetimes.has(productId)) return 'payment';\n if (this.subscriptions.has(productId)) return 'subscription';\n throw new Error(`Mode not found for product ${productId}`);\n };\n\n getCreditAmount = (productId: string, planId?: string): number => {\n const id = this.getId(productId, planId);\n const metadata = this.products.get(id);\n invariant(metadata, `Product not found for ${id}`);\n return metadata.credits;\n };\n\n private readonly getCreditExpiresIn = (productId: string, planId?: string): number => {\n const id = this.getId(productId, planId);\n const metadata = this.products.get(id);\n invariant(metadata, `Product not found for ${id}`);\n return ms(metadata.expiresIn);\n };\n\n getCreditExpiresAt = (productId: string, planId?: string): string => {\n const expiresIn = this.getCreditExpiresIn(productId, planId);\n return new Date(Date.now() + expiresIn).toISOString();\n };\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.cjs","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\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: I | 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 default = (defaultPriceId: I) => {\n invariant(this.prices.has(defaultPriceId), `Default price ${defaultPriceId} is not declared`);\n this.defaultPriceId = defaultPriceId;\n this.config[addMethod](this as never);\n return this.config;\n };\n}\n\ntype Options = {\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 products: Map<string, Product<NS, PE, BP, PL, PI>> = new Map();\n\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 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.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 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 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 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.cjs","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\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: I | 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 default = (defaultPriceId: I) => {\n invariant(this.prices.has(defaultPriceId), `Default price ${defaultPriceId} is not declared`);\n this.defaultPriceId = defaultPriceId;\n this.config[addMethod](this as never);\n return this.config;\n };\n}\n\ntype Options = {\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 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 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.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 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 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":";;;;;;AAMA,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,CAAA,GAAA,cAAA,UAAA,CAAU,CAAC,KAAK,OAAO,IAAI,OAAO,GAAG,mBAAmB,SAAS;EACjE,KAAK,OAAO,IAAI,SAAS,QAAQ;EACjC,OAAO;CACT;CAEA,WAAW,mBAAsB;EAC/B,CAAA,GAAA,cAAA,UAAA,CAAU,KAAK,OAAO,IAAI,cAAc,GAAG,iBAAiB,eAAe,iBAAiB;EAC5F,KAAK,iBAAiB;EACtB,KAAK,OAAO,UAAU,CAAC,IAAa;EACpC,OAAO,KAAK;CACd;AACF;AAUA,IAAa,eAAb,MAAa,aAMX;CACA,2BAAsE,IAAI,IAAI;CAE9E;CACA;CACA;CACA;CACA;CAEA,IAAI,aAAuB;EACzB,OAAO,MAAM,KAAK,KAAK,SAAS,KAAK,CAAC;CACxC;CAIA,CAAC,cAAc,YAAyC;EACtD,CAAA,GAAA,cAAA,UAAA,CAAU,CAAC,KAAK,SAAS,IAAI,QAAQ,EAAE,GAAG,qBAAqB,QAAQ,IAAI;EAC3E,IAAI,QAAQ,SAAS,MACnB,KAAK,MAAM,YAAY,KAAK,SAAS,OAAO,GAC1C,CAAA,GAAA,cAAA,UAAA,CACE,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,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,CAAA,GAAA,cAAA,UAAA,CAAU,SAAS,yBAAyB,WAAW;EACvD,CAAA,GAAA,cAAA,UAAA,CAAU,QAAQ,MAAM,WAAW,UAAU,uBAAuB;EACpE,OAAO,QAAQ;CACjB;CAEA,oBAAoB,cAA0B;EAC5C,MAAM,UAAU,KAAK,SAAS,IAAI,SAAS;EAC3C,CAAA,GAAA,cAAA,UAAA,CAAU,SAAS,yBAAyB,WAAW;EACvD,CAAA,GAAA,cAAA,UAAA,CAAU,QAAQ,eAAe,WAAW,UAAU,uBAAuB;EAC7E,OAAO,QAAQ;CACjB;CAEA,WAAW,cAAkD;EAC3D,MAAM,UAAU,KAAK,SAAS,IAAI,SAAS;EAC3C,CAAA,GAAA,cAAA,UAAA,CAAU,SAAS,yBAAyB,WAAW;EACvD,OAAO,QAAQ,SAAS,OAAO,YAAY;CAC7C;CAEA,cAAc,cAA+B;EAC3C,MAAM,UAAU,KAAK,SAAS,IAAI,SAAS;EAC3C,CAAA,GAAA,cAAA,UAAA,CAAU,SAAS,yBAAyB,WAAW;EACvD,CAAA,GAAA,cAAA,UAAA,CAAU,QAAQ,gBAAgB,uCAAuC,WAAW;EACpF,OAAO,QAAQ;CACjB;CAEA,mBAAmB,WAAmB,YAA6B;EACjE,MAAM,UAAU,KAAK,SAAS,IAAI,SAAS;EAC3C,CAAA,GAAA,cAAA,UAAA,CAAU,SAAS,yBAAyB,WAAW;EACvD,IAAI,SAAS;GACX,MAAM,QAAQ,QAAQ,OAAO,IAAI,OAAkB;GACnD,CAAA,GAAA,cAAA,UAAA,CAAU,OAAO,uBAAuB,SAAS;GACjD,OAAO,MAAM;EACf;EAEA,CAAA,GAAA,cAAA,UAAA,CAAU,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,CAAA,GAAA,cAAA,UAAA,CAAU,SAAS,yBAAyB,WAAW;EACvD,IAAI,SAAS;GACX,MAAM,QAAQ,QAAQ,OAAO,IAAI,OAAkB;GACnD,CAAA,GAAA,cAAA,UAAA,CAAU,OAAO,uBAAuB,SAAS;GACjD,QAAA,GAAA,GAAA,QAAA,CAAU,MAAM,SAAS;EAC3B;EAEA,CAAA,GAAA,cAAA,UAAA,CAAU,QAAQ,gBAAgB,uCAAuC,WAAW;EACpF,QAAA,GAAA,GAAA,QAAA,CAAU,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/config.d.cts
CHANGED
|
@@ -26,7 +26,7 @@ type Options = {
|
|
|
26
26
|
allowPromotionCodes: boolean;
|
|
27
27
|
};
|
|
28
28
|
declare class StripeConfig<NS extends Lowercase<string> = Lowercase<string>, PE extends string = string, BP extends string = string, PL extends string = never, PI extends string = never> {
|
|
29
|
-
private products;
|
|
29
|
+
private readonly products;
|
|
30
30
|
returnUrl: string;
|
|
31
31
|
cancelUrl: string;
|
|
32
32
|
successUrl: `${string}session_id={CHECKOUT_SESSION_ID}${string}`;
|
|
@@ -49,7 +49,7 @@ declare class StripeConfig<NS extends Lowercase<string> = Lowercase<string>, PE
|
|
|
49
49
|
getMode: (productId: string) => 'payment' | 'subscription';
|
|
50
50
|
getPriceId: (productId: string) => PriceId;
|
|
51
51
|
getCreditAmount: (productId: string, priceId?: string) => number;
|
|
52
|
-
private getCreditExpiresIn;
|
|
52
|
+
private readonly getCreditExpiresIn;
|
|
53
53
|
getCreditExpiresAt: (productId: string, priceId?: string) => string;
|
|
54
54
|
}
|
|
55
55
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.cts","names":[],"sources":["../../src/stripe/config.ts"],"mappings":";;KAIY;cAEN;cAEA,QACJ,WAAW,oBAAoB,mBAC/B,4BACA,4BACA,2BACA,2BACA,UAAU;WAED;WACA,QAAQ,aAAa,IAAI,IAAI,IAAI,IAAI;WACrC,MAAM;WACN,eAAe;WACf,QAAQ,IAAI,SAAS;EAE9B,gBAAgB;EAEhB,YACE,QAAQ,aAAa,IAAI,IAAI,IAAI,IAAI,KACrC,YACA,OAAM,WACN,gBAAe;;;;;;EAejB,QAAS,UAAU,SAAO,SACf,UAAU,YAAY,GAAC,WACtB,aACT,QAAQ,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI;EAMnC,UAAO,gBAAoB,MAAC,aAAA,IAAA,IAAA,IAAA,IAAA;;KAQzB;EACH;EACA;EACA;EACA;EACA;;cAGW,aACX,WAAW,oBAAoB,mBAC/B,4BACA,4BACA,2BACA;
|
|
1
|
+
{"version":3,"file":"config.d.cts","names":[],"sources":["../../src/stripe/config.ts"],"mappings":";;KAIY;cAEN;cAEA,QACJ,WAAW,oBAAoB,mBAC/B,4BACA,4BACA,2BACA,2BACA,UAAU;WAED;WACA,QAAQ,aAAa,IAAI,IAAI,IAAI,IAAI;WACrC,MAAM;WACN,eAAe;WACf,QAAQ,IAAI,SAAS;EAE9B,gBAAgB;EAEhB,YACE,QAAQ,aAAa,IAAI,IAAI,IAAI,IAAI,KACrC,YACA,OAAM,WACN,gBAAe;;;;;;EAejB,QAAS,UAAU,SAAO,SACf,UAAU,YAAY,GAAC,WACtB,aACT,QAAQ,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI;EAMnC,UAAO,gBAAoB,MAAC,aAAA,IAAA,IAAA,IAAA,IAAA;;KAQzB;EACH;EACA;EACA;EACA;EACA;;cAGW,aACX,WAAW,oBAAoB,mBAC/B,4BACA,4BACA,2BACA;mBAEiB;EAEV;EACA;EACA;EACA;EACA;MAEH;GAMH,aAAU,SAAa,QAAQ,IAAI,IAAI,IAAI,IAAI;UAczC;SAQA,SAAU,aAAW,mBAAmB,qBAAmB,8BAA0B,SACjF,YAAO,aAAA,MAAA,MAAA;;;;;EASlB;KACG,aAAa,MAAM,qBAClB,WAAW,UAAU,aAAa,IACjC,QAAQ,IAAI,IAAI,IAAI,IAAI,IAAI;KAC9B,aAAa,MAAM,qBAAqB,UAAU,IAAI,UAAU,IAC/D,WAAW,UAAU,aAAa,GAClC,MAAM,GACN,kBAAkB,KAAK,YAAY,aAAa,IAC/C,QAAQ,IAAI,IAAI,OAAO,KAAK,MAAM,IAAI,IAAI;;EAK/C,UAAO,sBAAwB;EAO/B,mBAAgB,sBAAwB;EAOxC,UAAO;EAMP,aAAU,sBAAwB;EAOlC,kBAAe,mBAAqB;mBAanB;EAajB,qBAAkB,mBAAqB"}
|
package/dist/stripe/config.d.mts
CHANGED
|
@@ -26,7 +26,7 @@ type Options = {
|
|
|
26
26
|
allowPromotionCodes: boolean;
|
|
27
27
|
};
|
|
28
28
|
declare class StripeConfig<NS extends Lowercase<string> = Lowercase<string>, PE extends string = string, BP extends string = string, PL extends string = never, PI extends string = never> {
|
|
29
|
-
private products;
|
|
29
|
+
private readonly products;
|
|
30
30
|
returnUrl: string;
|
|
31
31
|
cancelUrl: string;
|
|
32
32
|
successUrl: `${string}session_id={CHECKOUT_SESSION_ID}${string}`;
|
|
@@ -49,7 +49,7 @@ declare class StripeConfig<NS extends Lowercase<string> = Lowercase<string>, PE
|
|
|
49
49
|
getMode: (productId: string) => 'payment' | 'subscription';
|
|
50
50
|
getPriceId: (productId: string) => PriceId;
|
|
51
51
|
getCreditAmount: (productId: string, priceId?: string) => number;
|
|
52
|
-
private getCreditExpiresIn;
|
|
52
|
+
private readonly getCreditExpiresIn;
|
|
53
53
|
getCreditExpiresAt: (productId: string, priceId?: string) => string;
|
|
54
54
|
}
|
|
55
55
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.mts","names":[],"sources":["../../src/stripe/config.ts"],"mappings":";;KAIY;cAEN;cAEA,QACJ,WAAW,oBAAoB,mBAC/B,4BACA,4BACA,2BACA,2BACA,UAAU;WAED;WACA,QAAQ,aAAa,IAAI,IAAI,IAAI,IAAI;WACrC,MAAM;WACN,eAAe;WACf,QAAQ,IAAI,SAAS;EAE9B,gBAAgB;EAEhB,YACE,QAAQ,aAAa,IAAI,IAAI,IAAI,IAAI,KACrC,YACA,OAAM,WACN,gBAAe;;;;;;EAejB,QAAS,UAAU,SAAO,SACf,UAAU,YAAY,GAAC,WACtB,aACT,QAAQ,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI;EAMnC,UAAO,gBAAoB,MAAC,aAAA,IAAA,IAAA,IAAA,IAAA;;KAQzB;EACH;EACA;EACA;EACA;EACA;;cAGW,aACX,WAAW,oBAAoB,mBAC/B,4BACA,4BACA,2BACA;
|
|
1
|
+
{"version":3,"file":"config.d.mts","names":[],"sources":["../../src/stripe/config.ts"],"mappings":";;KAIY;cAEN;cAEA,QACJ,WAAW,oBAAoB,mBAC/B,4BACA,4BACA,2BACA,2BACA,UAAU;WAED;WACA,QAAQ,aAAa,IAAI,IAAI,IAAI,IAAI;WACrC,MAAM;WACN,eAAe;WACf,QAAQ,IAAI,SAAS;EAE9B,gBAAgB;EAEhB,YACE,QAAQ,aAAa,IAAI,IAAI,IAAI,IAAI,KACrC,YACA,OAAM,WACN,gBAAe;;;;;;EAejB,QAAS,UAAU,SAAO,SACf,UAAU,YAAY,GAAC,WACtB,aACT,QAAQ,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI;EAMnC,UAAO,gBAAoB,MAAC,aAAA,IAAA,IAAA,IAAA,IAAA;;KAQzB;EACH;EACA;EACA;EACA;EACA;;cAGW,aACX,WAAW,oBAAoB,mBAC/B,4BACA,4BACA,2BACA;mBAEiB;EAEV;EACA;EACA;EACA;EACA;MAEH;GAMH,aAAU,SAAa,QAAQ,IAAI,IAAI,IAAI,IAAI;UAczC;SAQA,SAAU,aAAW,mBAAmB,qBAAmB,8BAA0B,SACjF,YAAO,aAAA,MAAA,MAAA;;;;;EASlB;KACG,aAAa,MAAM,qBAClB,WAAW,UAAU,aAAa,IACjC,QAAQ,IAAI,IAAI,IAAI,IAAI,IAAI;KAC9B,aAAa,MAAM,qBAAqB,UAAU,IAAI,UAAU,IAC/D,WAAW,UAAU,aAAa,GAClC,MAAM,GACN,kBAAkB,KAAK,YAAY,aAAa,IAC/C,QAAQ,IAAI,IAAI,OAAO,KAAK,MAAM,IAAI,IAAI;;EAK/C,UAAO,sBAAwB;EAO/B,mBAAgB,sBAAwB;EAOxC,UAAO;EAMP,aAAU,sBAAwB;EAOlC,kBAAe,mBAAqB;mBAanB;EAajB,qBAAkB,mBAAqB"}
|
|
@@ -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\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: I | 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 default = (defaultPriceId: I) => {\n invariant(this.prices.has(defaultPriceId), `Default price ${defaultPriceId} is not declared`);\n this.defaultPriceId = defaultPriceId;\n this.config[addMethod](this as never);\n return this.config;\n };\n}\n\ntype Options = {\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 products: Map<string, Product<NS, PE, BP, PL, PI>> = new Map();\n\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 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.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 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 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 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":";;;AAMA,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;CAEA,WAAW,mBAAsB;EAC/B,UAAU,KAAK,OAAO,IAAI,cAAc,GAAG,iBAAiB,eAAe,iBAAiB;EAC5F,KAAK,iBAAiB;EACtB,KAAK,OAAO,UAAU,CAAC,IAAa;EACpC,OAAO,KAAK;CACd;AACF;AAUA,IAAa,eAAb,MAAa,aAMX;CACA,
|
|
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\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: I | 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 default = (defaultPriceId: I) => {\n invariant(this.prices.has(defaultPriceId), `Default price ${defaultPriceId} is not declared`);\n this.defaultPriceId = defaultPriceId;\n this.config[addMethod](this as never);\n return this.config;\n };\n}\n\ntype Options = {\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 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 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.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 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 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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"}
|
package/dist/stripe/mapper.cjs
CHANGED
|
@@ -138,6 +138,31 @@ function minorUnits(currency) {
|
|
|
138
138
|
function price(value, currency) {
|
|
139
139
|
return value / minorUnits(currency);
|
|
140
140
|
}
|
|
141
|
+
const GOOGLE_ADS_TRANSACTION_ID_MAX = 64;
|
|
142
|
+
/**
|
|
143
|
+
* GA4 purchase events feed Google Ads conversions, whose transaction IDs are
|
|
144
|
+
* capped at 64 characters — but Stripe checkout session ids exceed that:
|
|
145
|
+
* around 2021 they grew from ~32 chars (`cs_live_` + 24 random chars) to ~66
|
|
146
|
+
* (`cs_live_a` + 57), with no announcement. Stripe treats id length/format as
|
|
147
|
+
* opaque and backwards-compatible, guaranteeing only that ids never exceed
|
|
148
|
+
* 255 chars, so they may grow again at any time.
|
|
149
|
+
*
|
|
150
|
+
* Dropping the fixed `cs_live_`/`cs_test_` prefix brings the id under 64
|
|
151
|
+
* while staying reversible: prepend the prefix (livemode tells which) to
|
|
152
|
+
* reconstruct the full session id for Stripe dashboard lookups. The payment
|
|
153
|
+
* intent id (~27 chars) is not a substitute — it only exists on
|
|
154
|
+
* `mode: 'payment'` sessions (subscription-mode sessions carry it on the
|
|
155
|
+
* invoice instead), and its short length is just as unguaranteed as the
|
|
156
|
+
* session id's was.
|
|
157
|
+
*
|
|
158
|
+
* Must stay deterministic: GA4/Google Ads dedupe purchases by
|
|
159
|
+
* transaction_id, and the same session may be tracked from multiple paths.
|
|
160
|
+
*/
|
|
161
|
+
function toTransactionId(sessionId) {
|
|
162
|
+
const stripped = sessionId.replace(/^cs_(live|test)_/, "");
|
|
163
|
+
if (stripped.length > GOOGLE_ADS_TRANSACTION_ID_MAX) console.warn(`transaction_id still exceeds ${GOOGLE_ADS_TRANSACTION_ID_MAX} chars after stripping the prefix — Stripe may have lengthened session ids again; truncating: ${sessionId}`);
|
|
164
|
+
return stripped.slice(0, GOOGLE_ADS_TRANSACTION_ID_MAX);
|
|
165
|
+
}
|
|
141
166
|
function getPurchaseProperties(session) {
|
|
142
167
|
let value;
|
|
143
168
|
let currency;
|
|
@@ -149,7 +174,7 @@ function getPurchaseProperties(session) {
|
|
|
149
174
|
currency = session.currency;
|
|
150
175
|
}
|
|
151
176
|
return {
|
|
152
|
-
transaction_id: session.id,
|
|
177
|
+
transaction_id: toTransactionId(session.id),
|
|
153
178
|
value: price(value, currency),
|
|
154
179
|
currency: currency.toUpperCase(),
|
|
155
180
|
coupon: session.coupon,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mapper.cjs","names":[],"sources":["../../src/stripe/mapper.ts"],"sourcesContent":["import { type Stripe } from 'stripe';\nimport type { SubscriptionStatus } 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 function mapLineItem(item: Stripe.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\nexport type PaymentStatus = 'no_payment_required' | 'paid' | 'unpaid';\n\nexport function mapCheckoutSession(session: Stripe.Checkout.Session) {\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 // The variable annotation makes the emitted .d.ts reference the local\n // PaymentStatus alias instead of stripe's internal module path (TS2883)\n const payment_status: PaymentStatus = session.payment_status;\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,\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 CheckoutSession = ReturnType<typeof mapCheckoutSession>;\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\nexport const ZERO_DECIMAL_CURRENCIES = [\n 'BIF',\n 'CLP',\n 'DJF',\n 'GNF',\n 'JPY',\n 'KMF',\n 'KRW',\n 'MGA',\n 'PYG',\n 'RWF',\n 'UGX',\n 'VND',\n 'VUV',\n 'XAF',\n 'XOF',\n 'XPF',\n];\n\nexport function minorUnits(currency: string) {\n return ZERO_DECIMAL_CURRENCIES.includes(currency.toUpperCase()) ? 1 : 100;\n}\n\nexport function price(value: number, currency: string) {\n return value / minorUnits(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\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) ?? 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: 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"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mapper.cjs","names":[],"sources":["../../src/stripe/mapper.ts"],"sourcesContent":["import { type Stripe } from 'stripe';\nimport type { SubscriptionStatus } 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 function mapLineItem(item: Stripe.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\nexport type PaymentStatus = 'no_payment_required' | 'paid' | 'unpaid';\n\nexport function mapCheckoutSession(session: Stripe.Checkout.Session) {\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 // The variable annotation makes the emitted .d.ts reference the local\n // PaymentStatus alias instead of stripe's internal module path (TS2883)\n const payment_status: PaymentStatus = session.payment_status;\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,\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 CheckoutSession = ReturnType<typeof mapCheckoutSession>;\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\nexport const ZERO_DECIMAL_CURRENCIES = [\n 'BIF',\n 'CLP',\n 'DJF',\n 'GNF',\n 'JPY',\n 'KMF',\n 'KRW',\n 'MGA',\n 'PYG',\n 'RWF',\n 'UGX',\n 'VND',\n 'VUV',\n 'XAF',\n 'XOF',\n 'XPF',\n];\n\nexport function minorUnits(currency: string) {\n return ZERO_DECIMAL_CURRENCIES.includes(currency.toUpperCase()) ? 1 : 100;\n}\n\nexport function price(value: number, currency: string) {\n return value / minorUnits(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) ?? 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"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mapper.d.cts","names":[],"sources":["../../src/stripe/mapper.ts"],"mappings":";;;iBAGgB,QAAQ,yBACtB,wBAAwB,IACvB;KAKS;EACV;EACA,MAAM,OAAO,MAAM;EACnB;EACA,gBAAgB,OAAO,MAAM;EAC7B,UAAU,OAAO;EACjB;EACA;EACA,WAAW,OAAO,MAAM;;iBAGV,SAAS,OAAO,OAAO,QAAQ;iBAa/B,YAAY,MAAM,OAAO;EAErC;EAKA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,OAAK;;KAIG;iBAEI,mBAAmB,SAAS,OAAO,SAAS;;;;;;;;;;;;;;;;;;;;;iBA8B5C,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;;iBAIY,iBAAiB,QAAQ,OAAO;EAE5C;EACA;EACA;EACA;EACA;EACA;EACA;EACA,QAAM,OAAA,cAAA;EACN;;iBAIY,UAAU,QAAQ,OAAO;EAErC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,QAAM,OAAA,OAAA;EACN;EACA;;;;;;;;;;;;KAOQ,kBAAkB,kBAAkB;KACpC;EACV;EACA,MAAM,OAAO,MAAM;EACnB;EACA,UAAU,OAAO;EACjB;IACE,IAAI,OAAO;IACX,MAAM,OAAO;IACb,aAAa,OAAO;IACpB,UAAU,OAAO;;;iBAIL,sBAAsB,QAAQ,OAAO,aAAa,SAAS;cAyB9D;iBAmBG,WAAW;iBAIX,MAAM,eAAe;UAIpB;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;UAGe;EACf;EACA;EACA;EACA;EACA;EACA;EACA,QAAQ;;UAGO;EACf;EACA;EACA;EACA,OAAO;;
|
|
1
|
+
{"version":3,"file":"mapper.d.cts","names":[],"sources":["../../src/stripe/mapper.ts"],"mappings":";;;iBAGgB,QAAQ,yBACtB,wBAAwB,IACvB;KAKS;EACV;EACA,MAAM,OAAO,MAAM;EACnB;EACA,gBAAgB,OAAO,MAAM;EAC7B,UAAU,OAAO;EACjB;EACA;EACA,WAAW,OAAO,MAAM;;iBAGV,SAAS,OAAO,OAAO,QAAQ;iBAa/B,YAAY,MAAM,OAAO;EAErC;EAKA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,OAAK;;KAIG;iBAEI,mBAAmB,SAAS,OAAO,SAAS;;;;;;;;;;;;;;;;;;;;;iBA8B5C,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;;iBAIY,iBAAiB,QAAQ,OAAO;EAE5C;EACA;EACA;EACA;EACA;EACA;EACA;EACA,QAAM,OAAA,cAAA;EACN;;iBAIY,UAAU,QAAQ,OAAO;EAErC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,QAAM,OAAA,OAAA;EACN;EACA;;;;;;;;;;;;KAOQ,kBAAkB,kBAAkB;KACpC;EACV;EACA,MAAM,OAAO,MAAM;EACnB;EACA,UAAU,OAAO;EACjB;IACE,IAAI,OAAO;IACX,MAAM,OAAO;IACb,aAAa,OAAO;IACpB,UAAU,OAAO;;;iBAIL,sBAAsB,QAAQ,OAAO,aAAa,SAAS;cAyB9D;iBAmBG,WAAW;iBAIX,MAAM,eAAe;UAIpB;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;UAGe;EACf;EACA;EACA;EACA;EACA;EACA;EACA,QAAQ;;UAGO;EACf;EACA;EACA;EACA,OAAO;;iBAqCO,sBAAsB,SAAS,kBAAkB;iBA2BjD,2BAA2B,GAAG,eAAe"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mapper.d.mts","names":[],"sources":["../../src/stripe/mapper.ts"],"mappings":";;;iBAGgB,QAAQ,yBACtB,wBAAwB,IACvB;KAKS;EACV;EACA,MAAM,OAAO,MAAM;EACnB;EACA,gBAAgB,OAAO,MAAM;EAC7B,UAAU,OAAO;EACjB;EACA;EACA,WAAW,OAAO,MAAM;;iBAGV,SAAS,OAAO,OAAO,QAAQ;iBAa/B,YAAY,MAAM,OAAO;EAErC;EAKA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,OAAK;;KAIG;iBAEI,mBAAmB,SAAS,OAAO,SAAS;;;;;;;;;;;;;;;;;;;;;iBA8B5C,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;;iBAIY,iBAAiB,QAAQ,OAAO;EAE5C;EACA;EACA;EACA;EACA;EACA;EACA;EACA,QAAM,OAAA,cAAA;EACN;;iBAIY,UAAU,QAAQ,OAAO;EAErC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,QAAM,OAAA,OAAA;EACN;EACA;;;;;;;;;;;;KAOQ,kBAAkB,kBAAkB;KACpC;EACV;EACA,MAAM,OAAO,MAAM;EACnB;EACA,UAAU,OAAO;EACjB;IACE,IAAI,OAAO;IACX,MAAM,OAAO;IACb,aAAa,OAAO;IACpB,UAAU,OAAO;;;iBAIL,sBAAsB,QAAQ,OAAO,aAAa,SAAS;cAyB9D;iBAmBG,WAAW;iBAIX,MAAM,eAAe;UAIpB;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;UAGe;EACf;EACA;EACA;EACA;EACA;EACA;EACA,QAAQ;;UAGO;EACf;EACA;EACA;EACA,OAAO;;
|
|
1
|
+
{"version":3,"file":"mapper.d.mts","names":[],"sources":["../../src/stripe/mapper.ts"],"mappings":";;;iBAGgB,QAAQ,yBACtB,wBAAwB,IACvB;KAKS;EACV;EACA,MAAM,OAAO,MAAM;EACnB;EACA,gBAAgB,OAAO,MAAM;EAC7B,UAAU,OAAO;EACjB;EACA;EACA,WAAW,OAAO,MAAM;;iBAGV,SAAS,OAAO,OAAO,QAAQ;iBAa/B,YAAY,MAAM,OAAO;EAErC;EAKA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,OAAK;;KAIG;iBAEI,mBAAmB,SAAS,OAAO,SAAS;;;;;;;;;;;;;;;;;;;;;iBA8B5C,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;;iBAIY,iBAAiB,QAAQ,OAAO;EAE5C;EACA;EACA;EACA;EACA;EACA;EACA;EACA,QAAM,OAAA,cAAA;EACN;;iBAIY,UAAU,QAAQ,OAAO;EAErC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,QAAM,OAAA,OAAA;EACN;EACA;;;;;;;;;;;;KAOQ,kBAAkB,kBAAkB;KACpC;EACV;EACA,MAAM,OAAO,MAAM;EACnB;EACA,UAAU,OAAO;EACjB;IACE,IAAI,OAAO;IACX,MAAM,OAAO;IACb,aAAa,OAAO;IACpB,UAAU,OAAO;;;iBAIL,sBAAsB,QAAQ,OAAO,aAAa,SAAS;cAyB9D;iBAmBG,WAAW;iBAIX,MAAM,eAAe;UAIpB;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;UAGe;EACf;EACA;EACA;EACA;EACA;EACA;EACA,QAAQ;;UAGO;EACf;EACA;EACA;EACA,OAAO;;iBAqCO,sBAAsB,SAAS,kBAAkB;iBA2BjD,2BAA2B,GAAG,eAAe"}
|
package/dist/stripe/mapper.mjs
CHANGED
|
@@ -137,6 +137,31 @@ function minorUnits(currency) {
|
|
|
137
137
|
function price(value, currency) {
|
|
138
138
|
return value / minorUnits(currency);
|
|
139
139
|
}
|
|
140
|
+
const GOOGLE_ADS_TRANSACTION_ID_MAX = 64;
|
|
141
|
+
/**
|
|
142
|
+
* GA4 purchase events feed Google Ads conversions, whose transaction IDs are
|
|
143
|
+
* capped at 64 characters — but Stripe checkout session ids exceed that:
|
|
144
|
+
* around 2021 they grew from ~32 chars (`cs_live_` + 24 random chars) to ~66
|
|
145
|
+
* (`cs_live_a` + 57), with no announcement. Stripe treats id length/format as
|
|
146
|
+
* opaque and backwards-compatible, guaranteeing only that ids never exceed
|
|
147
|
+
* 255 chars, so they may grow again at any time.
|
|
148
|
+
*
|
|
149
|
+
* Dropping the fixed `cs_live_`/`cs_test_` prefix brings the id under 64
|
|
150
|
+
* while staying reversible: prepend the prefix (livemode tells which) to
|
|
151
|
+
* reconstruct the full session id for Stripe dashboard lookups. The payment
|
|
152
|
+
* intent id (~27 chars) is not a substitute — it only exists on
|
|
153
|
+
* `mode: 'payment'` sessions (subscription-mode sessions carry it on the
|
|
154
|
+
* invoice instead), and its short length is just as unguaranteed as the
|
|
155
|
+
* session id's was.
|
|
156
|
+
*
|
|
157
|
+
* Must stay deterministic: GA4/Google Ads dedupe purchases by
|
|
158
|
+
* transaction_id, and the same session may be tracked from multiple paths.
|
|
159
|
+
*/
|
|
160
|
+
function toTransactionId(sessionId) {
|
|
161
|
+
const stripped = sessionId.replace(/^cs_(live|test)_/, "");
|
|
162
|
+
if (stripped.length > GOOGLE_ADS_TRANSACTION_ID_MAX) console.warn(`transaction_id still exceeds ${GOOGLE_ADS_TRANSACTION_ID_MAX} chars after stripping the prefix — Stripe may have lengthened session ids again; truncating: ${sessionId}`);
|
|
163
|
+
return stripped.slice(0, GOOGLE_ADS_TRANSACTION_ID_MAX);
|
|
164
|
+
}
|
|
140
165
|
function getPurchaseProperties(session) {
|
|
141
166
|
let value;
|
|
142
167
|
let currency;
|
|
@@ -148,7 +173,7 @@ function getPurchaseProperties(session) {
|
|
|
148
173
|
currency = session.currency;
|
|
149
174
|
}
|
|
150
175
|
return {
|
|
151
|
-
transaction_id: session.id,
|
|
176
|
+
transaction_id: toTransactionId(session.id),
|
|
152
177
|
value: price(value, currency),
|
|
153
178
|
currency: currency.toUpperCase(),
|
|
154
179
|
coupon: session.coupon,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mapper.mjs","names":[],"sources":["../../src/stripe/mapper.ts"],"sourcesContent":["import { type Stripe } from 'stripe';\nimport type { SubscriptionStatus } 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 function mapLineItem(item: Stripe.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\nexport type PaymentStatus = 'no_payment_required' | 'paid' | 'unpaid';\n\nexport function mapCheckoutSession(session: Stripe.Checkout.Session) {\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 // The variable annotation makes the emitted .d.ts reference the local\n // PaymentStatus alias instead of stripe's internal module path (TS2883)\n const payment_status: PaymentStatus = session.payment_status;\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,\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 CheckoutSession = ReturnType<typeof mapCheckoutSession>;\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\nexport const ZERO_DECIMAL_CURRENCIES = [\n 'BIF',\n 'CLP',\n 'DJF',\n 'GNF',\n 'JPY',\n 'KMF',\n 'KRW',\n 'MGA',\n 'PYG',\n 'RWF',\n 'UGX',\n 'VND',\n 'VUV',\n 'XAF',\n 'XOF',\n 'XPF',\n];\n\nexport function minorUnits(currency: string) {\n return ZERO_DECIMAL_CURRENCIES.includes(currency.toUpperCase()) ? 1 : 100;\n}\n\nexport function price(value: number, currency: string) {\n return value / minorUnits(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\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) ?? 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: 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"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mapper.mjs","names":[],"sources":["../../src/stripe/mapper.ts"],"sourcesContent":["import { type Stripe } from 'stripe';\nimport type { SubscriptionStatus } 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 function mapLineItem(item: Stripe.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\nexport type PaymentStatus = 'no_payment_required' | 'paid' | 'unpaid';\n\nexport function mapCheckoutSession(session: Stripe.Checkout.Session) {\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 // The variable annotation makes the emitted .d.ts reference the local\n // PaymentStatus alias instead of stripe's internal module path (TS2883)\n const payment_status: PaymentStatus = session.payment_status;\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,\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 CheckoutSession = ReturnType<typeof mapCheckoutSession>;\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\nexport const ZERO_DECIMAL_CURRENCIES = [\n 'BIF',\n 'CLP',\n 'DJF',\n 'GNF',\n 'JPY',\n 'KMF',\n 'KRW',\n 'MGA',\n 'PYG',\n 'RWF',\n 'UGX',\n 'VND',\n 'VUV',\n 'XAF',\n 'XOF',\n 'XPF',\n];\n\nexport function minorUnits(currency: string) {\n return ZERO_DECIMAL_CURRENCIES.includes(currency.toUpperCase()) ? 1 : 100;\n}\n\nexport function price(value: number, currency: string) {\n return value / minorUnits(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) ?? 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"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.cts","names":[],"sources":["../../src/stripe/schema.ts"],"mappings":";;;cAea,8CAAyB
|
|
1
|
+
{"version":3,"file":"schema.d.cts","names":[],"sources":["../../src/stripe/schema.ts"],"mappings":";;;cAea,8CAAyB;;;;;;;;;;;;;;;;;yBAiBpC;iBAEc,4BAA4B,0CAAoB;;;;;;;yBAO/D;UAEgB,4BAA4B,cAAc;EACzD,SAAS,OAAO,aAAa,oBAAoB;;UAGlC;EACf;EACA;EACA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.mts","names":[],"sources":["../../src/stripe/schema.ts"],"mappings":";;;cAea,8CAAyB
|
|
1
|
+
{"version":3,"file":"schema.d.mts","names":[],"sources":["../../src/stripe/schema.ts"],"mappings":";;;cAea,8CAAyB;;;;;;;;;;;;;;;;;yBAiBpC;iBAEc,4BAA4B,0CAAoB;;;;;;;yBAO/D;UAEgB,4BAA4B,cAAc;EACzD,SAAS,OAAO,aAAa,oBAAoB;;UAGlC;EACf;EACA;EACA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shware/purchase",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"@types/ms": "^2.1.0",
|
|
60
60
|
"ms": "^2.1.3",
|
|
61
61
|
"zod": "^4.4.3",
|
|
62
|
-
"@shware/utils": "^1.5.
|
|
62
|
+
"@shware/utils": "^1.5.1"
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
65
|
"@types/node": "^26.1.1",
|
|
@@ -68,9 +68,13 @@
|
|
|
68
68
|
"@repo/typescript-config": "0.0.0"
|
|
69
69
|
},
|
|
70
70
|
"peerDependencies": {
|
|
71
|
-
"
|
|
71
|
+
"@apple/app-store-server-library": "^3.1.0",
|
|
72
|
+
"stripe": "^22.3.2"
|
|
72
73
|
},
|
|
73
74
|
"peerDependenciesMeta": {
|
|
75
|
+
"@apple/app-store-server-library": {
|
|
76
|
+
"optional": true
|
|
77
|
+
},
|
|
74
78
|
"stripe": {
|
|
75
79
|
"optional": true
|
|
76
80
|
}
|