alchemy 0.32.0 → 0.33.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.
@@ -37,6 +37,37 @@ export interface PriceRecurring {
37
37
  type TaxBehavior = Stripe.PriceCreateParams.TaxBehavior;
38
38
  type BillingScheme = Stripe.PriceCreateParams.BillingScheme;
39
39
 
40
+ /**
41
+ * Properties for price tier configuration
42
+ */
43
+ export interface PriceTier {
44
+ /**
45
+ * The flat billing amount for an entire tier, regardless of the number of units in the tier
46
+ */
47
+ flatAmount?: number;
48
+
49
+ /**
50
+ * Same as flat_amount, but accepts a decimal string with at most 12 decimal places
51
+ */
52
+ flatAmountDecimal?: string;
53
+
54
+ /**
55
+ * The per-unit amount to charge for units within this tier
56
+ */
57
+ unitAmount?: number;
58
+
59
+ /**
60
+ * Same as unit_amount, but accepts a decimal string with at most 12 decimal places
61
+ */
62
+ unitAmountDecimal?: string;
63
+
64
+ /**
65
+ * Specifies the upper bound of the tier. Can be a number or 'inf' for infinity.
66
+ * The last tier should have up_to set to 'inf'.
67
+ */
68
+ upTo: number | "inf";
69
+ }
70
+
40
71
  /**
41
72
  * Properties for creating a Stripe price
42
73
  */
@@ -119,6 +150,36 @@ export interface PriceProps {
119
150
  * If true, adopt existing resource if creation fails due to conflict
120
151
  */
121
152
  adopt?: boolean;
153
+
154
+ /**
155
+ * Each element represents a pricing tier. This parameter requires `billingScheme` to be set to `tiered`.
156
+ * An array of up to 250 tiers. Each tier has an upper bound and a price.
157
+ */
158
+ tiers?: PriceTier[];
159
+
160
+ /**
161
+ * Defines if the tiering price should be `graduated` or `volume` based.
162
+ * In `volume`-based tiering, the maximum quantity within a period determines the per unit price.
163
+ * In `graduated` tiering, pricing can change as the quantity grows.
164
+ */
165
+ tiersMode?: "graduated" | "volume" | undefined;
166
+
167
+ /**
168
+ * Apply a transformation to the reported usage or set quantity before computing the amount billed
169
+ */
170
+ transformQuantity?:
171
+ | {
172
+ /**
173
+ * Divide usage by this number
174
+ */
175
+ divideBy: number;
176
+
177
+ /**
178
+ * After dividing usage, either round the result `up` or `down`
179
+ */
180
+ round: "up" | "down";
181
+ }
182
+ | undefined;
122
183
  }
123
184
 
124
185
  /**
@@ -149,6 +210,26 @@ export interface Price extends Resource<"stripe::Price">, PriceProps {
149
210
  * The lookup key (if any) used by the customer to identify this price object
150
211
  */
151
212
  lookupKey?: string;
213
+
214
+ /**
215
+ * The pricing tiers (if using tiered billing scheme)
216
+ */
217
+ tiers?: PriceTier[];
218
+
219
+ /**
220
+ * The tiering mode (graduated or volume)
221
+ */
222
+ tiersMode?: "graduated" | "volume" | undefined;
223
+
224
+ /**
225
+ * Transform quantity configuration
226
+ */
227
+ transformQuantity?:
228
+ | {
229
+ divideBy: number;
230
+ round: "up" | "down";
231
+ }
232
+ | undefined;
152
233
  }
153
234
 
