@shware/purchase 7.5.0 → 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -14
- package/dist/app-store/config.cjs +24 -3
- package/dist/app-store/config.cjs.map +1 -1
- package/dist/app-store/config.d.cts +14 -2
- package/dist/app-store/config.d.cts.map +1 -1
- package/dist/app-store/config.d.mts +14 -2
- package/dist/app-store/config.d.mts.map +1 -1
- package/dist/app-store/config.mjs +24 -4
- package/dist/app-store/config.mjs.map +1 -1
- package/dist/app-store/index.cjs +2 -0
- package/dist/app-store/index.d.cts +3 -3
- package/dist/app-store/index.d.mts +3 -3
- package/dist/app-store/index.mjs +3 -3
- package/dist/app-store/mapper.cjs +22 -0
- package/dist/app-store/mapper.cjs.map +1 -1
- package/dist/app-store/mapper.d.cts +17 -1
- package/dist/app-store/mapper.d.cts.map +1 -1
- package/dist/app-store/mapper.d.mts +17 -1
- package/dist/app-store/mapper.d.mts.map +1 -1
- package/dist/app-store/mapper.mjs +22 -1
- package/dist/app-store/mapper.mjs.map +1 -1
- package/dist/catalog.cjs +22 -0
- package/dist/catalog.cjs.map +1 -0
- package/dist/catalog.d.cts +21 -0
- package/dist/catalog.d.cts.map +1 -0
- package/dist/catalog.d.mts +21 -0
- package/dist/catalog.d.mts.map +1 -0
- package/dist/catalog.mjs +19 -0
- package/dist/catalog.mjs.map +1 -0
- package/dist/google-play/config.cjs +23 -3
- package/dist/google-play/config.cjs.map +1 -1
- package/dist/google-play/config.d.cts +14 -2
- package/dist/google-play/config.d.cts.map +1 -1
- package/dist/google-play/config.d.mts +14 -2
- package/dist/google-play/config.d.mts.map +1 -1
- package/dist/google-play/config.mjs +23 -4
- package/dist/google-play/config.mjs.map +1 -1
- package/dist/google-play/index.cjs +2 -0
- package/dist/google-play/index.d.cts +3 -3
- package/dist/google-play/index.d.mts +3 -3
- package/dist/google-play/index.mjs +3 -3
- package/dist/google-play/mapper.cjs +12 -0
- package/dist/google-play/mapper.cjs.map +1 -1
- package/dist/google-play/mapper.d.cts +10 -1
- package/dist/google-play/mapper.d.cts.map +1 -1
- package/dist/google-play/mapper.d.mts +10 -1
- package/dist/google-play/mapper.d.mts.map +1 -1
- package/dist/google-play/mapper.mjs +12 -1
- package/dist/google-play/mapper.mjs.map +1 -1
- package/dist/index.cjs +3 -0
- package/dist/index.d.cts +4 -4
- package/dist/index.d.mts +4 -4
- package/dist/index.mjs +4 -4
- package/dist/stripe/config.cjs +50 -4
- package/dist/stripe/config.cjs.map +1 -1
- package/dist/stripe/config.d.cts +36 -2
- package/dist/stripe/config.d.cts.map +1 -1
- package/dist/stripe/config.d.mts +36 -2
- package/dist/stripe/config.d.mts.map +1 -1
- package/dist/stripe/config.mjs +50 -5
- package/dist/stripe/config.mjs.map +1 -1
- package/dist/stripe/index.cjs +5 -0
- package/dist/stripe/index.d.cts +3 -2
- package/dist/stripe/index.d.mts +3 -2
- package/dist/stripe/index.mjs +3 -2
- package/dist/stripe/plan-change.cjs +400 -0
- package/dist/stripe/plan-change.cjs.map +1 -0
- package/dist/stripe/plan-change.d.cts +142 -0
- package/dist/stripe/plan-change.d.cts.map +1 -0
- package/dist/stripe/plan-change.d.mts +142 -0
- package/dist/stripe/plan-change.d.mts.map +1 -0
- package/dist/stripe/plan-change.mjs +397 -0
- package/dist/stripe/plan-change.mjs.map +1 -0
- package/package.json +1 -1
|
@@ -53,6 +53,17 @@ function mapCancellationDetails(context) {
|
|
|
53
53
|
return null;
|
|
54
54
|
}
|
|
55
55
|
/**
|
|
56
|
+
* What a paid Play subscription order was for, by its order id. Play numbers a subscription's
|
|
57
|
+
* orders from the first purchase: the initial order is `GPA.1234-5678-9012-34567`, each renewal
|
|
58
|
+
* the same id with `..0`, `..1`, ... appended. A plan change through the Play Store is a new
|
|
59
|
+
* purchase with an order id of its own, so it reads as a start, like any first purchase.
|
|
60
|
+
*
|
|
61
|
+
* ref: [Order ID](https://developer.android.com/google/play/billing/subscriptions#renewals)
|
|
62
|
+
*/
|
|
63
|
+
function mapPaymentType(orderId) {
|
|
64
|
+
return orderId.includes("..") ? "SUBSCRIPTION_RENEWAL" : "SUBSCRIPTION_START";
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
56
67
|
* When the subscription was cancelled. Only a user cancellation carries the time; for the others
|
|
57
68
|
* the caller falls back to when it learnt of it.
|
|
58
69
|
*/
|
|
@@ -62,6 +73,7 @@ function mapCanceledAt(context) {
|
|
|
62
73
|
//#endregion
|
|
63
74
|
exports.mapCanceledAt = mapCanceledAt;
|
|
64
75
|
exports.mapCancellationDetails = mapCancellationDetails;
|
|
76
|
+
exports.mapPaymentType = mapPaymentType;
|
|
65
77
|
exports.mapSubscriptionStatus = mapSubscriptionStatus;
|
|
66
78
|
|
|
67
79
|
//# sourceMappingURL=mapper.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mapper.cjs","names":[],"sources":["../../src/google-play/mapper.ts"],"sourcesContent":["import type { CancellationDetails, SubscriptionStatus } from '../subscription/index';\nimport type { GooglePlaySubscriptionState } from './real-time-developer-notification';\n\nexport function mapSubscriptionStatus(state: GooglePlaySubscriptionState): SubscriptionStatus {\n switch (state) {\n case 'SUBSCRIPTION_STATE_PENDING':\n return 'PENDING';\n case 'SUBSCRIPTION_STATE_ACTIVE':\n return 'ACTIVE';\n case 'SUBSCRIPTION_STATE_PAUSED':\n return 'PAUSED';\n case 'SUBSCRIPTION_STATE_IN_GRACE_PERIOD':\n return 'IN_GRACE_PERIOD';\n case 'SUBSCRIPTION_STATE_ON_HOLD':\n return 'ON_HOLD';\n case 'SUBSCRIPTION_STATE_CANCELED':\n return 'CANCELED';\n case 'SUBSCRIPTION_STATE_EXPIRED':\n return 'EXPIRED';\n case 'SUBSCRIPTION_STATE_PENDING_PURCHASE_CANCELED':\n return 'PENDING_PURCHASE_CANCELED';\n default:\n throw new Error(`Unknown Google Play subscription state: ${state}`);\n }\n}\n\n/** Play's cancel-survey answers in the shared vocabulary. */\nconst SURVEY_FEEDBACK: Record<string, CancellationDetails['feedback']> = {\n CANCEL_SURVEY_REASON_NOT_ENOUGH_USAGE: 'unused',\n CANCEL_SURVEY_REASON_TECHNICAL_ISSUES: 'low_quality',\n CANCEL_SURVEY_REASON_COST_RELATED: 'too_expensive',\n CANCEL_SURVEY_REASON_FOUND_BETTER_APP: 'switched_service',\n CANCEL_SURVEY_REASON_OTHERS: 'other',\n};\n\n/**\n * `canceledStateContext` of `purchases.subscriptionsv2.get`, declared structurally so this\n * module does not depend on the Play Developer API client.\n */\nexport interface CanceledStateContext {\n userInitiatedCancellation?: {\n cancelTime?: string | null;\n cancelSurveyResult?: { reason?: string | null; reasonUserInput?: string | null } | null;\n } | null;\n systemInitiatedCancellation?: object | null;\n developerInitiatedCancellation?: object | null;\n replacementCancellation?: object | null;\n}\n\n/**\n * Why a Play subscription stopped renewing. The Play Store asks the user for a reason on cancel\n * (and for free text when it is \"Other\"), which comes back in `cancelSurveyResult`. Null while\n * the subscription renews, and for a replacement: that is the linked purchase taking over, not a\n * cancellation to report.\n */\nexport function mapCancellationDetails(\n context: CanceledStateContext | null | undefined\n): CancellationDetails | null {\n if (!context) return null;\n if (context.userInitiatedCancellation) {\n const survey = context.userInitiatedCancellation.cancelSurveyResult;\n return {\n initiator: 'user',\n reason: survey?.reason ?? null,\n feedback: (survey?.reason && SURVEY_FEEDBACK[survey.reason]) || null,\n comment: survey?.reasonUserInput ?? null,\n };\n }\n if (context.systemInitiatedCancellation) {\n return { initiator: 'system', reason: 'SYSTEM_INITIATED', feedback: null, comment: null };\n }\n if (context.developerInitiatedCancellation) {\n return { initiator: 'developer', reason: 'DEVELOPER_INITIATED', feedback: null, comment: null };\n }\n return null;\n}\n\n/**\n * When the subscription was cancelled. Only a user cancellation carries the time; for the others\n * the caller falls back to when it learnt of it.\n */\nexport function mapCanceledAt(context: CanceledStateContext | null | undefined): string | null {\n return context?.userInitiatedCancellation?.cancelTime ?? null;\n}\n"],"mappings":";;AAGA,SAAgB,sBAAsB,OAAwD;CAC5F,QAAQ,OAAR;EACE,KAAK,8BACH,OAAO;EACT,KAAK,6BACH,OAAO;EACT,KAAK,6BACH,OAAO;EACT,KAAK,sCACH,OAAO;EACT,KAAK,8BACH,OAAO;EACT,KAAK,+BACH,OAAO;EACT,KAAK,8BACH,OAAO;EACT,KAAK,gDACH,OAAO;EACT,SACE,MAAM,IAAI,MAAM,2CAA2C,OAAO;CACtE;AACF;;AAGA,MAAM,kBAAmE;CACvE,uCAAuC;CACvC,uCAAuC;CACvC,mCAAmC;CACnC,uCAAuC;CACvC,6BAA6B;AAC/B;;;;;;;AAsBA,SAAgB,uBACd,SAC4B;CAC5B,IAAI,CAAC,SAAS,OAAO;CACrB,IAAI,QAAQ,2BAA2B;EACrC,MAAM,SAAS,QAAQ,0BAA0B;EACjD,OAAO;GACL,WAAW;GACX,QAAQ,QAAQ,UAAU;GAC1B,UAAW,QAAQ,UAAU,gBAAgB,OAAO,WAAY;GAChE,SAAS,QAAQ,mBAAmB;EACtC;CACF;CACA,IAAI,QAAQ,6BACV,OAAO;EAAE,WAAW;EAAU,QAAQ;EAAoB,UAAU;EAAM,SAAS;CAAK;CAE1F,IAAI,QAAQ,gCACV,OAAO;EAAE,WAAW;EAAa,QAAQ;EAAuB,UAAU;EAAM,SAAS;CAAK;CAEhG,OAAO;AACT;;;;;AAMA,SAAgB,cAAc,SAAiE;CAC7F,OAAO,SAAS,2BAA2B,cAAc;AAC3D"}
|
|
1
|
+
{"version":3,"file":"mapper.cjs","names":[],"sources":["../../src/google-play/mapper.ts"],"sourcesContent":["import type { CancellationDetails, PaymentType, SubscriptionStatus } from '../subscription/index';\nimport type { GooglePlaySubscriptionState } from './real-time-developer-notification';\n\nexport function mapSubscriptionStatus(state: GooglePlaySubscriptionState): SubscriptionStatus {\n switch (state) {\n case 'SUBSCRIPTION_STATE_PENDING':\n return 'PENDING';\n case 'SUBSCRIPTION_STATE_ACTIVE':\n return 'ACTIVE';\n case 'SUBSCRIPTION_STATE_PAUSED':\n return 'PAUSED';\n case 'SUBSCRIPTION_STATE_IN_GRACE_PERIOD':\n return 'IN_GRACE_PERIOD';\n case 'SUBSCRIPTION_STATE_ON_HOLD':\n return 'ON_HOLD';\n case 'SUBSCRIPTION_STATE_CANCELED':\n return 'CANCELED';\n case 'SUBSCRIPTION_STATE_EXPIRED':\n return 'EXPIRED';\n case 'SUBSCRIPTION_STATE_PENDING_PURCHASE_CANCELED':\n return 'PENDING_PURCHASE_CANCELED';\n default:\n throw new Error(`Unknown Google Play subscription state: ${state}`);\n }\n}\n\n/** Play's cancel-survey answers in the shared vocabulary. */\nconst SURVEY_FEEDBACK: Record<string, CancellationDetails['feedback']> = {\n CANCEL_SURVEY_REASON_NOT_ENOUGH_USAGE: 'unused',\n CANCEL_SURVEY_REASON_TECHNICAL_ISSUES: 'low_quality',\n CANCEL_SURVEY_REASON_COST_RELATED: 'too_expensive',\n CANCEL_SURVEY_REASON_FOUND_BETTER_APP: 'switched_service',\n CANCEL_SURVEY_REASON_OTHERS: 'other',\n};\n\n/**\n * `canceledStateContext` of `purchases.subscriptionsv2.get`, declared structurally so this\n * module does not depend on the Play Developer API client.\n */\nexport interface CanceledStateContext {\n userInitiatedCancellation?: {\n cancelTime?: string | null;\n cancelSurveyResult?: { reason?: string | null; reasonUserInput?: string | null } | null;\n } | null;\n systemInitiatedCancellation?: object | null;\n developerInitiatedCancellation?: object | null;\n replacementCancellation?: object | null;\n}\n\n/**\n * Why a Play subscription stopped renewing. The Play Store asks the user for a reason on cancel\n * (and for free text when it is \"Other\"), which comes back in `cancelSurveyResult`. Null while\n * the subscription renews, and for a replacement: that is the linked purchase taking over, not a\n * cancellation to report.\n */\nexport function mapCancellationDetails(\n context: CanceledStateContext | null | undefined\n): CancellationDetails | null {\n if (!context) return null;\n if (context.userInitiatedCancellation) {\n const survey = context.userInitiatedCancellation.cancelSurveyResult;\n return {\n initiator: 'user',\n reason: survey?.reason ?? null,\n feedback: (survey?.reason && SURVEY_FEEDBACK[survey.reason]) || null,\n comment: survey?.reasonUserInput ?? null,\n };\n }\n if (context.systemInitiatedCancellation) {\n return { initiator: 'system', reason: 'SYSTEM_INITIATED', feedback: null, comment: null };\n }\n if (context.developerInitiatedCancellation) {\n return { initiator: 'developer', reason: 'DEVELOPER_INITIATED', feedback: null, comment: null };\n }\n return null;\n}\n\n/**\n * What a paid Play subscription order was for, by its order id. Play numbers a subscription's\n * orders from the first purchase: the initial order is `GPA.1234-5678-9012-34567`, each renewal\n * the same id with `..0`, `..1`, ... appended. A plan change through the Play Store is a new\n * purchase with an order id of its own, so it reads as a start, like any first purchase.\n *\n * ref: [Order ID](https://developer.android.com/google/play/billing/subscriptions#renewals)\n */\nexport function mapPaymentType(orderId: string): Exclude<PaymentType, 'ONE_TIME'> {\n return orderId.includes('..') ? 'SUBSCRIPTION_RENEWAL' : 'SUBSCRIPTION_START';\n}\n\n/**\n * When the subscription was cancelled. Only a user cancellation carries the time; for the others\n * the caller falls back to when it learnt of it.\n */\nexport function mapCanceledAt(context: CanceledStateContext | null | undefined): string | null {\n return context?.userInitiatedCancellation?.cancelTime ?? null;\n}\n"],"mappings":";;AAGA,SAAgB,sBAAsB,OAAwD;CAC5F,QAAQ,OAAR;EACE,KAAK,8BACH,OAAO;EACT,KAAK,6BACH,OAAO;EACT,KAAK,6BACH,OAAO;EACT,KAAK,sCACH,OAAO;EACT,KAAK,8BACH,OAAO;EACT,KAAK,+BACH,OAAO;EACT,KAAK,8BACH,OAAO;EACT,KAAK,gDACH,OAAO;EACT,SACE,MAAM,IAAI,MAAM,2CAA2C,OAAO;CACtE;AACF;;AAGA,MAAM,kBAAmE;CACvE,uCAAuC;CACvC,uCAAuC;CACvC,mCAAmC;CACnC,uCAAuC;CACvC,6BAA6B;AAC/B;;;;;;;AAsBA,SAAgB,uBACd,SAC4B;CAC5B,IAAI,CAAC,SAAS,OAAO;CACrB,IAAI,QAAQ,2BAA2B;EACrC,MAAM,SAAS,QAAQ,0BAA0B;EACjD,OAAO;GACL,WAAW;GACX,QAAQ,QAAQ,UAAU;GAC1B,UAAW,QAAQ,UAAU,gBAAgB,OAAO,WAAY;GAChE,SAAS,QAAQ,mBAAmB;EACtC;CACF;CACA,IAAI,QAAQ,6BACV,OAAO;EAAE,WAAW;EAAU,QAAQ;EAAoB,UAAU;EAAM,SAAS;CAAK;CAE1F,IAAI,QAAQ,gCACV,OAAO;EAAE,WAAW;EAAa,QAAQ;EAAuB,UAAU;EAAM,SAAS;CAAK;CAEhG,OAAO;AACT;;;;;;;;;AAUA,SAAgB,eAAe,SAAmD;CAChF,OAAO,QAAQ,SAAS,IAAI,IAAI,yBAAyB;AAC3D;;;;;AAMA,SAAgB,cAAc,SAAiE;CAC7F,OAAO,SAAS,2BAA2B,cAAc;AAC3D"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CancellationDetails, SubscriptionStatus } from "../subscription/index.cjs";
|
|
1
|
+
import { CancellationDetails, PaymentType, SubscriptionStatus } from "../subscription/index.cjs";
|
|
2
2
|
import { GooglePlaySubscriptionState } from "./real-time-developer-notification.cjs";
|
|
3
3
|
//#region src/google-play/mapper.d.ts
|
|
4
4
|
export declare function mapSubscriptionStatus(state: GooglePlaySubscriptionState): SubscriptionStatus;
|
|
@@ -25,6 +25,15 @@ export interface CanceledStateContext {
|
|
|
25
25
|
* cancellation to report.
|
|
26
26
|
*/
|
|
27
27
|
export declare function mapCancellationDetails(context: CanceledStateContext | null | undefined): CancellationDetails | null;
|
|
28
|
+
/**
|
|
29
|
+
* What a paid Play subscription order was for, by its order id. Play numbers a subscription's
|
|
30
|
+
* orders from the first purchase: the initial order is `GPA.1234-5678-9012-34567`, each renewal
|
|
31
|
+
* the same id with `..0`, `..1`, ... appended. A plan change through the Play Store is a new
|
|
32
|
+
* purchase with an order id of its own, so it reads as a start, like any first purchase.
|
|
33
|
+
*
|
|
34
|
+
* ref: [Order ID](https://developer.android.com/google/play/billing/subscriptions#renewals)
|
|
35
|
+
*/
|
|
36
|
+
export declare function mapPaymentType(orderId: string): Exclude<PaymentType, 'ONE_TIME'>;
|
|
28
37
|
/**
|
|
29
38
|
* When the subscription was cancelled. Only a user cancellation carries the time; for the others
|
|
30
39
|
* the caller falls back to when it learnt of it.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mapper.d.cts","names":[],"sources":["../../src/google-play/mapper.ts"],"mappings":";;;wBAGgB,sBAAsB,OAAO,8BAA8B;;;;;iBAoC1D;EACf;IACE;IACA;MAAuB;MAAwB;;;EAEjD;EACA;EACA;;;;;;;;wBASc,uBACd,SAAS,0CACR;;;;;
|
|
1
|
+
{"version":3,"file":"mapper.d.cts","names":[],"sources":["../../src/google-play/mapper.ts"],"mappings":";;;wBAGgB,sBAAsB,OAAO,8BAA8B;;;;;iBAoC1D;EACf;IACE;IACA;MAAuB;MAAwB;;;EAEjD;EACA;EACA;;;;;;;;wBASc,uBACd,SAAS,0CACR;;;;;;;;;wBA4Ba,eAAe,kBAAkB,QAAQ;;;;;wBAQzC,cAAc,SAAS"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CancellationDetails, SubscriptionStatus } from "../subscription/index.mjs";
|
|
1
|
+
import { CancellationDetails, PaymentType, SubscriptionStatus } from "../subscription/index.mjs";
|
|
2
2
|
import { GooglePlaySubscriptionState } from "./real-time-developer-notification.mjs";
|
|
3
3
|
//#region src/google-play/mapper.d.ts
|
|
4
4
|
export declare function mapSubscriptionStatus(state: GooglePlaySubscriptionState): SubscriptionStatus;
|
|
@@ -25,6 +25,15 @@ export interface CanceledStateContext {
|
|
|
25
25
|
* cancellation to report.
|
|
26
26
|
*/
|
|
27
27
|
export declare function mapCancellationDetails(context: CanceledStateContext | null | undefined): CancellationDetails | null;
|
|
28
|
+
/**
|
|
29
|
+
* What a paid Play subscription order was for, by its order id. Play numbers a subscription's
|
|
30
|
+
* orders from the first purchase: the initial order is `GPA.1234-5678-9012-34567`, each renewal
|
|
31
|
+
* the same id with `..0`, `..1`, ... appended. A plan change through the Play Store is a new
|
|
32
|
+
* purchase with an order id of its own, so it reads as a start, like any first purchase.
|
|
33
|
+
*
|
|
34
|
+
* ref: [Order ID](https://developer.android.com/google/play/billing/subscriptions#renewals)
|
|
35
|
+
*/
|
|
36
|
+
export declare function mapPaymentType(orderId: string): Exclude<PaymentType, 'ONE_TIME'>;
|
|
28
37
|
/**
|
|
29
38
|
* When the subscription was cancelled. Only a user cancellation carries the time; for the others
|
|
30
39
|
* the caller falls back to when it learnt of it.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mapper.d.mts","names":[],"sources":["../../src/google-play/mapper.ts"],"mappings":";;;wBAGgB,sBAAsB,OAAO,8BAA8B;;;;;iBAoC1D;EACf;IACE;IACA;MAAuB;MAAwB;;;EAEjD;EACA;EACA;;;;;;;;wBASc,uBACd,SAAS,0CACR;;;;;
|
|
1
|
+
{"version":3,"file":"mapper.d.mts","names":[],"sources":["../../src/google-play/mapper.ts"],"mappings":";;;wBAGgB,sBAAsB,OAAO,8BAA8B;;;;;iBAoC1D;EACf;IACE;IACA;MAAuB;MAAwB;;;EAEjD;EACA;EACA;;;;;;;;wBASc,uBACd,SAAS,0CACR;;;;;;;;;wBA4Ba,eAAe,kBAAkB,QAAQ;;;;;wBAQzC,cAAc,SAAS"}
|
|
@@ -52,6 +52,17 @@ function mapCancellationDetails(context) {
|
|
|
52
52
|
return null;
|
|
53
53
|
}
|
|
54
54
|
/**
|
|
55
|
+
* What a paid Play subscription order was for, by its order id. Play numbers a subscription's
|
|
56
|
+
* orders from the first purchase: the initial order is `GPA.1234-5678-9012-34567`, each renewal
|
|
57
|
+
* the same id with `..0`, `..1`, ... appended. A plan change through the Play Store is a new
|
|
58
|
+
* purchase with an order id of its own, so it reads as a start, like any first purchase.
|
|
59
|
+
*
|
|
60
|
+
* ref: [Order ID](https://developer.android.com/google/play/billing/subscriptions#renewals)
|
|
61
|
+
*/
|
|
62
|
+
function mapPaymentType(orderId) {
|
|
63
|
+
return orderId.includes("..") ? "SUBSCRIPTION_RENEWAL" : "SUBSCRIPTION_START";
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
55
66
|
* When the subscription was cancelled. Only a user cancellation carries the time; for the others
|
|
56
67
|
* the caller falls back to when it learnt of it.
|
|
57
68
|
*/
|
|
@@ -59,6 +70,6 @@ function mapCanceledAt(context) {
|
|
|
59
70
|
return context?.userInitiatedCancellation?.cancelTime ?? null;
|
|
60
71
|
}
|
|
61
72
|
//#endregion
|
|
62
|
-
export { mapCanceledAt, mapCancellationDetails, mapSubscriptionStatus };
|
|
73
|
+
export { mapCanceledAt, mapCancellationDetails, mapPaymentType, mapSubscriptionStatus };
|
|
63
74
|
|
|
64
75
|
//# sourceMappingURL=mapper.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mapper.mjs","names":[],"sources":["../../src/google-play/mapper.ts"],"sourcesContent":["import type { CancellationDetails, SubscriptionStatus } from '../subscription/index';\nimport type { GooglePlaySubscriptionState } from './real-time-developer-notification';\n\nexport function mapSubscriptionStatus(state: GooglePlaySubscriptionState): SubscriptionStatus {\n switch (state) {\n case 'SUBSCRIPTION_STATE_PENDING':\n return 'PENDING';\n case 'SUBSCRIPTION_STATE_ACTIVE':\n return 'ACTIVE';\n case 'SUBSCRIPTION_STATE_PAUSED':\n return 'PAUSED';\n case 'SUBSCRIPTION_STATE_IN_GRACE_PERIOD':\n return 'IN_GRACE_PERIOD';\n case 'SUBSCRIPTION_STATE_ON_HOLD':\n return 'ON_HOLD';\n case 'SUBSCRIPTION_STATE_CANCELED':\n return 'CANCELED';\n case 'SUBSCRIPTION_STATE_EXPIRED':\n return 'EXPIRED';\n case 'SUBSCRIPTION_STATE_PENDING_PURCHASE_CANCELED':\n return 'PENDING_PURCHASE_CANCELED';\n default:\n throw new Error(`Unknown Google Play subscription state: ${state}`);\n }\n}\n\n/** Play's cancel-survey answers in the shared vocabulary. */\nconst SURVEY_FEEDBACK: Record<string, CancellationDetails['feedback']> = {\n CANCEL_SURVEY_REASON_NOT_ENOUGH_USAGE: 'unused',\n CANCEL_SURVEY_REASON_TECHNICAL_ISSUES: 'low_quality',\n CANCEL_SURVEY_REASON_COST_RELATED: 'too_expensive',\n CANCEL_SURVEY_REASON_FOUND_BETTER_APP: 'switched_service',\n CANCEL_SURVEY_REASON_OTHERS: 'other',\n};\n\n/**\n * `canceledStateContext` of `purchases.subscriptionsv2.get`, declared structurally so this\n * module does not depend on the Play Developer API client.\n */\nexport interface CanceledStateContext {\n userInitiatedCancellation?: {\n cancelTime?: string | null;\n cancelSurveyResult?: { reason?: string | null; reasonUserInput?: string | null } | null;\n } | null;\n systemInitiatedCancellation?: object | null;\n developerInitiatedCancellation?: object | null;\n replacementCancellation?: object | null;\n}\n\n/**\n * Why a Play subscription stopped renewing. The Play Store asks the user for a reason on cancel\n * (and for free text when it is \"Other\"), which comes back in `cancelSurveyResult`. Null while\n * the subscription renews, and for a replacement: that is the linked purchase taking over, not a\n * cancellation to report.\n */\nexport function mapCancellationDetails(\n context: CanceledStateContext | null | undefined\n): CancellationDetails | null {\n if (!context) return null;\n if (context.userInitiatedCancellation) {\n const survey = context.userInitiatedCancellation.cancelSurveyResult;\n return {\n initiator: 'user',\n reason: survey?.reason ?? null,\n feedback: (survey?.reason && SURVEY_FEEDBACK[survey.reason]) || null,\n comment: survey?.reasonUserInput ?? null,\n };\n }\n if (context.systemInitiatedCancellation) {\n return { initiator: 'system', reason: 'SYSTEM_INITIATED', feedback: null, comment: null };\n }\n if (context.developerInitiatedCancellation) {\n return { initiator: 'developer', reason: 'DEVELOPER_INITIATED', feedback: null, comment: null };\n }\n return null;\n}\n\n/**\n * When the subscription was cancelled. Only a user cancellation carries the time; for the others\n * the caller falls back to when it learnt of it.\n */\nexport function mapCanceledAt(context: CanceledStateContext | null | undefined): string | null {\n return context?.userInitiatedCancellation?.cancelTime ?? null;\n}\n"],"mappings":";AAGA,SAAgB,sBAAsB,OAAwD;CAC5F,QAAQ,OAAR;EACE,KAAK,8BACH,OAAO;EACT,KAAK,6BACH,OAAO;EACT,KAAK,6BACH,OAAO;EACT,KAAK,sCACH,OAAO;EACT,KAAK,8BACH,OAAO;EACT,KAAK,+BACH,OAAO;EACT,KAAK,8BACH,OAAO;EACT,KAAK,gDACH,OAAO;EACT,SACE,MAAM,IAAI,MAAM,2CAA2C,OAAO;CACtE;AACF;;AAGA,MAAM,kBAAmE;CACvE,uCAAuC;CACvC,uCAAuC;CACvC,mCAAmC;CACnC,uCAAuC;CACvC,6BAA6B;AAC/B;;;;;;;AAsBA,SAAgB,uBACd,SAC4B;CAC5B,IAAI,CAAC,SAAS,OAAO;CACrB,IAAI,QAAQ,2BAA2B;EACrC,MAAM,SAAS,QAAQ,0BAA0B;EACjD,OAAO;GACL,WAAW;GACX,QAAQ,QAAQ,UAAU;GAC1B,UAAW,QAAQ,UAAU,gBAAgB,OAAO,WAAY;GAChE,SAAS,QAAQ,mBAAmB;EACtC;CACF;CACA,IAAI,QAAQ,6BACV,OAAO;EAAE,WAAW;EAAU,QAAQ;EAAoB,UAAU;EAAM,SAAS;CAAK;CAE1F,IAAI,QAAQ,gCACV,OAAO;EAAE,WAAW;EAAa,QAAQ;EAAuB,UAAU;EAAM,SAAS;CAAK;CAEhG,OAAO;AACT;;;;;AAMA,SAAgB,cAAc,SAAiE;CAC7F,OAAO,SAAS,2BAA2B,cAAc;AAC3D"}
|
|
1
|
+
{"version":3,"file":"mapper.mjs","names":[],"sources":["../../src/google-play/mapper.ts"],"sourcesContent":["import type { CancellationDetails, PaymentType, SubscriptionStatus } from '../subscription/index';\nimport type { GooglePlaySubscriptionState } from './real-time-developer-notification';\n\nexport function mapSubscriptionStatus(state: GooglePlaySubscriptionState): SubscriptionStatus {\n switch (state) {\n case 'SUBSCRIPTION_STATE_PENDING':\n return 'PENDING';\n case 'SUBSCRIPTION_STATE_ACTIVE':\n return 'ACTIVE';\n case 'SUBSCRIPTION_STATE_PAUSED':\n return 'PAUSED';\n case 'SUBSCRIPTION_STATE_IN_GRACE_PERIOD':\n return 'IN_GRACE_PERIOD';\n case 'SUBSCRIPTION_STATE_ON_HOLD':\n return 'ON_HOLD';\n case 'SUBSCRIPTION_STATE_CANCELED':\n return 'CANCELED';\n case 'SUBSCRIPTION_STATE_EXPIRED':\n return 'EXPIRED';\n case 'SUBSCRIPTION_STATE_PENDING_PURCHASE_CANCELED':\n return 'PENDING_PURCHASE_CANCELED';\n default:\n throw new Error(`Unknown Google Play subscription state: ${state}`);\n }\n}\n\n/** Play's cancel-survey answers in the shared vocabulary. */\nconst SURVEY_FEEDBACK: Record<string, CancellationDetails['feedback']> = {\n CANCEL_SURVEY_REASON_NOT_ENOUGH_USAGE: 'unused',\n CANCEL_SURVEY_REASON_TECHNICAL_ISSUES: 'low_quality',\n CANCEL_SURVEY_REASON_COST_RELATED: 'too_expensive',\n CANCEL_SURVEY_REASON_FOUND_BETTER_APP: 'switched_service',\n CANCEL_SURVEY_REASON_OTHERS: 'other',\n};\n\n/**\n * `canceledStateContext` of `purchases.subscriptionsv2.get`, declared structurally so this\n * module does not depend on the Play Developer API client.\n */\nexport interface CanceledStateContext {\n userInitiatedCancellation?: {\n cancelTime?: string | null;\n cancelSurveyResult?: { reason?: string | null; reasonUserInput?: string | null } | null;\n } | null;\n systemInitiatedCancellation?: object | null;\n developerInitiatedCancellation?: object | null;\n replacementCancellation?: object | null;\n}\n\n/**\n * Why a Play subscription stopped renewing. The Play Store asks the user for a reason on cancel\n * (and for free text when it is \"Other\"), which comes back in `cancelSurveyResult`. Null while\n * the subscription renews, and for a replacement: that is the linked purchase taking over, not a\n * cancellation to report.\n */\nexport function mapCancellationDetails(\n context: CanceledStateContext | null | undefined\n): CancellationDetails | null {\n if (!context) return null;\n if (context.userInitiatedCancellation) {\n const survey = context.userInitiatedCancellation.cancelSurveyResult;\n return {\n initiator: 'user',\n reason: survey?.reason ?? null,\n feedback: (survey?.reason && SURVEY_FEEDBACK[survey.reason]) || null,\n comment: survey?.reasonUserInput ?? null,\n };\n }\n if (context.systemInitiatedCancellation) {\n return { initiator: 'system', reason: 'SYSTEM_INITIATED', feedback: null, comment: null };\n }\n if (context.developerInitiatedCancellation) {\n return { initiator: 'developer', reason: 'DEVELOPER_INITIATED', feedback: null, comment: null };\n }\n return null;\n}\n\n/**\n * What a paid Play subscription order was for, by its order id. Play numbers a subscription's\n * orders from the first purchase: the initial order is `GPA.1234-5678-9012-34567`, each renewal\n * the same id with `..0`, `..1`, ... appended. A plan change through the Play Store is a new\n * purchase with an order id of its own, so it reads as a start, like any first purchase.\n *\n * ref: [Order ID](https://developer.android.com/google/play/billing/subscriptions#renewals)\n */\nexport function mapPaymentType(orderId: string): Exclude<PaymentType, 'ONE_TIME'> {\n return orderId.includes('..') ? 'SUBSCRIPTION_RENEWAL' : 'SUBSCRIPTION_START';\n}\n\n/**\n * When the subscription was cancelled. Only a user cancellation carries the time; for the others\n * the caller falls back to when it learnt of it.\n */\nexport function mapCanceledAt(context: CanceledStateContext | null | undefined): string | null {\n return context?.userInitiatedCancellation?.cancelTime ?? null;\n}\n"],"mappings":";AAGA,SAAgB,sBAAsB,OAAwD;CAC5F,QAAQ,OAAR;EACE,KAAK,8BACH,OAAO;EACT,KAAK,6BACH,OAAO;EACT,KAAK,6BACH,OAAO;EACT,KAAK,sCACH,OAAO;EACT,KAAK,8BACH,OAAO;EACT,KAAK,+BACH,OAAO;EACT,KAAK,8BACH,OAAO;EACT,KAAK,gDACH,OAAO;EACT,SACE,MAAM,IAAI,MAAM,2CAA2C,OAAO;CACtE;AACF;;AAGA,MAAM,kBAAmE;CACvE,uCAAuC;CACvC,uCAAuC;CACvC,mCAAmC;CACnC,uCAAuC;CACvC,6BAA6B;AAC/B;;;;;;;AAsBA,SAAgB,uBACd,SAC4B;CAC5B,IAAI,CAAC,SAAS,OAAO;CACrB,IAAI,QAAQ,2BAA2B;EACrC,MAAM,SAAS,QAAQ,0BAA0B;EACjD,OAAO;GACL,WAAW;GACX,QAAQ,QAAQ,UAAU;GAC1B,UAAW,QAAQ,UAAU,gBAAgB,OAAO,WAAY;GAChE,SAAS,QAAQ,mBAAmB;EACtC;CACF;CACA,IAAI,QAAQ,6BACV,OAAO;EAAE,WAAW;EAAU,QAAQ;EAAoB,UAAU;EAAM,SAAS;CAAK;CAE1F,IAAI,QAAQ,gCACV,OAAO;EAAE,WAAW;EAAa,QAAQ;EAAuB,UAAU;EAAM,SAAS;CAAK;CAEhG,OAAO;AACT;;;;;;;;;AAUA,SAAgB,eAAe,SAAmD;CAChF,OAAO,QAAQ,SAAS,IAAI,IAAI,yBAAyB;AAC3D;;;;;AAMA,SAAgB,cAAc,SAAiE;CAC7F,OAAO,SAAS,2BAA2B,cAAc;AAC3D"}
|
package/dist/index.cjs
CHANGED
|
@@ -17,3 +17,6 @@ exports.REFUND_STATUS_TRANSITIONS = require_subscription_index.REFUND_STATUS_TRA
|
|
|
17
17
|
exports.RESUBSCRIBE_INTENTS = require_subscription_index.RESUBSCRIBE_INTENTS;
|
|
18
18
|
exports.SUBSCRIPTION_STATUSES = require_subscription_index.SUBSCRIPTION_STATUSES;
|
|
19
19
|
exports.StripeConfig = require_stripe_config.StripeConfig;
|
|
20
|
+
exports.defineAppStoreConfig = require_app_store_config.defineAppStoreConfig;
|
|
21
|
+
exports.defineGooglePlayConfig = require_google_play_config.defineGooglePlayConfig;
|
|
22
|
+
exports.defineStripeConfig = require_stripe_config.defineStripeConfig;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { AppStoreConfig } from "./app-store/config.cjs";
|
|
1
|
+
import { AppStoreConfig, defineAppStoreConfig } from "./app-store/config.cjs";
|
|
2
2
|
import { AVAILABLE_STATUSES, CANCELLATION_FEEDBACKS, CANCELLATION_INITIATORS, CLOSED_DISPUTE_STATUSES, CancellationDetails, CancellationFeedback, CancellationInitiator, DISPUTE_STATUSES, DisputeStatus, PAYMENT_TYPES, PLATFORMS, PaymentType, Platform, REFUND_STATUSES, REFUND_STATUS_TRANSITIONS, RESUBSCRIBE_INTENTS, RefundStatus, ResubscribeIntent, SUBSCRIPTION_STATUSES, SubscriptionStatus } from "./subscription/index.cjs";
|
|
3
|
-
import { GooglePlayConfig } from "./google-play/config.cjs";
|
|
4
|
-
import { StripeConfig } from "./stripe/config.cjs";
|
|
5
|
-
export { AVAILABLE_STATUSES, AppStoreConfig, CANCELLATION_FEEDBACKS, CANCELLATION_INITIATORS, CLOSED_DISPUTE_STATUSES, type CancellationDetails, type CancellationFeedback, type CancellationInitiator, DISPUTE_STATUSES, type DisputeStatus, GooglePlayConfig, PAYMENT_TYPES, PLATFORMS, type PaymentType, type Platform, REFUND_STATUSES, REFUND_STATUS_TRANSITIONS, RESUBSCRIBE_INTENTS, type RefundStatus, type ResubscribeIntent, SUBSCRIPTION_STATUSES, StripeConfig, type SubscriptionStatus };
|
|
3
|
+
import { GooglePlayConfig, defineGooglePlayConfig } from "./google-play/config.cjs";
|
|
4
|
+
import { StripeConfig, defineStripeConfig } from "./stripe/config.cjs";
|
|
5
|
+
export { AVAILABLE_STATUSES, AppStoreConfig, CANCELLATION_FEEDBACKS, CANCELLATION_INITIATORS, CLOSED_DISPUTE_STATUSES, type CancellationDetails, type CancellationFeedback, type CancellationInitiator, DISPUTE_STATUSES, type DisputeStatus, GooglePlayConfig, PAYMENT_TYPES, PLATFORMS, type PaymentType, type Platform, REFUND_STATUSES, REFUND_STATUS_TRANSITIONS, RESUBSCRIBE_INTENTS, type RefundStatus, type ResubscribeIntent, SUBSCRIPTION_STATUSES, StripeConfig, type SubscriptionStatus, defineAppStoreConfig, defineGooglePlayConfig, defineStripeConfig };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { AppStoreConfig } from "./app-store/config.mjs";
|
|
1
|
+
import { AppStoreConfig, defineAppStoreConfig } from "./app-store/config.mjs";
|
|
2
2
|
import { AVAILABLE_STATUSES, CANCELLATION_FEEDBACKS, CANCELLATION_INITIATORS, CLOSED_DISPUTE_STATUSES, CancellationDetails, CancellationFeedback, CancellationInitiator, DISPUTE_STATUSES, DisputeStatus, PAYMENT_TYPES, PLATFORMS, PaymentType, Platform, REFUND_STATUSES, REFUND_STATUS_TRANSITIONS, RESUBSCRIBE_INTENTS, RefundStatus, ResubscribeIntent, SUBSCRIPTION_STATUSES, SubscriptionStatus } from "./subscription/index.mjs";
|
|
3
|
-
import { GooglePlayConfig } from "./google-play/config.mjs";
|
|
4
|
-
import { StripeConfig } from "./stripe/config.mjs";
|
|
5
|
-
export { AVAILABLE_STATUSES, AppStoreConfig, CANCELLATION_FEEDBACKS, CANCELLATION_INITIATORS, CLOSED_DISPUTE_STATUSES, type CancellationDetails, type CancellationFeedback, type CancellationInitiator, DISPUTE_STATUSES, type DisputeStatus, GooglePlayConfig, PAYMENT_TYPES, PLATFORMS, type PaymentType, type Platform, REFUND_STATUSES, REFUND_STATUS_TRANSITIONS, RESUBSCRIBE_INTENTS, type RefundStatus, type ResubscribeIntent, SUBSCRIPTION_STATUSES, StripeConfig, type SubscriptionStatus };
|
|
3
|
+
import { GooglePlayConfig, defineGooglePlayConfig } from "./google-play/config.mjs";
|
|
4
|
+
import { StripeConfig, defineStripeConfig } from "./stripe/config.mjs";
|
|
5
|
+
export { AVAILABLE_STATUSES, AppStoreConfig, CANCELLATION_FEEDBACKS, CANCELLATION_INITIATORS, CLOSED_DISPUTE_STATUSES, type CancellationDetails, type CancellationFeedback, type CancellationInitiator, DISPUTE_STATUSES, type DisputeStatus, GooglePlayConfig, PAYMENT_TYPES, PLATFORMS, type PaymentType, type Platform, REFUND_STATUSES, REFUND_STATUS_TRANSITIONS, RESUBSCRIBE_INTENTS, type RefundStatus, type ResubscribeIntent, SUBSCRIPTION_STATUSES, StripeConfig, type SubscriptionStatus, defineAppStoreConfig, defineGooglePlayConfig, defineStripeConfig };
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AVAILABLE_STATUSES, CANCELLATION_FEEDBACKS, CANCELLATION_INITIATORS, CLOSED_DISPUTE_STATUSES, DISPUTE_STATUSES, PAYMENT_TYPES, PLATFORMS, REFUND_STATUSES, REFUND_STATUS_TRANSITIONS, RESUBSCRIBE_INTENTS, SUBSCRIPTION_STATUSES } from "./subscription/index.mjs";
|
|
2
|
-
import { AppStoreConfig } from "./app-store/config.mjs";
|
|
3
|
-
import { GooglePlayConfig } from "./google-play/config.mjs";
|
|
4
|
-
import { StripeConfig } from "./stripe/config.mjs";
|
|
5
|
-
export { AVAILABLE_STATUSES, AppStoreConfig, CANCELLATION_FEEDBACKS, CANCELLATION_INITIATORS, CLOSED_DISPUTE_STATUSES, DISPUTE_STATUSES, GooglePlayConfig, PAYMENT_TYPES, PLATFORMS, REFUND_STATUSES, REFUND_STATUS_TRANSITIONS, RESUBSCRIBE_INTENTS, SUBSCRIPTION_STATUSES, StripeConfig };
|
|
2
|
+
import { AppStoreConfig, defineAppStoreConfig } from "./app-store/config.mjs";
|
|
3
|
+
import { GooglePlayConfig, defineGooglePlayConfig } from "./google-play/config.mjs";
|
|
4
|
+
import { StripeConfig, defineStripeConfig } from "./stripe/config.mjs";
|
|
5
|
+
export { AVAILABLE_STATUSES, AppStoreConfig, CANCELLATION_FEEDBACKS, CANCELLATION_INITIATORS, CLOSED_DISPUTE_STATUSES, DISPUTE_STATUSES, GooglePlayConfig, PAYMENT_TYPES, PLATFORMS, REFUND_STATUSES, REFUND_STATUS_TRANSITIONS, RESUBSCRIBE_INTENTS, SUBSCRIPTION_STATUSES, StripeConfig, defineAppStoreConfig, defineGooglePlayConfig, defineStripeConfig };
|
package/dist/stripe/config.cjs
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
|
|
3
|
+
const require_catalog = require("../catalog.cjs");
|
|
3
4
|
let _shware_utils = require("@shware/utils");
|
|
4
5
|
let ms = require("ms");
|
|
5
6
|
ms = require_runtime.__toESM(ms, 1);
|
|
6
7
|
//#region src/stripe/config.ts
|
|
7
8
|
const addMethod = Symbol("addMethod");
|
|
9
|
+
/** The private constructor's one door, for this module's `define` function only. */
|
|
10
|
+
const construct = Symbol("construct");
|
|
8
11
|
var Product = class {
|
|
9
12
|
id;
|
|
10
13
|
config;
|
|
@@ -62,6 +65,9 @@ var StripeConfig = class StripeConfig {
|
|
|
62
65
|
successUrl;
|
|
63
66
|
cancellationCouponId;
|
|
64
67
|
allowPromotionCodes;
|
|
68
|
+
namespace;
|
|
69
|
+
plans;
|
|
70
|
+
billingPeriods;
|
|
65
71
|
get productIds() {
|
|
66
72
|
return Array.from(this.products.keys());
|
|
67
73
|
}
|
|
@@ -73,6 +79,10 @@ var StripeConfig = class StripeConfig {
|
|
|
73
79
|
return this;
|
|
74
80
|
};
|
|
75
81
|
constructor(options) {
|
|
82
|
+
require_catalog.assertCatalog(options);
|
|
83
|
+
this.namespace = options.namespace;
|
|
84
|
+
this.plans = options.plans;
|
|
85
|
+
this.billingPeriods = options.billingPeriods;
|
|
76
86
|
this.mode = options.mode;
|
|
77
87
|
this.returnUrl = options.returnUrl;
|
|
78
88
|
this.cancelUrl = options.cancelUrl;
|
|
@@ -80,14 +90,16 @@ var StripeConfig = class StripeConfig {
|
|
|
80
90
|
this.cancellationCouponId = options.cancellationCouponId;
|
|
81
91
|
this.allowPromotionCodes = options.allowPromotionCodes;
|
|
82
92
|
}
|
|
83
|
-
static
|
|
84
|
-
return new StripeConfig(options);
|
|
85
|
-
};
|
|
93
|
+
static [construct] = (options) => new StripeConfig(options);
|
|
86
94
|
/**
|
|
87
95
|
* One-time products take no extra args; subscriptions require both plan and billingPeriod.
|
|
88
96
|
* A plan+period pair may be declared on only one product — version its prices in that chain.
|
|
89
97
|
*/
|
|
90
|
-
product = (productId, plan = null, billingPeriod = null) =>
|
|
98
|
+
product = (productId, plan = null, billingPeriod = null) => {
|
|
99
|
+
require_catalog.assertInNamespace(this.namespace, productId);
|
|
100
|
+
require_catalog.assertDeclared(this, plan, billingPeriod);
|
|
101
|
+
return new Product(this, productId, plan, billingPeriod);
|
|
102
|
+
};
|
|
91
103
|
getPlan = (productId) => {
|
|
92
104
|
const product = this.products.get(productId);
|
|
93
105
|
(0, _shware_utils.invariant)(product, `Product not found for ${productId}`);
|
|
@@ -105,6 +117,15 @@ var StripeConfig = class StripeConfig {
|
|
|
105
117
|
(0, _shware_utils.invariant)(product, `Product not found for ${productId}`);
|
|
106
118
|
return product.plan === null ? "payment" : "subscription";
|
|
107
119
|
};
|
|
120
|
+
/**
|
|
121
|
+
* Whether the config declares `productId` and sells it as a subscription, so `getPlan` and
|
|
122
|
+
* `getBillingPeriod` hold for it. False, not a throw, for an id it does not know: a product that
|
|
123
|
+
* left the config (renamed, retired) or one named by metadata set outside the app.
|
|
124
|
+
*/
|
|
125
|
+
isSubscription = (productId) => {
|
|
126
|
+
const product = this.products.get(productId);
|
|
127
|
+
return product !== void 0 && product.plan !== null;
|
|
128
|
+
};
|
|
108
129
|
/** The price new checkouts are created with: the default, or its test twin in test mode. */
|
|
109
130
|
getPriceId = (productId) => {
|
|
110
131
|
const product = this.products.get(productId);
|
|
@@ -122,6 +143,22 @@ var StripeConfig = class StripeConfig {
|
|
|
122
143
|
for (const product of this.products.values()) if (product.prices.has(priceId)) return product.id;
|
|
123
144
|
return null;
|
|
124
145
|
};
|
|
146
|
+
/**
|
|
147
|
+
* How switching from one subscription product to another relates them (see
|
|
148
|
+
* {@link PlanComparison}), by the order `plans` and `billingPeriods` were declared in. Throws for
|
|
149
|
+
* a product that is not a subscription.
|
|
150
|
+
*/
|
|
151
|
+
comparePlans = (fromProductId, toProductId) => {
|
|
152
|
+
const level = (productId) => ({
|
|
153
|
+
plan: this.plans.indexOf(this.getPlan(productId)),
|
|
154
|
+
period: this.billingPeriods.indexOf(this.getBillingPeriod(productId))
|
|
155
|
+
});
|
|
156
|
+
const from = level(fromProductId);
|
|
157
|
+
const to = level(toProductId);
|
|
158
|
+
if (to.plan !== from.plan) return to.plan > from.plan ? "upgrade" : "downgrade";
|
|
159
|
+
if (to.period !== from.period) return to.period > from.period ? "longer" : "shorter";
|
|
160
|
+
return "same";
|
|
161
|
+
};
|
|
125
162
|
getCreditAmount = (productId, priceId) => {
|
|
126
163
|
const product = this.products.get(productId);
|
|
127
164
|
(0, _shware_utils.invariant)(product, `Product not found for ${productId}`);
|
|
@@ -149,7 +186,16 @@ var StripeConfig = class StripeConfig {
|
|
|
149
186
|
return new Date(Date.now() + expiresIn).toISOString();
|
|
150
187
|
};
|
|
151
188
|
};
|
|
189
|
+
/**
|
|
190
|
+
* Declare the Stripe product catalog. Every type parameter is inferred from the options:
|
|
191
|
+
* the namespace from `namespace`, the plans and billing periods from `plans` and `billingPeriods`
|
|
192
|
+
* (`const`, so a literal array needs no `as const`). Products are then added with the fluent chain.
|
|
193
|
+
*/
|
|
194
|
+
function defineStripeConfig(options) {
|
|
195
|
+
return StripeConfig[construct](options);
|
|
196
|
+
}
|
|
152
197
|
//#endregion
|
|
153
198
|
exports.StripeConfig = StripeConfig;
|
|
199
|
+
exports.defineStripeConfig = defineStripeConfig;
|
|
154
200
|
|
|
155
201
|
//# sourceMappingURL=config.cjs.map
|
|
@@ -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\n/**\n * Which Stripe object space the config addresses. Test and live prices are\n * disjoint, so a config is built for exactly one of them. Derive it from the\n * secret key (`sk_test_` vs `sk_live_`) rather than from the deployment\n * environment: the key is what actually decides which space API calls hit.\n */\nexport type StripeMode = 'live' | 'test';\n\nconst addMethod = Symbol('addMethod');\n\nclass Product<\n NS extends Lowercase<string> = Lowercase<string>,\n PE extends string = string,\n BP extends string = string,\n PL extends string = never,\n PI extends string = never,\n I extends PriceId = never,\n> {\n readonly id: string;\n readonly config: StripeConfig<NS, PE, BP, PL, PI>;\n readonly plan: PE | null;\n readonly billingPeriod: BP | null;\n readonly prices: Map<PriceId, Metadata>;\n\n defaultPriceId: PriceId | null;\n\n constructor(\n config: StripeConfig<NS, PE, BP, PL, PI>,\n id: string,\n plan: PE | null = null,\n billingPeriod: BP | null = null\n ) {\n this.id = id;\n this.config = config;\n this.plan = plan;\n this.billingPeriod = billingPeriod;\n this.prices = new Map();\n this.defaultPriceId = null;\n }\n\n /**\n * Non-default prices stay declared on purpose: they keep resolving for\n * grandfathered subscribers and serve as price-experiment arms. Never\n * create new checkouts with a legacy price unless that is intentional.\n */\n price = <K extends PriceId>(\n priceId: K extends I ? never : K,\n metadata: Metadata = { credits: 0, expiresIn: '0' }\n ): Product<NS, PE, BP, PL, PI, I | K> => {\n invariant(!this.prices.has(priceId), `Duplicate price ${priceId}`);\n this.prices.set(priceId, metadata);\n return this as Product<NS, PE, BP, PL, PI, I | K>;\n };\n\n /**\n * `test` is the sandbox twin of the default price. In test mode it is what\n * checkouts are created with, and it resolves to the default price's\n * entitlement, so credits are declared once and cannot drift between the\n * two modes. Legacy prices need no twin: a sandbox holds no grandfathered\n * subscribers. Live mode ignores it, so a test price reaching a live config\n * still fails as an unknown price instead of being granted an entitlement.\n */\n default = (defaultPriceId: I, twin?: { test: PriceId }) => {\n const metadata = this.prices.get(defaultPriceId);\n invariant(metadata, `Default price ${defaultPriceId} is not declared`);\n if (this.config.mode === 'test') {\n invariant(twin, `Test price not declared for default ${defaultPriceId} of ${this.id}`);\n invariant(!this.prices.has(twin.test), `Duplicate price ${twin.test}`);\n this.prices.set(twin.test, metadata);\n this.defaultPriceId = twin.test;\n } else {\n this.defaultPriceId = defaultPriceId;\n }\n this.config[addMethod](this);\n return this.config;\n };\n}\n\ntype Options = {\n mode: StripeMode;\n returnUrl: string;\n cancelUrl: string;\n successUrl: `${string}session_id={CHECKOUT_SESSION_ID}${string}`;\n cancellationCouponId: string;\n allowPromotionCodes: boolean;\n};\n\nexport class StripeConfig<\n NS extends Lowercase<string> = Lowercase<string>,\n PE extends string = string,\n BP extends string = string,\n PL extends string = never,\n PI extends string = never,\n> {\n private readonly products: Map<string, Product<NS, PE, BP, PL, PI>> = new Map();\n\n public readonly mode: StripeMode;\n public returnUrl: string;\n public cancelUrl: string;\n public successUrl: `${string}session_id={CHECKOUT_SESSION_ID}${string}`;\n public cancellationCouponId: string;\n public allowPromotionCodes: boolean;\n\n get productIds(): string[] {\n return Array.from(this.products.keys());\n }\n\n // runtime mirror of the type-level guards: generic accumulation only\n // protects a single fluent chain, not statement-style re-entry or casts\n [addMethod] = (product: Product<NS, PE, BP, PL, PI>) => {\n invariant(!this.products.has(product.id), `Duplicate product ${product.id}`);\n // A Stripe price belongs to one Stripe product, and getProductId must have one answer.\n for (const existing of this.products.values()) {\n for (const priceId of product.prices.keys()) {\n invariant(!existing.prices.has(priceId), `Duplicate price ${priceId} on ${existing.id}`);\n }\n }\n if (product.plan !== null) {\n for (const existing of this.products.values()) {\n invariant(\n existing.plan !== product.plan || existing.billingPeriod !== product.billingPeriod,\n `Duplicate plan ${product.plan}:${product.billingPeriod}`\n );\n }\n }\n this.products.set(product.id, product);\n return this;\n };\n\n private constructor(options: Options) {\n this.mode = options.mode;\n this.returnUrl = options.returnUrl;\n this.cancelUrl = options.cancelUrl;\n this.successUrl = options.successUrl;\n this.cancellationCouponId = options.cancellationCouponId;\n this.allowPromotionCodes = options.allowPromotionCodes;\n }\n\n static create = <NS extends Lowercase<string>, PE extends string, BP extends string = string>(\n options: Options\n ) => {\n return new StripeConfig<NS, PE, BP>(options);\n };\n\n /**\n * One-time products take no extra args; subscriptions require both plan and billingPeriod.\n * A plan+period pair may be declared on only one product — version its prices in that chain.\n */\n product: {\n <K extends `${NS}.${Lowercase<string>}`>(\n productId: K extends PI ? never : K\n ): Product<NS, PE, BP, PL, K | PI>;\n <K extends `${NS}.${Lowercase<string>}`, L extends PE, P extends BP>(\n productId: K extends PI ? never : K,\n plan: L,\n billingPeriod: `${L}:${P}` extends PL ? never : P\n ): Product<NS, PE, BP, `${L}:${P}` | PL, K | PI>;\n } = (productId: string, plan: PE | null = null, billingPeriod: BP | null = null) =>\n // oxlint-disable-next-line typescript/no-unnecessary-type-assertion -- widens to the overloads' accumulated generics\n new Product(this, productId, plan, billingPeriod) as never;\n\n getPlan = (productId: string): PE => {\n const product = this.products.get(productId);\n invariant(product, `Product not found for ${productId}`);\n invariant(product.plan, `Product ${productId} is not a subscription`);\n return product.plan;\n };\n\n getBillingPeriod = (productId: string): BP => {\n const product = this.products.get(productId);\n invariant(product, `Product not found for ${productId}`);\n invariant(product.billingPeriod, `Product ${productId} is not a subscription`);\n return product.billingPeriod;\n };\n\n getMode = (productId: string): 'payment' | 'subscription' => {\n const product = this.products.get(productId);\n invariant(product, `Product not found for ${productId}`);\n return product.plan === null ? 'payment' : 'subscription';\n };\n\n /** The price new checkouts are created with: the default, or its test twin in test mode. */\n getPriceId = (productId: string): PriceId => {\n const product = this.products.get(productId);\n invariant(product, `Product not found for ${productId}`);\n invariant(product.defaultPriceId, `Default price not found for product ${productId}`);\n return product.defaultPriceId;\n };\n\n /**\n * The product that declares `priceId` (a legacy price or the test twin included), or null for a\n * price this config does not know: one set up by hand in the dashboard, or a test price reaching\n * a live config. Reading the product from the price a Stripe object charged, rather than from\n * metadata that may already name the next plan, is what an invoice should be credited by.\n */\n getProductId = (priceId: string): string | null => {\n for (const product of this.products.values()) {\n if (product.prices.has(priceId as PriceId)) return product.id;\n }\n return null;\n };\n\n getCreditAmount = (productId: string, priceId?: string): number => {\n const product = this.products.get(productId);\n invariant(product, `Product not found for ${productId}`);\n if (priceId) {\n const price = product.prices.get(priceId as PriceId);\n invariant(price, `Price not found for ${priceId}`);\n return price.credits;\n }\n\n invariant(product.defaultPriceId, `Default price not found for product ${productId}`);\n return product.prices.get(product.defaultPriceId)?.credits ?? 0;\n };\n\n private readonly getCreditExpiresIn = (productId: string, priceId?: string): number => {\n const product = this.products.get(productId);\n invariant(product, `Product not found for ${productId}`);\n if (priceId) {\n const price = product.prices.get(priceId as PriceId);\n invariant(price, `Price not found for ${priceId}`);\n return ms(price.expiresIn);\n }\n\n invariant(product.defaultPriceId, `Default price not found for product ${productId}`);\n return ms(product.prices.get(product.defaultPriceId)?.expiresIn ?? '0');\n };\n\n getCreditExpiresAt = (productId: string, priceId?: string): string => {\n const expiresIn = this.getCreditExpiresIn(productId, priceId);\n return new Date(Date.now() + expiresIn).toISOString();\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"config.cjs","names":[],"sources":["../../src/stripe/config.ts"],"sourcesContent":["import { invariant } from '@shware/utils';\nimport ms from 'ms';\nimport { type Catalog, assertCatalog, assertDeclared, assertInNamespace } from '../catalog';\nimport type { Metadata } from '../types';\n\nexport type PriceId = `price_${string}`;\n\n/**\n * Which Stripe object space the config addresses. Test and live prices are\n * disjoint, so a config is built for exactly one of them. Derive it from the\n * secret key (`sk_test_` vs `sk_live_`) rather than from the deployment\n * environment: the key is what actually decides which space API calls hit.\n */\nexport type StripeMode = 'live' | 'test';\n\nconst addMethod = Symbol('addMethod');\n/** The private constructor's one door, for this module's `define` function only. */\nconst construct = Symbol('construct');\n\nclass Product<\n NS extends Lowercase<string> = Lowercase<string>,\n PE extends string = string,\n BP extends string = string,\n PL extends string = never,\n PI extends string = never,\n I extends PriceId = never,\n> {\n readonly id: string;\n readonly config: StripeConfig<NS, PE, BP, PL, PI>;\n readonly plan: PE | null;\n readonly billingPeriod: BP | null;\n readonly prices: Map<PriceId, Metadata>;\n\n defaultPriceId: PriceId | null;\n\n constructor(\n config: StripeConfig<NS, PE, BP, PL, PI>,\n id: string,\n plan: PE | null = null,\n billingPeriod: BP | null = null\n ) {\n this.id = id;\n this.config = config;\n this.plan = plan;\n this.billingPeriod = billingPeriod;\n this.prices = new Map();\n this.defaultPriceId = null;\n }\n\n /**\n * Non-default prices stay declared on purpose: they keep resolving for\n * grandfathered subscribers and serve as price-experiment arms. Never\n * create new checkouts with a legacy price unless that is intentional.\n */\n price = <K extends PriceId>(\n priceId: K extends I ? never : K,\n metadata: Metadata = { credits: 0, expiresIn: '0' }\n ): Product<NS, PE, BP, PL, PI, I | K> => {\n invariant(!this.prices.has(priceId), `Duplicate price ${priceId}`);\n this.prices.set(priceId, metadata);\n return this as Product<NS, PE, BP, PL, PI, I | K>;\n };\n\n /**\n * `test` is the sandbox twin of the default price. In test mode it is what\n * checkouts are created with, and it resolves to the default price's\n * entitlement, so credits are declared once and cannot drift between the\n * two modes. Legacy prices need no twin: a sandbox holds no grandfathered\n * subscribers. Live mode ignores it, so a test price reaching a live config\n * still fails as an unknown price instead of being granted an entitlement.\n */\n default = (defaultPriceId: I, twin?: { test: PriceId }) => {\n const metadata = this.prices.get(defaultPriceId);\n invariant(metadata, `Default price ${defaultPriceId} is not declared`);\n if (this.config.mode === 'test') {\n invariant(twin, `Test price not declared for default ${defaultPriceId} of ${this.id}`);\n invariant(!this.prices.has(twin.test), `Duplicate price ${twin.test}`);\n this.prices.set(twin.test, metadata);\n this.defaultPriceId = twin.test;\n } else {\n this.defaultPriceId = defaultPriceId;\n }\n this.config[addMethod](this);\n return this.config;\n };\n}\n\n/** `plans` and `billingPeriods` are the order {@link StripeConfig.comparePlans} reads. */\ntype Options<NS extends Lowercase<string>, PE extends string, BP extends string> = Catalog<\n NS,\n PE,\n BP\n> & {\n mode: StripeMode;\n returnUrl: string;\n cancelUrl: string;\n successUrl: `${string}session_id={CHECKOUT_SESSION_ID}${string}`;\n cancellationCouponId: string;\n allowPromotionCodes: boolean;\n};\n\n/**\n * How a switch between two subscription products relates them, by plan level first and billing\n * period second:\n * - `upgrade` / `downgrade`: a higher or lower plan, whatever the periods.\n * - `longer` / `shorter`: the same plan billed over a longer or shorter period (monthly → yearly\n * is `longer`). Apple calls these crossgrades.\n * - `same`: the same plan and period.\n */\nexport type PlanComparison = 'upgrade' | 'downgrade' | 'longer' | 'shorter' | 'same';\n\nexport class StripeConfig<\n NS extends Lowercase<string> = Lowercase<string>,\n PE extends string = string,\n BP extends string = string,\n PL extends string = never,\n PI extends string = never,\n> {\n private readonly products: Map<string, Product<NS, PE, BP, PL, PI>> = new Map();\n\n public readonly mode: StripeMode;\n public returnUrl: string;\n public cancelUrl: string;\n public successUrl: `${string}session_id={CHECKOUT_SESSION_ID}${string}`;\n public cancellationCouponId: string;\n public allowPromotionCodes: boolean;\n public readonly namespace: NS;\n public readonly plans: readonly PE[];\n public readonly billingPeriods: readonly BP[];\n\n get productIds(): string[] {\n return Array.from(this.products.keys());\n }\n\n // runtime mirror of the type-level guards: generic accumulation only\n // protects a single fluent chain, not statement-style re-entry or casts\n [addMethod] = (product: Product<NS, PE, BP, PL, PI>) => {\n invariant(!this.products.has(product.id), `Duplicate product ${product.id}`);\n // A Stripe price belongs to one Stripe product, and getProductId must have one answer.\n for (const existing of this.products.values()) {\n for (const priceId of product.prices.keys()) {\n invariant(!existing.prices.has(priceId), `Duplicate price ${priceId} on ${existing.id}`);\n }\n }\n if (product.plan !== null) {\n for (const existing of this.products.values()) {\n invariant(\n existing.plan !== product.plan || existing.billingPeriod !== product.billingPeriod,\n `Duplicate plan ${product.plan}:${product.billingPeriod}`\n );\n }\n }\n this.products.set(product.id, product);\n return this;\n };\n\n private constructor(options: Options<NS, PE, BP>) {\n assertCatalog(options);\n this.namespace = options.namespace;\n this.plans = options.plans;\n this.billingPeriods = options.billingPeriods;\n this.mode = options.mode;\n this.returnUrl = options.returnUrl;\n this.cancelUrl = options.cancelUrl;\n this.successUrl = options.successUrl;\n this.cancellationCouponId = options.cancellationCouponId;\n this.allowPromotionCodes = options.allowPromotionCodes;\n }\n\n static [construct] = <NS extends Lowercase<string>, PE extends string, BP extends string>(\n options: Options<NS, PE, BP>\n ) => new StripeConfig<NS, PE, BP>(options);\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 assertInNamespace(this.namespace, productId);\n assertDeclared(this, plan, billingPeriod);\n // oxlint-disable-next-line typescript/no-unnecessary-type-assertion -- widens to the overloads' accumulated generics\n return new Product(this, productId, plan, billingPeriod) as never;\n };\n\n getPlan = (productId: string): PE => {\n const product = this.products.get(productId);\n invariant(product, `Product not found for ${productId}`);\n invariant(product.plan, `Product ${productId} is not a subscription`);\n return product.plan;\n };\n\n getBillingPeriod = (productId: string): BP => {\n const product = this.products.get(productId);\n invariant(product, `Product not found for ${productId}`);\n invariant(product.billingPeriod, `Product ${productId} is not a subscription`);\n return product.billingPeriod;\n };\n\n getMode = (productId: string): 'payment' | 'subscription' => {\n const product = this.products.get(productId);\n invariant(product, `Product not found for ${productId}`);\n return product.plan === null ? 'payment' : 'subscription';\n };\n\n /**\n * Whether the config declares `productId` and sells it as a subscription, so `getPlan` and\n * `getBillingPeriod` hold for it. False, not a throw, for an id it does not know: a product that\n * left the config (renamed, retired) or one named by metadata set outside the app.\n */\n isSubscription = (productId: string): boolean => {\n const product = this.products.get(productId);\n return product !== undefined && product.plan !== null;\n };\n\n /** The price new checkouts are created with: the default, or its test twin in test mode. */\n getPriceId = (productId: string): PriceId => {\n const product = this.products.get(productId);\n invariant(product, `Product not found for ${productId}`);\n invariant(product.defaultPriceId, `Default price not found for product ${productId}`);\n return product.defaultPriceId;\n };\n\n /**\n * The product that declares `priceId` (a legacy price or the test twin included), or null for a\n * price this config does not know: one set up by hand in the dashboard, or a test price reaching\n * a live config. Reading the product from the price a Stripe object charged, rather than from\n * metadata that may already name the next plan, is what an invoice should be credited by.\n */\n getProductId = (priceId: string): string | null => {\n for (const product of this.products.values()) {\n if (product.prices.has(priceId as PriceId)) return product.id;\n }\n return null;\n };\n\n /**\n * How switching from one subscription product to another relates them (see\n * {@link PlanComparison}), by the order `plans` and `billingPeriods` were declared in. Throws for\n * a product that is not a subscription.\n */\n comparePlans = (fromProductId: string, toProductId: string): PlanComparison => {\n const level = (productId: string) => ({\n plan: this.plans.indexOf(this.getPlan(productId)),\n period: this.billingPeriods.indexOf(this.getBillingPeriod(productId)),\n });\n const from = level(fromProductId);\n const to = level(toProductId);\n if (to.plan !== from.plan) return to.plan > from.plan ? 'upgrade' : 'downgrade';\n if (to.period !== from.period) return to.period > from.period ? 'longer' : 'shorter';\n return 'same';\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\n/**\n * Declare the Stripe product catalog. Every type parameter is inferred from the options:\n * the namespace from `namespace`, the plans and billing periods from `plans` and `billingPeriods`\n * (`const`, so a literal array needs no `as const`). Products are then added with the fluent chain.\n */\nexport function defineStripeConfig<\n const NS extends Lowercase<string>,\n const PE extends string,\n const BP extends string,\n>(options: Options<NS, PE, BP>): StripeConfig<NS, PE, BP> {\n return StripeConfig[construct](options);\n}\n"],"mappings":"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"}
|
package/dist/stripe/config.d.cts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Catalog } from "../catalog.cjs";
|
|
1
2
|
import { Metadata } from "../types.cjs";
|
|
2
3
|
//#region src/stripe/config.d.ts
|
|
3
4
|
export type PriceId = `price_${string}`;
|
|
@@ -9,6 +10,8 @@ export type PriceId = `price_${string}`;
|
|
|
9
10
|
*/
|
|
10
11
|
export type StripeMode = 'live' | 'test';
|
|
11
12
|
declare const addMethod: unique symbol;
|
|
13
|
+
/** The private constructor's one door, for this module's `define` function only. */
|
|
14
|
+
declare const construct: unique symbol;
|
|
12
15
|
declare class Product<NS extends Lowercase<string> = Lowercase<string>, PE extends string = string, BP extends string = string, PL extends string = never, PI extends string = never, I extends PriceId = never> {
|
|
13
16
|
readonly id: string;
|
|
14
17
|
readonly config: StripeConfig<NS, PE, BP, PL, PI>;
|
|
@@ -35,7 +38,8 @@ declare class Product<NS extends Lowercase<string> = Lowercase<string>, PE exten
|
|
|
35
38
|
test: PriceId;
|
|
36
39
|
}) => StripeConfig<NS, PE, BP, PL, PI>;
|
|
37
40
|
}
|
|
38
|
-
|
|
41
|
+
/** `plans` and `billingPeriods` are the order {@link StripeConfig.comparePlans} reads. */
|
|
42
|
+
type Options<NS extends Lowercase<string>, PE extends string, BP extends string> = Catalog<NS, PE, BP> & {
|
|
39
43
|
mode: StripeMode;
|
|
40
44
|
returnUrl: string;
|
|
41
45
|
cancelUrl: string;
|
|
@@ -43,6 +47,15 @@ type Options = {
|
|
|
43
47
|
cancellationCouponId: string;
|
|
44
48
|
allowPromotionCodes: boolean;
|
|
45
49
|
};
|
|
50
|
+
/**
|
|
51
|
+
* How a switch between two subscription products relates them, by plan level first and billing
|
|
52
|
+
* period second:
|
|
53
|
+
* - `upgrade` / `downgrade`: a higher or lower plan, whatever the periods.
|
|
54
|
+
* - `longer` / `shorter`: the same plan billed over a longer or shorter period (monthly → yearly
|
|
55
|
+
* is `longer`). Apple calls these crossgrades.
|
|
56
|
+
* - `same`: the same plan and period.
|
|
57
|
+
*/
|
|
58
|
+
export type PlanComparison = 'upgrade' | 'downgrade' | 'longer' | 'shorter' | 'same';
|
|
46
59
|
export 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> {
|
|
47
60
|
private readonly products;
|
|
48
61
|
readonly mode: StripeMode;
|
|
@@ -51,10 +64,13 @@ export declare class StripeConfig<NS extends Lowercase<string> = Lowercase<strin
|
|
|
51
64
|
successUrl: `${string}session_id={CHECKOUT_SESSION_ID}${string}`;
|
|
52
65
|
cancellationCouponId: string;
|
|
53
66
|
allowPromotionCodes: boolean;
|
|
67
|
+
readonly namespace: NS;
|
|
68
|
+
readonly plans: readonly PE[];
|
|
69
|
+
readonly billingPeriods: readonly BP[];
|
|
54
70
|
get productIds(): string[];
|
|
55
71
|
[addMethod]: (product: Product<NS, PE, BP, PL, PI>) => this;
|
|
56
72
|
private constructor();
|
|
57
|
-
static
|
|
73
|
+
static [construct]: <NS_1 extends Lowercase<string>, PE_1 extends string, BP_1 extends string>(options: Options<NS_1, PE_1, BP_1>) => StripeConfig<NS_1, PE_1, BP_1, never, never>;
|
|
58
74
|
/**
|
|
59
75
|
* One-time products take no extra args; subscriptions require both plan and billingPeriod.
|
|
60
76
|
* A plan+period pair may be declared on only one product — version its prices in that chain.
|
|
@@ -66,6 +82,12 @@ export declare class StripeConfig<NS extends Lowercase<string> = Lowercase<strin
|
|
|
66
82
|
getPlan: (productId: string) => PE;
|
|
67
83
|
getBillingPeriod: (productId: string) => BP;
|
|
68
84
|
getMode: (productId: string) => 'payment' | 'subscription';
|
|
85
|
+
/**
|
|
86
|
+
* Whether the config declares `productId` and sells it as a subscription, so `getPlan` and
|
|
87
|
+
* `getBillingPeriod` hold for it. False, not a throw, for an id it does not know: a product that
|
|
88
|
+
* left the config (renamed, retired) or one named by metadata set outside the app.
|
|
89
|
+
*/
|
|
90
|
+
isSubscription: (productId: string) => boolean;
|
|
69
91
|
/** The price new checkouts are created with: the default, or its test twin in test mode. */
|
|
70
92
|
getPriceId: (productId: string) => PriceId;
|
|
71
93
|
/**
|
|
@@ -75,10 +97,22 @@ export declare class StripeConfig<NS extends Lowercase<string> = Lowercase<strin
|
|
|
75
97
|
* metadata that may already name the next plan, is what an invoice should be credited by.
|
|
76
98
|
*/
|
|
77
99
|
getProductId: (priceId: string) => string | null;
|
|
100
|
+
/**
|
|
101
|
+
* How switching from one subscription product to another relates them (see
|
|
102
|
+
* {@link PlanComparison}), by the order `plans` and `billingPeriods` were declared in. Throws for
|
|
103
|
+
* a product that is not a subscription.
|
|
104
|
+
*/
|
|
105
|
+
comparePlans: (fromProductId: string, toProductId: string) => PlanComparison;
|
|
78
106
|
getCreditAmount: (productId: string, priceId?: string) => number;
|
|
79
107
|
private readonly getCreditExpiresIn;
|
|
80
108
|
getCreditExpiresAt: (productId: string, priceId?: string) => string;
|
|
81
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* Declare the Stripe product catalog. Every type parameter is inferred from the options:
|
|
112
|
+
* the namespace from `namespace`, the plans and billing periods from `plans` and `billingPeriods`
|
|
113
|
+
* (`const`, so a literal array needs no `as const`). Products are then added with the fluent chain.
|
|
114
|
+
*/
|
|
115
|
+
export declare function defineStripeConfig<const NS extends Lowercase<string>, const PE extends string, const BP extends string>(options: Options<NS, PE, BP>): StripeConfig<NS, PE, BP>;
|
|
82
116
|
//#endregion
|
|
83
117
|
export {};
|
|
84
118
|
//# sourceMappingURL=config.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.cts","names":[],"sources":["../../src/stripe/config.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"config.d.cts","names":[],"sources":["../../src/stripe/config.ts"],"mappings":";;;YAKY;;;;;;;YAQA;cAEN;;cAEA;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;;;;;;;;;EAcnC,UAAO,gBAAoB,GAAC;IAAW,MAAM;QAAS,aAAA,IAAA,IAAA,IAAA,IAAA;;;KAiBnD,QAAQ,WAAW,mBAAmB,mBAAmB,qBAAqB,QACjF,IACA,IACA;EAEA,MAAM;EACN;EACA;EACA;EACA;EACA;;;;;;;;;;YAWU;qBAEC,aACX,WAAW,oBAAoB,mBAC/B,4BACA,4BACA,2BACA;mBAEiB;WAED,MAAM;EACf;EACA;EACA;EACA;EACA;WACS,WAAW;WACX,gBAAgB;WAChB,yBAAyB;MAErC;GAMH,aAAU,SAAa,QAAQ,IAAI,IAAI,IAAI,IAAI;UAoBzC;UAaC,aAAc,aAAW,mBAAmB,qBAAmB,qBAAiB,SAC7E,QAAQ,MAAI,MAAI,UAAG,aAAA,MAAA,MAAA;;;;;EAO9B;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;;EAQ/C,UAAO,sBAAwB;EAO/B,mBAAgB,sBAAwB;EAOxC,UAAO;;;;;;EAWP,iBAAc;;EAMd,aAAU,sBAAwB;;;;;;;EAalC,eAAY;;;;;;EAYZ,eAAY,uBAAyB,wBAAwB;EAY7D,kBAAe,mBAAqB;mBAanB;EAajB,qBAAkB,mBAAqB;;;;;;;wBAWzB,yBACR,WAAW,yBACX,yBACA,mBACN,SAAS,QAAQ,IAAI,IAAI,MAAM,aAAa,IAAI,IAAI"}
|
package/dist/stripe/config.d.mts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Catalog } from "../catalog.mjs";
|
|
1
2
|
import { Metadata } from "../types.mjs";
|
|
2
3
|
//#region src/stripe/config.d.ts
|
|
3
4
|
export type PriceId = `price_${string}`;
|
|
@@ -9,6 +10,8 @@ export type PriceId = `price_${string}`;
|
|
|
9
10
|
*/
|
|
10
11
|
export type StripeMode = 'live' | 'test';
|
|
11
12
|
declare const addMethod: unique symbol;
|
|
13
|
+
/** The private constructor's one door, for this module's `define` function only. */
|
|
14
|
+
declare const construct: unique symbol;
|
|
12
15
|
declare class Product<NS extends Lowercase<string> = Lowercase<string>, PE extends string = string, BP extends string = string, PL extends string = never, PI extends string = never, I extends PriceId = never> {
|
|
13
16
|
readonly id: string;
|
|
14
17
|
readonly config: StripeConfig<NS, PE, BP, PL, PI>;
|
|
@@ -35,7 +38,8 @@ declare class Product<NS extends Lowercase<string> = Lowercase<string>, PE exten
|
|
|
35
38
|
test: PriceId;
|
|
36
39
|
}) => StripeConfig<NS, PE, BP, PL, PI>;
|
|
37
40
|
}
|
|
38
|
-
|
|
41
|
+
/** `plans` and `billingPeriods` are the order {@link StripeConfig.comparePlans} reads. */
|
|
42
|
+
type Options<NS extends Lowercase<string>, PE extends string, BP extends string> = Catalog<NS, PE, BP> & {
|
|
39
43
|
mode: StripeMode;
|
|
40
44
|
returnUrl: string;
|
|
41
45
|
cancelUrl: string;
|
|
@@ -43,6 +47,15 @@ type Options = {
|
|
|
43
47
|
cancellationCouponId: string;
|
|
44
48
|
allowPromotionCodes: boolean;
|
|
45
49
|
};
|
|
50
|
+
/**
|
|
51
|
+
* How a switch between two subscription products relates them, by plan level first and billing
|
|
52
|
+
* period second:
|
|
53
|
+
* - `upgrade` / `downgrade`: a higher or lower plan, whatever the periods.
|
|
54
|
+
* - `longer` / `shorter`: the same plan billed over a longer or shorter period (monthly → yearly
|
|
55
|
+
* is `longer`). Apple calls these crossgrades.
|
|
56
|
+
* - `same`: the same plan and period.
|
|
57
|
+
*/
|
|
58
|
+
export type PlanComparison = 'upgrade' | 'downgrade' | 'longer' | 'shorter' | 'same';
|
|
46
59
|
export 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> {
|
|
47
60
|
private readonly products;
|
|
48
61
|
readonly mode: StripeMode;
|
|
@@ -51,10 +64,13 @@ export declare class StripeConfig<NS extends Lowercase<string> = Lowercase<strin
|
|
|
51
64
|
successUrl: `${string}session_id={CHECKOUT_SESSION_ID}${string}`;
|
|
52
65
|
cancellationCouponId: string;
|
|
53
66
|
allowPromotionCodes: boolean;
|
|
67
|
+
readonly namespace: NS;
|
|
68
|
+
readonly plans: readonly PE[];
|
|
69
|
+
readonly billingPeriods: readonly BP[];
|
|
54
70
|
get productIds(): string[];
|
|
55
71
|
[addMethod]: (product: Product<NS, PE, BP, PL, PI>) => this;
|
|
56
72
|
private constructor();
|
|
57
|
-
static
|
|
73
|
+
static [construct]: <NS_1 extends Lowercase<string>, PE_1 extends string, BP_1 extends string>(options: Options<NS_1, PE_1, BP_1>) => StripeConfig<NS_1, PE_1, BP_1, never, never>;
|
|
58
74
|
/**
|
|
59
75
|
* One-time products take no extra args; subscriptions require both plan and billingPeriod.
|
|
60
76
|
* A plan+period pair may be declared on only one product — version its prices in that chain.
|
|
@@ -66,6 +82,12 @@ export declare class StripeConfig<NS extends Lowercase<string> = Lowercase<strin
|
|
|
66
82
|
getPlan: (productId: string) => PE;
|
|
67
83
|
getBillingPeriod: (productId: string) => BP;
|
|
68
84
|
getMode: (productId: string) => 'payment' | 'subscription';
|
|
85
|
+
/**
|
|
86
|
+
* Whether the config declares `productId` and sells it as a subscription, so `getPlan` and
|
|
87
|
+
* `getBillingPeriod` hold for it. False, not a throw, for an id it does not know: a product that
|
|
88
|
+
* left the config (renamed, retired) or one named by metadata set outside the app.
|
|
89
|
+
*/
|
|
90
|
+
isSubscription: (productId: string) => boolean;
|
|
69
91
|
/** The price new checkouts are created with: the default, or its test twin in test mode. */
|
|
70
92
|
getPriceId: (productId: string) => PriceId;
|
|
71
93
|
/**
|
|
@@ -75,10 +97,22 @@ export declare class StripeConfig<NS extends Lowercase<string> = Lowercase<strin
|
|
|
75
97
|
* metadata that may already name the next plan, is what an invoice should be credited by.
|
|
76
98
|
*/
|
|
77
99
|
getProductId: (priceId: string) => string | null;
|
|
100
|
+
/**
|
|
101
|
+
* How switching from one subscription product to another relates them (see
|
|
102
|
+
* {@link PlanComparison}), by the order `plans` and `billingPeriods` were declared in. Throws for
|
|
103
|
+
* a product that is not a subscription.
|
|
104
|
+
*/
|
|
105
|
+
comparePlans: (fromProductId: string, toProductId: string) => PlanComparison;
|
|
78
106
|
getCreditAmount: (productId: string, priceId?: string) => number;
|
|
79
107
|
private readonly getCreditExpiresIn;
|
|
80
108
|
getCreditExpiresAt: (productId: string, priceId?: string) => string;
|
|
81
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* Declare the Stripe product catalog. Every type parameter is inferred from the options:
|
|
112
|
+
* the namespace from `namespace`, the plans and billing periods from `plans` and `billingPeriods`
|
|
113
|
+
* (`const`, so a literal array needs no `as const`). Products are then added with the fluent chain.
|
|
114
|
+
*/
|
|
115
|
+
export declare function defineStripeConfig<const NS extends Lowercase<string>, const PE extends string, const BP extends string>(options: Options<NS, PE, BP>): StripeConfig<NS, PE, BP>;
|
|
82
116
|
//#endregion
|
|
83
117
|
export {};
|
|
84
118
|
//# sourceMappingURL=config.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.mts","names":[],"sources":["../../src/stripe/config.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"config.d.mts","names":[],"sources":["../../src/stripe/config.ts"],"mappings":";;;YAKY;;;;;;;YAQA;cAEN;;cAEA;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;;;;;;;;;EAcnC,UAAO,gBAAoB,GAAC;IAAW,MAAM;QAAS,aAAA,IAAA,IAAA,IAAA,IAAA;;;KAiBnD,QAAQ,WAAW,mBAAmB,mBAAmB,qBAAqB,QACjF,IACA,IACA;EAEA,MAAM;EACN;EACA;EACA;EACA;EACA;;;;;;;;;;YAWU;qBAEC,aACX,WAAW,oBAAoB,mBAC/B,4BACA,4BACA,2BACA;mBAEiB;WAED,MAAM;EACf;EACA;EACA;EACA;EACA;WACS,WAAW;WACX,gBAAgB;WAChB,yBAAyB;MAErC;GAMH,aAAU,SAAa,QAAQ,IAAI,IAAI,IAAI,IAAI;UAoBzC;UAaC,aAAc,aAAW,mBAAmB,qBAAmB,qBAAiB,SAC7E,QAAQ,MAAI,MAAI,UAAG,aAAA,MAAA,MAAA;;;;;EAO9B;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;;EAQ/C,UAAO,sBAAwB;EAO/B,mBAAgB,sBAAwB;EAOxC,UAAO;;;;;;EAWP,iBAAc;;EAMd,aAAU,sBAAwB;;;;;;;EAalC,eAAY;;;;;;EAYZ,eAAY,uBAAyB,wBAAwB;EAY7D,kBAAe,mBAAqB;mBAanB;EAajB,qBAAkB,mBAAqB;;;;;;;wBAWzB,yBACR,WAAW,yBACX,yBACA,mBACN,SAAS,QAAQ,IAAI,IAAI,MAAM,aAAa,IAAI,IAAI"}
|