alchemy 0.15.6 → 0.15.8
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/lib/cloudflare/bundle/external.d.ts.map +1 -1
- package/lib/cloudflare/bundle/external.js +2 -1
- package/lib/cloudflare/bundle/external.js.map +1 -1
- package/lib/stripe/index.d.ts +1 -0
- package/lib/stripe/index.d.ts.map +1 -1
- package/lib/stripe/index.js +1 -0
- package/lib/stripe/index.js.map +1 -1
- package/lib/stripe/meter.d.ts +137 -0
- package/lib/stripe/meter.d.ts.map +1 -0
- package/lib/stripe/meter.js +226 -0
- package/lib/stripe/meter.js.map +1 -0
- package/package.json +1 -1
- package/src/cloudflare/bundle/external.ts +2 -1
- package/src/stripe/index.ts +1 -0
- package/src/stripe/meter.ts +314 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"external.d.ts","sourceRoot":"","sources":["../../../src/cloudflare/bundle/external.ts"],"names":[],"mappings":"AA2BA,eAAO,MAAM,QAAQ,
|
|
1
|
+
{"version":3,"file":"external.d.ts","sourceRoot":"","sources":["../../../src/cloudflare/bundle/external.ts"],"names":[],"mappings":"AA2BA,eAAO,MAAM,QAAQ,UAMpB,CAAC;AAEF,eAAO,MAAM,YAAY,UAKxB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"external.js","sourceRoot":"","sources":["../../../src/cloudflare/bundle/external.ts"],"names":[],"mappings":"AAAA,uFAAuF;AACvF,MAAM,aAAa,GAAG;IACpB,kBAAkB;IAClB,aAAa;IACb,aAAa;IACb,cAAc;IACd,aAAa;IACb,YAAY;IACZ,0BAA0B;IAC1B,UAAU;IACV,aAAa;IACb,gBAAgB;IAChB,UAAU;IACV,WAAW;IACX,iBAAiB,EAAE,sBAAsB;IACzC,cAAc;IACd,kBAAkB;IAClB,aAAa;IACb,qBAAqB;IACrB,aAAa;IACb,UAAU,EAAE,sBAAsB;IAClC,UAAU;IACV,WAAW;IACX,WAAW;IACX,YAAY;CACb,CAAC;AAEF,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,GAAG,aAAa;IAChB,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5C,oBAAoB;IACpB,
|
|
1
|
+
{"version":3,"file":"external.js","sourceRoot":"","sources":["../../../src/cloudflare/bundle/external.ts"],"names":[],"mappings":"AAAA,uFAAuF;AACvF,MAAM,aAAa,GAAG;IACpB,kBAAkB;IAClB,aAAa;IACb,aAAa;IACb,cAAc;IACd,aAAa;IACb,YAAY;IACZ,0BAA0B;IAC1B,UAAU;IACV,aAAa;IACb,gBAAgB;IAChB,UAAU;IACV,WAAW;IACX,iBAAiB,EAAE,sBAAsB;IACzC,cAAc;IACd,kBAAkB;IAClB,aAAa;IACb,qBAAqB;IACrB,aAAa;IACb,UAAU,EAAE,sBAAsB;IAClC,UAAU;IACV,WAAW;IACX,WAAW;IACX,YAAY;CACb,CAAC;AAEF,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,GAAG,aAAa;IAChB,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5C,oBAAoB;IACpB,sBAAsB;IACtB,cAAc;CACf,CAAC;AAEF,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,EAAE;IACF,kBAAkB;IAClB,aAAa;IACb,cAAc;CACf,CAAC"}
|
package/lib/stripe/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/stripe/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/stripe/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC"}
|
package/lib/stripe/index.js
CHANGED
package/lib/stripe/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/stripe/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/stripe/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import Stripe from "stripe";
|
|
2
|
+
import type { Context } from "../context.js";
|
|
3
|
+
import { Resource } from "../resource.js";
|
|
4
|
+
/**
|
|
5
|
+
* Properties for creating or updating a Stripe Meter.
|
|
6
|
+
* Note: Nested objects like defaultAggregation, customerMapping, valueSettings
|
|
7
|
+
* use camelCase for keys here (e.g., eventPayloadKey), and the resource
|
|
8
|
+
* implementation will map them to snake_case for Stripe API calls.
|
|
9
|
+
*/
|
|
10
|
+
export interface MeterProps {
|
|
11
|
+
displayName: string;
|
|
12
|
+
eventName: string;
|
|
13
|
+
status?: "active" | "inactive";
|
|
14
|
+
defaultAggregation: {
|
|
15
|
+
formula: Stripe.Billing.MeterCreateParams.DefaultAggregation.Formula;
|
|
16
|
+
};
|
|
17
|
+
customerMapping: {
|
|
18
|
+
eventPayloadKey: string;
|
|
19
|
+
type: Stripe.Billing.MeterCreateParams.CustomerMapping["type"];
|
|
20
|
+
};
|
|
21
|
+
valueSettings: {
|
|
22
|
+
eventPayloadKey: string;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Output returned after Stripe Meter creation/update.
|
|
27
|
+
*/
|
|
28
|
+
export interface Meter extends Resource<"stripe::Meter"> {
|
|
29
|
+
id: string;
|
|
30
|
+
object: "billing.meter";
|
|
31
|
+
displayName: string;
|
|
32
|
+
eventName: string;
|
|
33
|
+
defaultAggregation: {
|
|
34
|
+
formula: Stripe.Billing.Meter.DefaultAggregation["formula"];
|
|
35
|
+
};
|
|
36
|
+
customerMapping: {
|
|
37
|
+
eventPayloadKey: string;
|
|
38
|
+
type: Stripe.Billing.Meter.CustomerMapping["type"];
|
|
39
|
+
};
|
|
40
|
+
valueSettings: {
|
|
41
|
+
eventPayloadKey: string;
|
|
42
|
+
};
|
|
43
|
+
status: "active" | "inactive";
|
|
44
|
+
createdAt: number;
|
|
45
|
+
updatedAt: number;
|
|
46
|
+
livemode: boolean;
|
|
47
|
+
statusTransitions?: {
|
|
48
|
+
deactivatedAt: number | null;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Manages Stripe Billing Meters. Meters allow you to define how usage of a product
|
|
53
|
+
* is measured and aggregated for billing purposes. You can track events and their
|
|
54
|
+
* values, and Stripe will calculate the usage based on the meter's configuration.
|
|
55
|
+
*
|
|
56
|
+
* Stripe Meters have several properties that are immutable after creation:
|
|
57
|
+
* `displayName`, `eventName`, `defaultAggregation`, `customerMapping`, and
|
|
58
|
+
* `valueSettings`. Attempting to update these will result in an error.
|
|
59
|
+
* The `status` of a meter (active/inactive) can be updated.
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* // Create a new active meter to track API calls, summing the 'count' field from events.
|
|
63
|
+
* const apiCallMeter = await Meter("apiUsageMeter", {
|
|
64
|
+
* displayName: "API Call Usage",
|
|
65
|
+
* eventName: "api.call.recorded", // The event name Stripe will listen for
|
|
66
|
+
* defaultAggregation: {
|
|
67
|
+
* formula: "sum" // Sum up the values of events
|
|
68
|
+
* },
|
|
69
|
+
* customerMapping: {
|
|
70
|
+
* eventPayloadKey: "customer_id", // Field in the event payload that identifies the Stripe Customer
|
|
71
|
+
* type: "by_id"
|
|
72
|
+
* },
|
|
73
|
+
* valueSettings: {
|
|
74
|
+
* eventPayloadKey: "count" // Field in the event payload that represents the value to aggregate
|
|
75
|
+
* }
|
|
76
|
+
* // status defaults to 'active' if not specified
|
|
77
|
+
* });
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* // Create a new meter for data storage, initially inactive.
|
|
81
|
+
* // This meter will take the last reported 'gb_used' value within a billing period.
|
|
82
|
+
* const dataStorageMeterInactive = await Meter("dataStorageMeter", {
|
|
83
|
+
* displayName: "Data Storage GB",
|
|
84
|
+
* eventName: "data.storage.reported",
|
|
85
|
+
* status: "inactive", // Explicitly set to inactive
|
|
86
|
+
* defaultAggregation: {
|
|
87
|
+
* formula: "last_during_period"
|
|
88
|
+
* },
|
|
89
|
+
* customerMapping: {
|
|
90
|
+
* eventPayloadKey: "user_stripe_id",
|
|
91
|
+
* type: "by_id"
|
|
92
|
+
* },
|
|
93
|
+
* valueSettings: {
|
|
94
|
+
* eventPayloadKey: "gb_used"
|
|
95
|
+
* }
|
|
96
|
+
* });
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* // (Scenario: Assuming 'dataStorageMeterInactive' from the previous example was created
|
|
100
|
+
* // and its ID is available, e.g., via `dataStorageMeterInactive.id`) *
|
|
101
|
+
* // If dataStorageMeterInactive was previously defined and we want to activate it:
|
|
102
|
+
* const activatedStorageMeter = await Meter("dataStorageMeter", { // Same logicalId
|
|
103
|
+
* displayName: "Data Storage GB", // Immutable, must match existing
|
|
104
|
+
* eventName: "data.storage.reported", // Immutable, must match existing
|
|
105
|
+
* status: "active", // Update: change status to active
|
|
106
|
+
* defaultAggregation: { // Immutable, must match existing
|
|
107
|
+
* formula: "last_during_period"
|
|
108
|
+
* },
|
|
109
|
+
* customerMapping: { // Immutable, must match existing
|
|
110
|
+
* eventPayloadKey: "user_stripe_id",
|
|
111
|
+
* type: "by_id"
|
|
112
|
+
* },
|
|
113
|
+
* valueSettings: { // Immutable, must match existing
|
|
114
|
+
* eventPayloadKey: "gb_used"
|
|
115
|
+
* }
|
|
116
|
+
* });
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* // To deactivate an existing active meter (e.g. 'apiCallMeter' from first example):
|
|
120
|
+
* const deactivatedApiMeter = await Meter("apiUsageMeter", { // Same logicalId
|
|
121
|
+
* displayName: "API Call Usage", // Immutable, must match existing
|
|
122
|
+
* eventName: "api.call.recorded", // Immutable, must match existing
|
|
123
|
+
* status: "inactive", // Update: change status to inactive
|
|
124
|
+
* defaultAggregation: { // Immutable, must match existing
|
|
125
|
+
* formula: "sum"
|
|
126
|
+
* },
|
|
127
|
+
* customerMapping: { // Immutable, must match existing
|
|
128
|
+
* eventPayloadKey: "customer_id",
|
|
129
|
+
* type: "by_id"
|
|
130
|
+
* },
|
|
131
|
+
* valueSettings: { // Immutable, must match existing
|
|
132
|
+
* eventPayloadKey: "count"
|
|
133
|
+
* }
|
|
134
|
+
* });
|
|
135
|
+
*/
|
|
136
|
+
export declare const Meter: (((this: any, id: string, props?: {}) => never) & (new (_: never) => never)) | ((this: Context<Meter>, logicalId: string, props: MeterProps) => Promise<Meter>);
|
|
137
|
+
//# sourceMappingURL=meter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meter.d.ts","sourceRoot":"","sources":["../../src/stripe/meter.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAC;IAC/B,kBAAkB,EAAE;QAClB,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,iBAAiB,CAAC,kBAAkB,CAAC,OAAO,CAAC;KACtE,CAAC;IACF,eAAe,EAAE;QACf,eAAe,EAAE,MAAM,CAAC;QACxB,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,iBAAiB,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;KAChE,CAAC;IACF,aAAa,EAAE;QACb,eAAe,EAAE,MAAM,CAAC;KACzB,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,KAAM,SAAQ,QAAQ,CAAC,eAAe,CAAC;IACtD,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,eAAe,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB,EAAE;QAClB,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAC;KAC7D,CAAC;IACF,eAAe,EAAE;QACf,eAAe,EAAE,MAAM,CAAC;QACxB,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;KACpD,CAAC;IACF,aAAa,EAAE;QACb,eAAe,EAAE,MAAM,CAAC;KACzB,CAAC;IACF,MAAM,EAAE,QAAQ,GAAG,UAAU,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,iBAAiB,CAAC,EAAE;QAClB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;KAC9B,CAAC;CACH;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoFG;AAEH,eAAO,MAAM,KAAK,yFAGR,OAAO,CAAC,KAAK,CAAC,aACT,MAAM,SACV,UAAU,KAChB,OAAO,CAAC,KAAK,CAAC,CAwKlB,CAAC"}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import Stripe from "stripe";
|
|
2
|
+
import { Resource } from "../resource.js";
|
|
3
|
+
/**
|
|
4
|
+
* Manages Stripe Billing Meters. Meters allow you to define how usage of a product
|
|
5
|
+
* is measured and aggregated for billing purposes. You can track events and their
|
|
6
|
+
* values, and Stripe will calculate the usage based on the meter's configuration.
|
|
7
|
+
*
|
|
8
|
+
* Stripe Meters have several properties that are immutable after creation:
|
|
9
|
+
* `displayName`, `eventName`, `defaultAggregation`, `customerMapping`, and
|
|
10
|
+
* `valueSettings`. Attempting to update these will result in an error.
|
|
11
|
+
* The `status` of a meter (active/inactive) can be updated.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* // Create a new active meter to track API calls, summing the 'count' field from events.
|
|
15
|
+
* const apiCallMeter = await Meter("apiUsageMeter", {
|
|
16
|
+
* displayName: "API Call Usage",
|
|
17
|
+
* eventName: "api.call.recorded", // The event name Stripe will listen for
|
|
18
|
+
* defaultAggregation: {
|
|
19
|
+
* formula: "sum" // Sum up the values of events
|
|
20
|
+
* },
|
|
21
|
+
* customerMapping: {
|
|
22
|
+
* eventPayloadKey: "customer_id", // Field in the event payload that identifies the Stripe Customer
|
|
23
|
+
* type: "by_id"
|
|
24
|
+
* },
|
|
25
|
+
* valueSettings: {
|
|
26
|
+
* eventPayloadKey: "count" // Field in the event payload that represents the value to aggregate
|
|
27
|
+
* }
|
|
28
|
+
* // status defaults to 'active' if not specified
|
|
29
|
+
* });
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* // Create a new meter for data storage, initially inactive.
|
|
33
|
+
* // This meter will take the last reported 'gb_used' value within a billing period.
|
|
34
|
+
* const dataStorageMeterInactive = await Meter("dataStorageMeter", {
|
|
35
|
+
* displayName: "Data Storage GB",
|
|
36
|
+
* eventName: "data.storage.reported",
|
|
37
|
+
* status: "inactive", // Explicitly set to inactive
|
|
38
|
+
* defaultAggregation: {
|
|
39
|
+
* formula: "last_during_period"
|
|
40
|
+
* },
|
|
41
|
+
* customerMapping: {
|
|
42
|
+
* eventPayloadKey: "user_stripe_id",
|
|
43
|
+
* type: "by_id"
|
|
44
|
+
* },
|
|
45
|
+
* valueSettings: {
|
|
46
|
+
* eventPayloadKey: "gb_used"
|
|
47
|
+
* }
|
|
48
|
+
* });
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* // (Scenario: Assuming 'dataStorageMeterInactive' from the previous example was created
|
|
52
|
+
* // and its ID is available, e.g., via `dataStorageMeterInactive.id`) *
|
|
53
|
+
* // If dataStorageMeterInactive was previously defined and we want to activate it:
|
|
54
|
+
* const activatedStorageMeter = await Meter("dataStorageMeter", { // Same logicalId
|
|
55
|
+
* displayName: "Data Storage GB", // Immutable, must match existing
|
|
56
|
+
* eventName: "data.storage.reported", // Immutable, must match existing
|
|
57
|
+
* status: "active", // Update: change status to active
|
|
58
|
+
* defaultAggregation: { // Immutable, must match existing
|
|
59
|
+
* formula: "last_during_period"
|
|
60
|
+
* },
|
|
61
|
+
* customerMapping: { // Immutable, must match existing
|
|
62
|
+
* eventPayloadKey: "user_stripe_id",
|
|
63
|
+
* type: "by_id"
|
|
64
|
+
* },
|
|
65
|
+
* valueSettings: { // Immutable, must match existing
|
|
66
|
+
* eventPayloadKey: "gb_used"
|
|
67
|
+
* }
|
|
68
|
+
* });
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* // To deactivate an existing active meter (e.g. 'apiCallMeter' from first example):
|
|
72
|
+
* const deactivatedApiMeter = await Meter("apiUsageMeter", { // Same logicalId
|
|
73
|
+
* displayName: "API Call Usage", // Immutable, must match existing
|
|
74
|
+
* eventName: "api.call.recorded", // Immutable, must match existing
|
|
75
|
+
* status: "inactive", // Update: change status to inactive
|
|
76
|
+
* defaultAggregation: { // Immutable, must match existing
|
|
77
|
+
* formula: "sum"
|
|
78
|
+
* },
|
|
79
|
+
* customerMapping: { // Immutable, must match existing
|
|
80
|
+
* eventPayloadKey: "customer_id",
|
|
81
|
+
* type: "by_id"
|
|
82
|
+
* },
|
|
83
|
+
* valueSettings: { // Immutable, must match existing
|
|
84
|
+
* eventPayloadKey: "count"
|
|
85
|
+
* }
|
|
86
|
+
* });
|
|
87
|
+
*/
|
|
88
|
+
export const Meter = Resource("stripe::Meter", async function (logicalId, props) {
|
|
89
|
+
const apiKey = process.env.STRIPE_API_KEY;
|
|
90
|
+
if (!apiKey) {
|
|
91
|
+
throw new Error("STRIPE_API_KEY environment variable is required");
|
|
92
|
+
}
|
|
93
|
+
const stripe = new Stripe(apiKey);
|
|
94
|
+
const currentOutputId = this.output?.id;
|
|
95
|
+
// Helper to map MeterProps (camelCase nested) to Stripe API params (snake_case nested)
|
|
96
|
+
const mapPropsToStripeParams = (inputProps) => ({
|
|
97
|
+
display_name: inputProps.displayName,
|
|
98
|
+
event_name: inputProps.eventName,
|
|
99
|
+
default_aggregation: {
|
|
100
|
+
formula: inputProps.defaultAggregation.formula,
|
|
101
|
+
},
|
|
102
|
+
customer_mapping: {
|
|
103
|
+
event_payload_key: inputProps.customerMapping.eventPayloadKey,
|
|
104
|
+
type: inputProps.customerMapping.type,
|
|
105
|
+
},
|
|
106
|
+
value_settings: {
|
|
107
|
+
event_payload_key: inputProps.valueSettings.eventPayloadKey,
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
// Helper to map Stripe API response (snake_case) to Meter output interface (camelCase)
|
|
111
|
+
const mapStripeObjectToMeterOutput = (stripeMeter) => ({
|
|
112
|
+
id: stripeMeter.id,
|
|
113
|
+
object: stripeMeter.object,
|
|
114
|
+
displayName: stripeMeter.display_name,
|
|
115
|
+
eventName: stripeMeter.event_name,
|
|
116
|
+
defaultAggregation: {
|
|
117
|
+
formula: stripeMeter.default_aggregation.formula,
|
|
118
|
+
},
|
|
119
|
+
customerMapping: {
|
|
120
|
+
eventPayloadKey: stripeMeter.customer_mapping.event_payload_key,
|
|
121
|
+
type: stripeMeter.customer_mapping.type,
|
|
122
|
+
},
|
|
123
|
+
valueSettings: {
|
|
124
|
+
eventPayloadKey: stripeMeter.value_settings.event_payload_key,
|
|
125
|
+
},
|
|
126
|
+
status: stripeMeter.status,
|
|
127
|
+
createdAt: stripeMeter.created,
|
|
128
|
+
updatedAt: stripeMeter.updated,
|
|
129
|
+
livemode: stripeMeter.livemode,
|
|
130
|
+
statusTransitions: stripeMeter.status_transitions
|
|
131
|
+
? {
|
|
132
|
+
deactivatedAt: stripeMeter.status_transitions.deactivated_at,
|
|
133
|
+
}
|
|
134
|
+
: undefined,
|
|
135
|
+
});
|
|
136
|
+
if (this.phase === "delete") {
|
|
137
|
+
if (currentOutputId) {
|
|
138
|
+
try {
|
|
139
|
+
const meter = await stripe.billing.meters.retrieve(currentOutputId);
|
|
140
|
+
if (meter.status === "active") {
|
|
141
|
+
await stripe.billing.meters.deactivate(currentOutputId);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
if (error?.code !== "resource_missing") {
|
|
146
|
+
throw error;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return this.destroy();
|
|
151
|
+
}
|
|
152
|
+
// --- Create or Update Phase ---
|
|
153
|
+
let stripeAPIResponse;
|
|
154
|
+
if (this.phase === "update" && currentOutputId) {
|
|
155
|
+
const existingStripeMeter = await stripe.billing.meters.retrieve(currentOutputId);
|
|
156
|
+
// Normalize existingMeter's relevant fields for immutable comparison
|
|
157
|
+
const existingPropsForCompare = {
|
|
158
|
+
displayName: existingStripeMeter.display_name,
|
|
159
|
+
eventName: existingStripeMeter.event_name,
|
|
160
|
+
defaultAggregation: {
|
|
161
|
+
formula: existingStripeMeter.default_aggregation.formula,
|
|
162
|
+
},
|
|
163
|
+
customerMapping: {
|
|
164
|
+
eventPayloadKey: existingStripeMeter.customer_mapping.event_payload_key,
|
|
165
|
+
type: existingStripeMeter.customer_mapping.type,
|
|
166
|
+
},
|
|
167
|
+
valueSettings: {
|
|
168
|
+
eventPayloadKey: existingStripeMeter.value_settings.event_payload_key,
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
// Check for immutable property changes (comparing against props passed to resource)
|
|
172
|
+
if (props.displayName !== existingPropsForCompare.displayName ||
|
|
173
|
+
props.eventName !== existingPropsForCompare.eventName ||
|
|
174
|
+
JSON.stringify(props.defaultAggregation) !==
|
|
175
|
+
JSON.stringify(existingPropsForCompare.defaultAggregation) ||
|
|
176
|
+
JSON.stringify(props.customerMapping) !==
|
|
177
|
+
JSON.stringify(existingPropsForCompare.customerMapping) ||
|
|
178
|
+
JSON.stringify(props.valueSettings) !==
|
|
179
|
+
JSON.stringify(existingPropsForCompare.valueSettings)) {
|
|
180
|
+
throw new Error(`Attempted to update immutable properties for Stripe Meter ${currentOutputId}. ` +
|
|
181
|
+
"displayName, eventName, defaultAggregation, customerMapping, and valueSettings cannot be changed.");
|
|
182
|
+
}
|
|
183
|
+
// Handle status change
|
|
184
|
+
if (props.status && props.status !== existingStripeMeter.status) {
|
|
185
|
+
if (props.status === "inactive" &&
|
|
186
|
+
existingStripeMeter.status === "active") {
|
|
187
|
+
stripeAPIResponse =
|
|
188
|
+
await stripe.billing.meters.deactivate(currentOutputId);
|
|
189
|
+
}
|
|
190
|
+
else if (props.status === "active" &&
|
|
191
|
+
existingStripeMeter.status === "inactive") {
|
|
192
|
+
console.log(`Reactivating Stripe Meter ${currentOutputId}.`);
|
|
193
|
+
stripeAPIResponse =
|
|
194
|
+
await stripe.billing.meters.reactivate(currentOutputId);
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
stripeAPIResponse = existingStripeMeter; // No change in status
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
stripeAPIResponse = existingStripeMeter; // No status change requested
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
// Create phase
|
|
206
|
+
if (!props.displayName ||
|
|
207
|
+
!props.eventName ||
|
|
208
|
+
!props.defaultAggregation ||
|
|
209
|
+
!props.customerMapping ||
|
|
210
|
+
!props.valueSettings) {
|
|
211
|
+
throw new Error("displayName, eventName, defaultAggregation, customerMapping, and valueSettings are required for creating a Stripe Meter.");
|
|
212
|
+
}
|
|
213
|
+
const createParams = mapPropsToStripeParams(props);
|
|
214
|
+
stripeAPIResponse = await stripe.billing.meters.create(createParams);
|
|
215
|
+
// If status 'inactive' is requested during creation, and Stripe created it as 'active'
|
|
216
|
+
if (props.status === "inactive" &&
|
|
217
|
+
stripeAPIResponse.status === "active") {
|
|
218
|
+
stripeAPIResponse = await stripe.billing.meters.deactivate(stripeAPIResponse.id);
|
|
219
|
+
}
|
|
220
|
+
else if (props.status && props.status !== stripeAPIResponse.status) {
|
|
221
|
+
console.warn(`Meter ${stripeAPIResponse.id} created with status ${stripeAPIResponse.status} but requested ${props.status}. Ensure this is intended.`);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return this(mapStripeObjectToMeterOutput(stripeAPIResponse));
|
|
225
|
+
});
|
|
226
|
+
//# sourceMappingURL=meter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meter.js","sourceRoot":"","sources":["../../src/stripe/meter.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAmD1C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoFG;AAEH,MAAM,CAAC,MAAM,KAAK,GAAG,QAAQ,CAC3B,eAAe,EACf,KAAK,WAEH,SAAiB,EACjB,KAAiB;IAEjB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IAC1C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;IACrE,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC,CAAC;IAElC,MAAM,eAAe,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;IAExC,uFAAuF;IACvF,MAAM,sBAAsB,GAAG,CAC7B,UAAsB,EACY,EAAE,CAAC,CAAC;QACtC,YAAY,EAAE,UAAU,CAAC,WAAW;QACpC,UAAU,EAAE,UAAU,CAAC,SAAS;QAChC,mBAAmB,EAAE;YACnB,OAAO,EAAE,UAAU,CAAC,kBAAkB,CAAC,OAAO;SAC/C;QACD,gBAAgB,EAAE;YAChB,iBAAiB,EAAE,UAAU,CAAC,eAAe,CAAC,eAAe;YAC7D,IAAI,EAAE,UAAU,CAAC,eAAe,CAAC,IAAI;SACtC;QACD,cAAc,EAAE;YACd,iBAAiB,EAAE,UAAU,CAAC,aAAa,CAAC,eAAe;SAC5D;KACF,CAAC,CAAC;IAEH,uFAAuF;IACvF,MAAM,4BAA4B,GAAG,CACnC,WAAiC,EACa,EAAE,CAAC,CAAC;QAClD,EAAE,EAAE,WAAW,CAAC,EAAE;QAClB,MAAM,EAAE,WAAW,CAAC,MAAM;QAC1B,WAAW,EAAE,WAAW,CAAC,YAAY;QACrC,SAAS,EAAE,WAAW,CAAC,UAAU;QACjC,kBAAkB,EAAE;YAClB,OAAO,EAAE,WAAW,CAAC,mBAAmB,CAAC,OAAO;SACjD;QACD,eAAe,EAAE;YACf,eAAe,EAAE,WAAW,CAAC,gBAAgB,CAAC,iBAAiB;YAC/D,IAAI,EAAE,WAAW,CAAC,gBAAgB,CAAC,IAAI;SACxC;QACD,aAAa,EAAE;YACb,eAAe,EAAE,WAAW,CAAC,cAAc,CAAC,iBAAiB;SAC9D;QACD,MAAM,EAAE,WAAW,CAAC,MAA+B;QACnD,SAAS,EAAE,WAAW,CAAC,OAAO;QAC9B,SAAS,EAAE,WAAW,CAAC,OAAO;QAC9B,QAAQ,EAAE,WAAW,CAAC,QAAQ;QAC9B,iBAAiB,EAAE,WAAW,CAAC,kBAAkB;YAC/C,CAAC,CAAC;gBACE,aAAa,EAAE,WAAW,CAAC,kBAAkB,CAAC,cAAc;aAC7D;YACH,CAAC,CAAC,SAAS;KACd,CAAC,CAAC;IAEH,IAAI,IAAI,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC5B,IAAI,eAAe,EAAE,CAAC;YACpB,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;gBACpE,IAAI,KAAK,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;oBAC9B,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC;gBAC1D,CAAC;YACH,CAAC;YAAC,OAAO,KAAU,EAAE,CAAC;gBACpB,IAAI,KAAK,EAAE,IAAI,KAAK,kBAAkB,EAAE,CAAC;oBACvC,MAAM,KAAK,CAAC;gBACd,CAAC;YACH,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC;IACxB,CAAC;IAED,iCAAiC;IACjC,IAAI,iBAAuC,CAAC;IAE5C,IAAI,IAAI,CAAC,KAAK,KAAK,QAAQ,IAAI,eAAe,EAAE,CAAC;QAC/C,MAAM,mBAAmB,GACvB,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;QAExD,qEAAqE;QACrE,MAAM,uBAAuB,GAAG;YAC9B,WAAW,EAAE,mBAAmB,CAAC,YAAY;YAC7C,SAAS,EAAE,mBAAmB,CAAC,UAAU;YACzC,kBAAkB,EAAE;gBAClB,OAAO,EAAE,mBAAmB,CAAC,mBAAmB,CAAC,OAAO;aACzD;YACD,eAAe,EAAE;gBACf,eAAe,EACb,mBAAmB,CAAC,gBAAgB,CAAC,iBAAiB;gBACxD,IAAI,EAAE,mBAAmB,CAAC,gBAAgB,CAAC,IAAI;aAChD;YACD,aAAa,EAAE;gBACb,eAAe,EAAE,mBAAmB,CAAC,cAAc,CAAC,iBAAiB;aACtE;SACF,CAAC;QAEF,oFAAoF;QACpF,IACE,KAAK,CAAC,WAAW,KAAK,uBAAuB,CAAC,WAAW;YACzD,KAAK,CAAC,SAAS,KAAK,uBAAuB,CAAC,SAAS;YACrD,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,kBAAkB,CAAC;gBACtC,IAAI,CAAC,SAAS,CAAC,uBAAuB,CAAC,kBAAkB,CAAC;YAC5D,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,eAAe,CAAC;gBACnC,IAAI,CAAC,SAAS,CAAC,uBAAuB,CAAC,eAAe,CAAC;YACzD,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,aAAa,CAAC;gBACjC,IAAI,CAAC,SAAS,CAAC,uBAAuB,CAAC,aAAa,CAAC,EACvD,CAAC;YACD,MAAM,IAAI,KAAK,CACb,6DAA6D,eAAe,IAAI;gBAC9E,mGAAmG,CACtG,CAAC;QACJ,CAAC;QAED,uBAAuB;QACvB,IAAI,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,KAAK,mBAAmB,CAAC,MAAM,EAAE,CAAC;YAChE,IACE,KAAK,CAAC,MAAM,KAAK,UAAU;gBAC3B,mBAAmB,CAAC,MAAM,KAAK,QAAQ,EACvC,CAAC;gBACD,iBAAiB;oBACf,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC;YAC5D,CAAC;iBAAM,IACL,KAAK,CAAC,MAAM,KAAK,QAAQ;gBACzB,mBAAmB,CAAC,MAAM,KAAK,UAAU,EACzC,CAAC;gBACD,OAAO,CAAC,GAAG,CAAC,6BAA6B,eAAe,GAAG,CAAC,CAAC;gBAC7D,iBAAiB;oBACf,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC;YAC5D,CAAC;iBAAM,CAAC;gBACN,iBAAiB,GAAG,mBAAmB,CAAC,CAAC,sBAAsB;YACjE,CAAC;QACH,CAAC;aAAM,CAAC;YACN,iBAAiB,GAAG,mBAAmB,CAAC,CAAC,6BAA6B;QACxE,CAAC;IACH,CAAC;SAAM,CAAC;QACN,eAAe;QACf,IACE,CAAC,KAAK,CAAC,WAAW;YAClB,CAAC,KAAK,CAAC,SAAS;YAChB,CAAC,KAAK,CAAC,kBAAkB;YACzB,CAAC,KAAK,CAAC,eAAe;YACtB,CAAC,KAAK,CAAC,aAAa,EACpB,CAAC;YACD,MAAM,IAAI,KAAK,CACb,0HAA0H,CAC3H,CAAC;QACJ,CAAC;QAED,MAAM,YAAY,GAAG,sBAAsB,CAAC,KAAK,CAAC,CAAC;QACnD,iBAAiB,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QAErE,uFAAuF;QACvF,IACE,KAAK,CAAC,MAAM,KAAK,UAAU;YAC3B,iBAAiB,CAAC,MAAM,KAAK,QAAQ,EACrC,CAAC;YACD,iBAAiB,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CACxD,iBAAiB,CAAC,EAAE,CACrB,CAAC;QACJ,CAAC;aAAM,IAAI,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,KAAK,iBAAiB,CAAC,MAAM,EAAE,CAAC;YACrE,OAAO,CAAC,IAAI,CACV,SAAS,iBAAiB,CAAC,EAAE,wBAAwB,iBAAiB,CAAC,MAAM,kBAAkB,KAAK,CAAC,MAAM,4BAA4B,CACxI,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC,4BAA4B,CAAC,iBAAiB,CAAC,CAAC,CAAC;AAC/D,CAAC,CACF,CAAC"}
|
package/package.json
CHANGED
package/src/stripe/index.ts
CHANGED
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import Stripe from "stripe";
|
|
2
|
+
import type { Context } from "../context.js";
|
|
3
|
+
import { Resource } from "../resource.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Properties for creating or updating a Stripe Meter.
|
|
7
|
+
* Note: Nested objects like defaultAggregation, customerMapping, valueSettings
|
|
8
|
+
* use camelCase for keys here (e.g., eventPayloadKey), and the resource
|
|
9
|
+
* implementation will map them to snake_case for Stripe API calls.
|
|
10
|
+
*/
|
|
11
|
+
export interface MeterProps {
|
|
12
|
+
displayName: string;
|
|
13
|
+
eventName: string;
|
|
14
|
+
status?: "active" | "inactive"; // 'active' is default on create by Stripe
|
|
15
|
+
defaultAggregation: {
|
|
16
|
+
formula: Stripe.Billing.MeterCreateParams.DefaultAggregation.Formula;
|
|
17
|
+
};
|
|
18
|
+
customerMapping: {
|
|
19
|
+
eventPayloadKey: string;
|
|
20
|
+
type: Stripe.Billing.MeterCreateParams.CustomerMapping["type"];
|
|
21
|
+
};
|
|
22
|
+
valueSettings: {
|
|
23
|
+
eventPayloadKey: string;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Output returned after Stripe Meter creation/update.
|
|
29
|
+
*/
|
|
30
|
+
export interface Meter extends Resource<"stripe::Meter"> {
|
|
31
|
+
id: string;
|
|
32
|
+
object: "billing.meter";
|
|
33
|
+
displayName: string;
|
|
34
|
+
eventName: string;
|
|
35
|
+
defaultAggregation: {
|
|
36
|
+
formula: Stripe.Billing.Meter.DefaultAggregation["formula"];
|
|
37
|
+
};
|
|
38
|
+
customerMapping: {
|
|
39
|
+
eventPayloadKey: string;
|
|
40
|
+
type: Stripe.Billing.Meter.CustomerMapping["type"];
|
|
41
|
+
};
|
|
42
|
+
valueSettings: {
|
|
43
|
+
eventPayloadKey: string;
|
|
44
|
+
};
|
|
45
|
+
status: "active" | "inactive";
|
|
46
|
+
createdAt: number; // Unix timestamp
|
|
47
|
+
updatedAt: number; // Unix timestamp
|
|
48
|
+
livemode: boolean;
|
|
49
|
+
statusTransitions?: {
|
|
50
|
+
deactivatedAt: number | null; // Unix timestamp
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Manages Stripe Billing Meters. Meters allow you to define how usage of a product
|
|
56
|
+
* is measured and aggregated for billing purposes. You can track events and their
|
|
57
|
+
* values, and Stripe will calculate the usage based on the meter's configuration.
|
|
58
|
+
*
|
|
59
|
+
* Stripe Meters have several properties that are immutable after creation:
|
|
60
|
+
* `displayName`, `eventName`, `defaultAggregation`, `customerMapping`, and
|
|
61
|
+
* `valueSettings`. Attempting to update these will result in an error.
|
|
62
|
+
* The `status` of a meter (active/inactive) can be updated.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* // Create a new active meter to track API calls, summing the 'count' field from events.
|
|
66
|
+
* const apiCallMeter = await Meter("apiUsageMeter", {
|
|
67
|
+
* displayName: "API Call Usage",
|
|
68
|
+
* eventName: "api.call.recorded", // The event name Stripe will listen for
|
|
69
|
+
* defaultAggregation: {
|
|
70
|
+
* formula: "sum" // Sum up the values of events
|
|
71
|
+
* },
|
|
72
|
+
* customerMapping: {
|
|
73
|
+
* eventPayloadKey: "customer_id", // Field in the event payload that identifies the Stripe Customer
|
|
74
|
+
* type: "by_id"
|
|
75
|
+
* },
|
|
76
|
+
* valueSettings: {
|
|
77
|
+
* eventPayloadKey: "count" // Field in the event payload that represents the value to aggregate
|
|
78
|
+
* }
|
|
79
|
+
* // status defaults to 'active' if not specified
|
|
80
|
+
* });
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* // Create a new meter for data storage, initially inactive.
|
|
84
|
+
* // This meter will take the last reported 'gb_used' value within a billing period.
|
|
85
|
+
* const dataStorageMeterInactive = await Meter("dataStorageMeter", {
|
|
86
|
+
* displayName: "Data Storage GB",
|
|
87
|
+
* eventName: "data.storage.reported",
|
|
88
|
+
* status: "inactive", // Explicitly set to inactive
|
|
89
|
+
* defaultAggregation: {
|
|
90
|
+
* formula: "last_during_period"
|
|
91
|
+
* },
|
|
92
|
+
* customerMapping: {
|
|
93
|
+
* eventPayloadKey: "user_stripe_id",
|
|
94
|
+
* type: "by_id"
|
|
95
|
+
* },
|
|
96
|
+
* valueSettings: {
|
|
97
|
+
* eventPayloadKey: "gb_used"
|
|
98
|
+
* }
|
|
99
|
+
* });
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* // (Scenario: Assuming 'dataStorageMeterInactive' from the previous example was created
|
|
103
|
+
* // and its ID is available, e.g., via `dataStorageMeterInactive.id`) *
|
|
104
|
+
* // If dataStorageMeterInactive was previously defined and we want to activate it:
|
|
105
|
+
* const activatedStorageMeter = await Meter("dataStorageMeter", { // Same logicalId
|
|
106
|
+
* displayName: "Data Storage GB", // Immutable, must match existing
|
|
107
|
+
* eventName: "data.storage.reported", // Immutable, must match existing
|
|
108
|
+
* status: "active", // Update: change status to active
|
|
109
|
+
* defaultAggregation: { // Immutable, must match existing
|
|
110
|
+
* formula: "last_during_period"
|
|
111
|
+
* },
|
|
112
|
+
* customerMapping: { // Immutable, must match existing
|
|
113
|
+
* eventPayloadKey: "user_stripe_id",
|
|
114
|
+
* type: "by_id"
|
|
115
|
+
* },
|
|
116
|
+
* valueSettings: { // Immutable, must match existing
|
|
117
|
+
* eventPayloadKey: "gb_used"
|
|
118
|
+
* }
|
|
119
|
+
* });
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* // To deactivate an existing active meter (e.g. 'apiCallMeter' from first example):
|
|
123
|
+
* const deactivatedApiMeter = await Meter("apiUsageMeter", { // Same logicalId
|
|
124
|
+
* displayName: "API Call Usage", // Immutable, must match existing
|
|
125
|
+
* eventName: "api.call.recorded", // Immutable, must match existing
|
|
126
|
+
* status: "inactive", // Update: change status to inactive
|
|
127
|
+
* defaultAggregation: { // Immutable, must match existing
|
|
128
|
+
* formula: "sum"
|
|
129
|
+
* },
|
|
130
|
+
* customerMapping: { // Immutable, must match existing
|
|
131
|
+
* eventPayloadKey: "customer_id",
|
|
132
|
+
* type: "by_id"
|
|
133
|
+
* },
|
|
134
|
+
* valueSettings: { // Immutable, must match existing
|
|
135
|
+
* eventPayloadKey: "count"
|
|
136
|
+
* }
|
|
137
|
+
* });
|
|
138
|
+
*/
|
|
139
|
+
|
|
140
|
+
export const Meter = Resource(
|
|
141
|
+
"stripe::Meter",
|
|
142
|
+
async function (
|
|
143
|
+
this: Context<Meter>,
|
|
144
|
+
logicalId: string,
|
|
145
|
+
props: MeterProps,
|
|
146
|
+
): Promise<Meter> {
|
|
147
|
+
const apiKey = process.env.STRIPE_API_KEY;
|
|
148
|
+
if (!apiKey) {
|
|
149
|
+
throw new Error("STRIPE_API_KEY environment variable is required");
|
|
150
|
+
}
|
|
151
|
+
const stripe = new Stripe(apiKey);
|
|
152
|
+
|
|
153
|
+
const currentOutputId = this.output?.id;
|
|
154
|
+
|
|
155
|
+
// Helper to map MeterProps (camelCase nested) to Stripe API params (snake_case nested)
|
|
156
|
+
const mapPropsToStripeParams = (
|
|
157
|
+
inputProps: MeterProps,
|
|
158
|
+
): Stripe.Billing.MeterCreateParams => ({
|
|
159
|
+
display_name: inputProps.displayName,
|
|
160
|
+
event_name: inputProps.eventName,
|
|
161
|
+
default_aggregation: {
|
|
162
|
+
formula: inputProps.defaultAggregation.formula,
|
|
163
|
+
},
|
|
164
|
+
customer_mapping: {
|
|
165
|
+
event_payload_key: inputProps.customerMapping.eventPayloadKey,
|
|
166
|
+
type: inputProps.customerMapping.type,
|
|
167
|
+
},
|
|
168
|
+
value_settings: {
|
|
169
|
+
event_payload_key: inputProps.valueSettings.eventPayloadKey,
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// Helper to map Stripe API response (snake_case) to Meter output interface (camelCase)
|
|
174
|
+
const mapStripeObjectToMeterOutput = (
|
|
175
|
+
stripeMeter: Stripe.Billing.Meter,
|
|
176
|
+
): Omit<Meter, keyof Resource<"stripe::Meter">> => ({
|
|
177
|
+
id: stripeMeter.id,
|
|
178
|
+
object: stripeMeter.object,
|
|
179
|
+
displayName: stripeMeter.display_name,
|
|
180
|
+
eventName: stripeMeter.event_name,
|
|
181
|
+
defaultAggregation: {
|
|
182
|
+
formula: stripeMeter.default_aggregation.formula,
|
|
183
|
+
},
|
|
184
|
+
customerMapping: {
|
|
185
|
+
eventPayloadKey: stripeMeter.customer_mapping.event_payload_key,
|
|
186
|
+
type: stripeMeter.customer_mapping.type,
|
|
187
|
+
},
|
|
188
|
+
valueSettings: {
|
|
189
|
+
eventPayloadKey: stripeMeter.value_settings.event_payload_key,
|
|
190
|
+
},
|
|
191
|
+
status: stripeMeter.status as "active" | "inactive",
|
|
192
|
+
createdAt: stripeMeter.created,
|
|
193
|
+
updatedAt: stripeMeter.updated,
|
|
194
|
+
livemode: stripeMeter.livemode,
|
|
195
|
+
statusTransitions: stripeMeter.status_transitions
|
|
196
|
+
? {
|
|
197
|
+
deactivatedAt: stripeMeter.status_transitions.deactivated_at,
|
|
198
|
+
}
|
|
199
|
+
: undefined,
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
if (this.phase === "delete") {
|
|
203
|
+
if (currentOutputId) {
|
|
204
|
+
try {
|
|
205
|
+
const meter = await stripe.billing.meters.retrieve(currentOutputId);
|
|
206
|
+
if (meter.status === "active") {
|
|
207
|
+
await stripe.billing.meters.deactivate(currentOutputId);
|
|
208
|
+
}
|
|
209
|
+
} catch (error: any) {
|
|
210
|
+
if (error?.code !== "resource_missing") {
|
|
211
|
+
throw error;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return this.destroy();
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// --- Create or Update Phase ---
|
|
219
|
+
let stripeAPIResponse: Stripe.Billing.Meter;
|
|
220
|
+
|
|
221
|
+
if (this.phase === "update" && currentOutputId) {
|
|
222
|
+
const existingStripeMeter =
|
|
223
|
+
await stripe.billing.meters.retrieve(currentOutputId);
|
|
224
|
+
|
|
225
|
+
// Normalize existingMeter's relevant fields for immutable comparison
|
|
226
|
+
const existingPropsForCompare = {
|
|
227
|
+
displayName: existingStripeMeter.display_name,
|
|
228
|
+
eventName: existingStripeMeter.event_name,
|
|
229
|
+
defaultAggregation: {
|
|
230
|
+
formula: existingStripeMeter.default_aggregation.formula,
|
|
231
|
+
},
|
|
232
|
+
customerMapping: {
|
|
233
|
+
eventPayloadKey:
|
|
234
|
+
existingStripeMeter.customer_mapping.event_payload_key,
|
|
235
|
+
type: existingStripeMeter.customer_mapping.type,
|
|
236
|
+
},
|
|
237
|
+
valueSettings: {
|
|
238
|
+
eventPayloadKey: existingStripeMeter.value_settings.event_payload_key,
|
|
239
|
+
},
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
// Check for immutable property changes (comparing against props passed to resource)
|
|
243
|
+
if (
|
|
244
|
+
props.displayName !== existingPropsForCompare.displayName ||
|
|
245
|
+
props.eventName !== existingPropsForCompare.eventName ||
|
|
246
|
+
JSON.stringify(props.defaultAggregation) !==
|
|
247
|
+
JSON.stringify(existingPropsForCompare.defaultAggregation) ||
|
|
248
|
+
JSON.stringify(props.customerMapping) !==
|
|
249
|
+
JSON.stringify(existingPropsForCompare.customerMapping) ||
|
|
250
|
+
JSON.stringify(props.valueSettings) !==
|
|
251
|
+
JSON.stringify(existingPropsForCompare.valueSettings)
|
|
252
|
+
) {
|
|
253
|
+
throw new Error(
|
|
254
|
+
`Attempted to update immutable properties for Stripe Meter ${currentOutputId}. ` +
|
|
255
|
+
"displayName, eventName, defaultAggregation, customerMapping, and valueSettings cannot be changed.",
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Handle status change
|
|
260
|
+
if (props.status && props.status !== existingStripeMeter.status) {
|
|
261
|
+
if (
|
|
262
|
+
props.status === "inactive" &&
|
|
263
|
+
existingStripeMeter.status === "active"
|
|
264
|
+
) {
|
|
265
|
+
stripeAPIResponse =
|
|
266
|
+
await stripe.billing.meters.deactivate(currentOutputId);
|
|
267
|
+
} else if (
|
|
268
|
+
props.status === "active" &&
|
|
269
|
+
existingStripeMeter.status === "inactive"
|
|
270
|
+
) {
|
|
271
|
+
console.log(`Reactivating Stripe Meter ${currentOutputId}.`);
|
|
272
|
+
stripeAPIResponse =
|
|
273
|
+
await stripe.billing.meters.reactivate(currentOutputId);
|
|
274
|
+
} else {
|
|
275
|
+
stripeAPIResponse = existingStripeMeter; // No change in status
|
|
276
|
+
}
|
|
277
|
+
} else {
|
|
278
|
+
stripeAPIResponse = existingStripeMeter; // No status change requested
|
|
279
|
+
}
|
|
280
|
+
} else {
|
|
281
|
+
// Create phase
|
|
282
|
+
if (
|
|
283
|
+
!props.displayName ||
|
|
284
|
+
!props.eventName ||
|
|
285
|
+
!props.defaultAggregation ||
|
|
286
|
+
!props.customerMapping ||
|
|
287
|
+
!props.valueSettings
|
|
288
|
+
) {
|
|
289
|
+
throw new Error(
|
|
290
|
+
"displayName, eventName, defaultAggregation, customerMapping, and valueSettings are required for creating a Stripe Meter.",
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const createParams = mapPropsToStripeParams(props);
|
|
295
|
+
stripeAPIResponse = await stripe.billing.meters.create(createParams);
|
|
296
|
+
|
|
297
|
+
// If status 'inactive' is requested during creation, and Stripe created it as 'active'
|
|
298
|
+
if (
|
|
299
|
+
props.status === "inactive" &&
|
|
300
|
+
stripeAPIResponse.status === "active"
|
|
301
|
+
) {
|
|
302
|
+
stripeAPIResponse = await stripe.billing.meters.deactivate(
|
|
303
|
+
stripeAPIResponse.id,
|
|
304
|
+
);
|
|
305
|
+
} else if (props.status && props.status !== stripeAPIResponse.status) {
|
|
306
|
+
console.warn(
|
|
307
|
+
`Meter ${stripeAPIResponse.id} created with status ${stripeAPIResponse.status} but requested ${props.status}. Ensure this is intended.`,
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return this(mapStripeObjectToMeterOutput(stripeAPIResponse));
|
|
313
|
+
},
|
|
314
|
+
);
|