154
235
  /**
@@ -188,17 +269,56 @@ export interface Price extends Resource<"stripe::Price">, PriceProps {
188
269
  * });
189
270
  *
190
271
  * @example
191
- * // Create a tiered price with tax behavior
192
- * const tieredPrice = await Price("enterprise", {
272
+ * // Create a graduated tiered price with usage-based billing
273
+ * const tieredPrice = await Price("api-usage", {
193
274
  * currency: "usd",
194
- * unitAmount: 10000, // $100.00
195
275
  * product: "prod_xyz",
196
276
  * billingScheme: "tiered",
197
- * taxBehavior: "exclusive",
198
- * metadata: {
199
- * tier: "enterprise",
200
- * features: "all"
201
- * }
277
+ * tiersMode: "graduated",
278
+ * recurring: {
279
+ * interval: "month",
280
+ * usageType: "metered"
281
+ * },
282
+ * tiers: [
283
+ * {
284
+ * upTo: 10000,
285
+ * unitAmount: 0 // First 10k API calls free
286
+ * },
287
+ * {
288
+ * upTo: 50000,
289
+ * unitAmount: 2 // $0.02 per call up to 50k
290
+ * },
291
+ * {
292
+ * upTo: "inf",
293
+ * unitAmount: 1 // $0.01 per call beyond 50k
294
+ * }
295
+ * ]
296
+ * });
297
+ *
298
+ * @example
299
+ * // Create a volume-based tiered price with overage cap
300
+ * const volumePrice = await Price("storage", {
301
+ * currency: "usd",
302
+ * product: "prod_xyz",
303
+ * billingScheme: "tiered",
304
+ * tiersMode: "volume",
305
+ * recurring: {
306
+ * interval: "month"
307
+ * },
308
+ * tiers: [
309
+ * {
310
+ * upTo: 100,
311
+ * unitAmount: 500 // $5 per GB for up to 100GB
312
+ * },
313
+ * {
314
+ * upTo: 1000,
315
+ * unitAmount: 400 // $4 per GB for 101-1000GB
316
+ * },
317
+ * {
318
+ * upTo: "inf",
319
+ * flatAmount: 300000 // Cap at $3000 for unlimited storage
320
+ * }
321
+ * ]
202
322
  * });
203
323
  */
204
324
  export const Price = Resource(
@@ -222,23 +342,38 @@ export const Price = Resource(
222
342
 
223
343
  return this.destroy();
224
344
  }
345
+
346
+ // Validate tier-related constraints
347
+ if (props.tiers && props.billingScheme !== "tiered") {
348
+ throw new Error("Tiers can only be used with billingScheme: 'tiered'");
349
+ }
350
+
351
+ if (props.tiers && (props.unitAmount || props.unitAmountDecimal)) {
352
+ throw new Error("Cannot set both tiers and unitAmount/unitAmountDecimal");
353
+ }
354
+
355
+ if (props.tiersMode && !props.tiers) {
356
+ throw new Error("tiersMode requires tiers to be defined");
357
+ }
358
+
225
359
  try {
226
360
  let price: Stripe.Price;
227
361
 
228
362
  if (this.phase === "update" && this.output?.id) {
229
363
  // Update existing price (limited properties can be updated)
230
- const updateParams: Stripe.PriceUpdateParams = {
364
+ const updateParams: Stripe.PriceUpdateParams & { expand?: string[] } = {
231
365
  active: props.active,
232
366
  metadata: props.metadata,
233
367
  nickname: props.nickname,
234
368
  lookup_key: props.lookupKey,
235
369
  transfer_lookup_key: props.transferLookupKey,
370
+ expand: ["tiers"],
236
371
  };
237
372
 
238
373
  price = await stripe.prices.update(this.output.id, updateParams);
239
374
  } else {
240
375
  // Create new price
241
- const createParams: Stripe.PriceCreateParams = {
376
+ const createParams: Stripe.PriceCreateParams & { expand?: string[] } = {
242
377
  currency: props.currency,
243
378
  product: props.product,
244
379
  active: props.active,
@@ -248,6 +383,7 @@ export const Price = Resource(
248
383
  tax_behavior: props.taxBehavior,
249
384
  lookup_key: props.lookupKey,
250
385
  transfer_lookup_key: props.transferLookupKey,
386
+ expand: ["tiers"],
251
387
  };
252
388
 
253
389
  // Add unit amount fields
@@ -267,6 +403,29 @@ export const Price = Resource(
267
403
  };
268
404
  }
269
405
 
406
+ // Add tier configuration if present
407
+ if (props.tiers) {
408
+ createParams.tiers = props.tiers.map((tier) => ({
409
+ up_to: tier.upTo === "inf" ? "inf" : tier.upTo,
410
+ flat_amount: tier.flatAmount,
411
+ flat_amount_decimal: tier.flatAmountDecimal,
412
+ unit_amount: tier.unitAmount,
413
+ unit_amount_decimal: tier.unitAmountDecimal,
414
+ }));
415
+ }
416
+
417
+ if (props.tiersMode) {
418
+ createParams.tiers_mode = props.tiersMode;
419
+ }
420
+
421
+ // Add transform quantity if present
422
+ if (props.transformQuantity) {
423
+ createParams.transform_quantity = {
424
+ divide_by: props.transformQuantity.divideBy,
425
+ round: props.transformQuantity.round,
426
+ };
427
+ }
428
+
270
429
  if (props.lookupKey) {
271
430
  const existingPrices = await stripe.prices.list({
272
431
  lookup_keys: [props.lookupKey],
@@ -275,19 +434,25 @@ export const Price = Resource(
275
434
  if (existingPrices.data.length > 0) {
276
435
  if (props.adopt) {
277
436
  const existingPrice = existingPrices.data[0];
278
- const updateParams: Stripe.PriceUpdateParams = {
437
+ const updateParams: Stripe.PriceUpdateParams & {
438
+ expand?: string[];
439
+ } = {
279
440
  active: props.active,
280
441
  metadata: props.metadata,
281
442
  nickname: props.nickname,
282
443
  lookup_key: props.lookupKey,
283
444
  transfer_lookup_key: props.transferLookupKey,
445
+ expand: ["tiers"],
284
446
  };
285
447
  price = await stripe.prices.update(
286
448
  existingPrice.id,
287
449
  updateParams,
288
450
  );
289
451
  } else {
290
- price = existingPrices.data[0];
452
+ // Need to retrieve with expanded tiers
453
+ price = await stripe.prices.retrieve(existingPrices.data[0].id, {
454
+ expand: ["tiers"],
455
+ } as any);
291
456
  }
292
457
  } else {
293
458
  price = await stripe.prices.create(createParams);
@@ -310,6 +475,25 @@ export const Price = Resource(
310
475
  }
311
476
  : undefined;
312
477
 
478
+ // Transform Stripe tiers array to our format
479
+ const tiers = price.tiers
480
+ ? price.tiers.map((tier) => ({
481
+ flatAmount: tier.flat_amount || undefined,
482
+ flatAmountDecimal: tier.flat_amount_decimal || undefined,
483
+ unitAmount: tier.unit_amount || undefined,
484
+ unitAmountDecimal: tier.unit_amount_decimal || undefined,
485
+ upTo: tier.up_to === null ? ("inf" as const) : tier.up_to!,
486
+ }))
487
+ : undefined;
488
+
489
+ // Transform transform_quantity if present
490
+ const transformQuantity = price.transform_quantity
491
+ ? {
492
+ divideBy: price.transform_quantity.divide_by,
493
+ round: price.transform_quantity.round as "up" | "down",
494
+ }
495
+ : undefined;
496
+
313
497
  // Map Stripe API response to our output format
314
498
  return this({
315
499
  id: price.id,
@@ -328,6 +512,9 @@ export const Price = Resource(
328
512
  livemode: price.livemode,
329
513
  type: price.type as Stripe.Price.Type,
330
514
  lookupKey: price.lookup_key || undefined,
515
+ tiers: tiers,
516
+ tiersMode: price.tiers_mode ?? undefined,
517
+ transformQuantity: transformQuantity,
331
518
  });
332
519
  } catch (error) {
333
520
  logger.error("Error creating/updating price:", error);