@billkit-eu/sdk 0.6.0 → 0.7.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/src/resources.ts CHANGED
@@ -19,17 +19,18 @@
19
19
  */
20
20
 
21
21
  import { paginate, type ListResponseEnvelope } from "./pagination.js";
22
- import type { QueryValue, Transport } from "./transport.js";
22
+ import type { Transport } from "./transport.js";
23
23
 
24
24
  // ─── Shared parameter shapes ───────────────────────────────────────
25
25
 
26
26
  /**
27
27
  * Cursor-pagination knobs shared by every `list()` method.
28
28
  *
29
- * The index signature is what lets a resource-specific extension
30
- * (e.g. `EventsListParams` adds `type?: string`) flow through the
31
- * Transport's `query` shape without a cast. Excess fields are
32
- * tolerated; `undefined` values are pruned before serialisation.
29
+ * Closed on purpose: there is no index signature, so a misspelled filter
30
+ * (`provisonal`) is a compile error instead of a query parameter the
31
+ * server ignores. The Transport takes `query` as a plain `object` and
32
+ * prunes/stringifies it, which is what lets these interfaces through
33
+ * without a cast.
33
34
  */
34
35
  export interface BaseListParams {
35
36
  limit?: number;
@@ -38,7 +39,15 @@ export interface BaseListParams {
38
39
  // `limit` + `starting_after` only (see `api/billkit/api/pagination.py`).
39
40
  // Advertising a backwards cursor the server ignores is worse than not
40
41
  // having one — the request succeeds and silently re-serves page 1.
41
- readonly [key: string]: QueryValue;
42
+ }
43
+
44
+ /**
45
+ * `?expand=` on a single-object GET. The relations a route accepts differ
46
+ * per resource and are listed on each `retrieve()`; an unknown one is a
47
+ * `400` naming the ones that work.
48
+ */
49
+ export interface ExpandOptions {
50
+ expand?: string[];
42
51
  }
43
52
 
44
53
  /**
@@ -51,6 +60,43 @@ export interface PricesListParams extends BaseListParams {
51
60
  product_id?: string;
52
61
  }
53
62
 
63
+ /**
64
+ * `payments.list` params. `customer_id` narrows to one customer's
65
+ * charges. Mandate verifications are never listed, so every row is a
66
+ * real purchase attempt; check `status` before treating one as revenue.
67
+ */
68
+ export interface PaymentsListParams extends BaseListParams {
69
+ customer_id?: string;
70
+ /** Expandable here: `customer`, `subscription`. */
71
+ expand?: string[];
72
+ }
73
+
74
+ /**
75
+ * `invoices.list` params. The three id filters each narrow to one row's
76
+ * worth of invoices: `payment_id` answers "which invoice did this charge
77
+ * produce".
78
+ */
79
+ export interface InvoicesListParams extends BaseListParams {
80
+ customer_id?: string;
81
+ subscription_id?: string;
82
+ payment_id?: string;
83
+ /** One of `draft`, `open`, `paid`, `void`, `uncollectible`. */
84
+ status?: string;
85
+ /** Expandable here: `customer`. */
86
+ expand?: string[];
87
+ }
88
+
89
+ /**
90
+ * `disputes.list` params. `status` takes a comma-separated list of `open`
91
+ * / `won`. There is no `lost`, because the provider gives no signal for
92
+ * one. `payment_id` matches subscription payments only, not one-off
93
+ * charges.
94
+ */
95
+ export interface DisputesListParams extends BaseListParams {
96
+ status?: string;
97
+ payment_id?: string;
98
+ }
99
+
54
100
  /**
55
101
  * `subscriptions.list` params. Both filters take a comma-separated
56
102
  * list (`"active,past_due"`); an unrecognised value is rejected with
@@ -70,6 +116,8 @@ export interface SubscriptionsListParams extends BaseListParams {
70
116
  status?: string;
71
117
  /** `auto_renew` | `paused` | `canceling` | `stopped`, CSV. */
72
118
  renewal_state?: string;
119
+ /** Expandable here: `customer`, `price`, `refund_eligibility`. */
120
+ expand?: string[];
73
121
  }
74
122
 
75
123
  /** Optional idempotency knob carried by every mutating call. */
@@ -81,12 +129,12 @@ export interface IdempotencyOptions {
81
129
  idempotencyKey?: string;
82
130
  }
83
131
 
84
- // Keep a type alias for backward compatibility with the previous
85
- // loosely-typed ListParams. New code should reach for the
86
- // resource-specific *ListParams (e.g. `EventsListParams`).
87
- export type ListParams = BaseListParams & {
88
- readonly [key: string]: QueryValue;
89
- };
132
+ /**
133
+ * @deprecated Alias of {@link BaseListParams}, kept for callers that
134
+ * imported the older name. Reach for the resource-specific `*ListParams`
135
+ * (e.g. `EventsListParams`) instead.
136
+ */
137
+ export type ListParams = BaseListParams;
90
138
 
91
139
  // ─── Per-resource parameter shapes ─────────────────────────────────
92
140
  //
@@ -116,15 +164,27 @@ export interface CustomerListParams extends BaseListParams {
116
164
  * omitted for both.
117
165
  */
118
166
  provisional?: boolean;
167
+ /** Expandable here: `stats`. Sent as `expand=a,b`. */
168
+ expand?: string[];
169
+ }
170
+
171
+ /** Query parameters accepted by `GET /v1/products`. */
172
+ export interface ProductsListParams extends BaseListParams {
173
+ /** Expandable here: `prices`, `stats`. */
174
+ expand?: string[];
119
175
  }
120
176
 
121
177
  /**
122
178
  * Body for `POST /v1/customers/{id}/vat_number`. The VAT number is
123
179
  * sent through VIES server-side; the response carries
124
180
  * `vat_number_validated` reflecting the outcome.
181
+ *
182
+ * `vat_number: null` **clears** the registration, and is sent as an
183
+ * explicit null rather than pruned: only `undefined` is dropped.
125
184
  */
126
185
  export interface SetCustomerVatNumberParams extends IdempotencyOptions {
127
- vat_number: string;
186
+ vat_number: string | null;
187
+ /** VIES needs a country; send it when the customer has none yet. */
128
188
  country_code?: string;
129
189
  }
130
190
 
@@ -171,13 +231,36 @@ export interface UpdateProductParams extends IdempotencyOptions {
171
231
  }
172
232
 
173
233
  /**
174
- * Body for `POST /v1/prices/{id}`. `active` is the only field a price
175
- * accepts, and it moves both ways: `false` withdraws the price from sale,
176
- * `true` puts it back. The amount, currency and interval are fixed at
177
- * creation, so neither direction changes what anyone was charged.
234
+ * Body for `POST /v1/prices/{id}`. Every field is optional; omitted ones
235
+ * are left alone.
236
+ *
237
+ * The dividing line is what a field decides. `amount_cents`, `currency`,
238
+ * `interval` and `usage_type` decide **what a past charge was**, so they
239
+ * are fixed at creation and absent here, because subscriptions renew
240
+ * against a price by id and editing one would re-price live customers.
241
+ * Everything
242
+ * below decides **what happens next**, which is why it is editable:
243
+ * setting `refund_on_cancel` covers the customers already on the price.
244
+ *
245
+ * `tax_behavior` is the exception and moves one way. It can be set while
246
+ * the price is still `"unspecified"` and never changed again, because
247
+ * flipping it would restate whether tax was inside or on top of an amount
248
+ * somebody has already paid.
178
249
  */
179
250
  export interface UpdatePriceParams extends IdempotencyOptions {
180
- active: boolean;
251
+ /** `false` withdraws the price from sale, `true` puts it back. */
252
+ active?: boolean;
253
+ metadata?: Record<string, string>;
254
+ /** Settable once, while the price is still `"unspecified"`. */
255
+ tax_behavior?: "inclusive" | "exclusive";
256
+ /** Read when a checkout opens. At least one entry. */
257
+ payment_methods?: Array<
258
+ "creditcard" | "directdebit" | "ideal" | "eps" | "applepay" | "paypal" | (string & {})
259
+ >;
260
+ refund_on_cancel?: "none" | "full" | "prorated";
261
+ /** `0` disables refunds for that charge type; `N > 0` is an N-day window. */
262
+ refund_window_initial_days?: number;
263
+ refund_window_renewal_days?: number;
181
264
  }
182
265
 
183
266
  /**
@@ -369,6 +452,14 @@ export interface CreateCheckoutSessionParams extends IdempotencyOptions {
369
452
  price_id: string;
370
453
  success_url: string;
371
454
  cancel_url: string;
455
+ /**
456
+ * The buyer's ISO-3166-1 alpha-2 country, when you already know it.
457
+ * Stored on the customer if they do not have one yet, which is what
458
+ * lets VAT apply to the very first charge. On the hosted flow the buyer
459
+ * only reaches a country-collecting page after the charge exists.
460
+ * Never overwrites a country the customer already has.
461
+ */
462
+ country?: string;
372
463
  /**
373
464
  * Pin the Mollie payment method. `undefined` lets Mollie pick from
374
465
  * the customer's available methods; when set, must be in the price's
@@ -512,6 +603,8 @@ export interface UpdateWebhookEndpointParams extends IdempotencyOptions {
512
603
  export interface EventsListParams extends BaseListParams {
513
604
  /** Server-side filter, e.g. `customer.created`. */
514
605
  type?: string;
606
+ /** Expandable here: `customer`. `events.retrieve` accepts none. */
607
+ expand?: string[];
515
608
  }
516
609
 
517
610
  export interface SetPortalBrandingParams extends IdempotencyOptions {
@@ -533,7 +626,12 @@ export interface RotateProviderCredentialParams extends IdempotencyOptions {
533
626
 
534
627
  export interface CreateCouponParams extends IdempotencyOptions {
535
628
  code: string;
536
- discount_type: "percentage" | "amount" | (string & {});
629
+ /**
630
+ * `"percent"` reads `discount_value` as whole percent; `"fixed_cents"`
631
+ * reads it as minor units off the charge. Those are the only two the
632
+ * API accepts (`schemas/coupon.py`); anything else is a `422`.
633
+ */
634
+ discount_type: "percent" | "fixed_cents" | (string & {});
537
635
  discount_value: number;
538
636
  duration: "once" | "repeating" | "forever" | (string & {});
539
637
  duration_in_months?: number;
@@ -608,6 +706,42 @@ export interface CreditNotesListParams extends BaseListParams {
608
706
  customer_id?: string;
609
707
  }
610
708
 
709
+ /**
710
+ * Body for `POST /v1/tenant/billing_profile`: the seller identity that
711
+ * VAT is decided against and that an invoice prints.
712
+ *
713
+ * `country_code` is required on every call: there is nothing to leave
714
+ * alone about a jurisdiction. Every other field is partial-update: omit
715
+ * it to leave the stored value alone, or pass an explicit `null` to
716
+ * clear it, because ceasing to be VAT registered (or moving office) is a
717
+ * real event.
718
+ */
719
+ export interface SetTenantBillingProfileParams extends IdempotencyOptions {
720
+ /** ISO-3166-1 alpha-2, e.g. `"NL"`. */
721
+ country_code: string;
722
+ /** Your own EU VAT registration, or `null` to clear it. */
723
+ vat_id?: string | null;
724
+ address_line1?: string | null;
725
+ address_line2?: string | null;
726
+ postal_code?: string | null;
727
+ city?: string | null;
728
+ registration_number?: string | null;
729
+ }
730
+
731
+ /**
732
+ * Body for `POST /v1/api_keys`. The full key is returned **once**, on the
733
+ * create response, and is never retrievable again.
734
+ */
735
+ export interface CreateApiKeyParams extends IdempotencyOptions {
736
+ /** Human label, so a key can be identified before it is revoked. */
737
+ label?: string;
738
+ /**
739
+ * Narrow what the key may do. Omit to inherit the calling key's own
740
+ * scopes; a key can never grant more than it holds.
741
+ */
742
+ scopes?: string[];
743
+ }
744
+
611
745
  /** `invoices.void` params. `reason` is recorded on the audit row only. */
612
746
  export interface VoidInvoiceParams extends IdempotencyOptions {
613
747
  reason?: string;
@@ -616,10 +750,29 @@ export interface VoidInvoiceParams extends IdempotencyOptions {
616
750
  export interface CreateBillingPortalSessionParams extends IdempotencyOptions {
617
751
  subscription_id: string;
618
752
  return_url: string;
753
+ /**
754
+ * Also email the portal link to the subscription's customer, at the
755
+ * address on their record, as a tenant-branded message. Defaults to
756
+ * `false`: without it you distribute the returned `url` yourself.
757
+ */
758
+ deliver_email?: boolean;
619
759
  }
620
760
 
621
761
  // ─── Internals ─────────────────────────────────────────────────────
622
762
 
763
+ /**
764
+ * Percent-encode a caller-supplied id before it becomes a path segment.
765
+ *
766
+ * Ids reach the SDK from the caller's own storage, and one carrying `/`,
767
+ * `?` or `#` would otherwise rewrite the request: `#` truncates the path,
768
+ * `?` turns the tail into a query string, and `/` walks to a different
769
+ * route entirely. Encoding keeps the request on the route the method
770
+ * names, so a bad id is a clean `404` rather than a call somewhere else.
771
+ */
772
+ function p(id: string): string {
773
+ return encodeURIComponent(id);
774
+ }
775
+
623
776
  function dropUndefined<T extends Record<string, unknown>>(obj: T): Record<string, unknown> {
624
777
  const out: Record<string, unknown> = {};
625
778
  for (const [k, v] of Object.entries(obj)) {
@@ -683,7 +836,13 @@ function assertPriceRatesAreStrings(params: CreatePriceParams): CreatePriceParam
683
836
  abstract class BaseResource {
684
837
  constructor(protected readonly t: Transport) {}
685
838
 
686
- protected get<T>(path: string, query?: BaseListParams & Record<string, QueryValue>): Promise<T> {
839
+ /**
840
+ * `query` is a plain object rather than an index-signature type: TypeScript
841
+ * only gives an implicit index signature to type *aliases*, so a closed
842
+ * `*ListParams` interface would otherwise need a cast at every call site.
843
+ * The transport prunes `undefined`/`null` and joins arrays with commas.
844
+ */
845
+ protected get<T>(path: string, query?: object): Promise<T> {
687
846
  return this.t.request<T>({ method: "GET", path, query });
688
847
  }
689
848
 
@@ -735,11 +894,11 @@ export class Customers extends BaseResource {
735
894
  }
736
895
 
737
896
  retrieve<T = unknown>(id: string): Promise<T> {
738
- return this.get<T>(`/v1/customers/${id}`);
897
+ return this.get<T>(`/v1/customers/${p(id)}`);
739
898
  }
740
899
 
741
900
  update<T = unknown>(id: string, params: UpdateCustomerParams = {}): Promise<T> {
742
- return this.post<T, UpdateCustomerParams>(`/v1/customers/${id}`, params);
901
+ return this.post<T, UpdateCustomerParams>(`/v1/customers/${p(id)}`, params);
743
902
  }
744
903
 
745
904
  /**
@@ -753,7 +912,7 @@ export class Customers extends BaseResource {
753
912
  * charge them.
754
913
  */
755
914
  delete<T = unknown>(id: string, params: IdempotencyOptions = {}): Promise<T> {
756
- return this.del<T>(`/v1/customers/${id}`, params);
915
+ return this.del<T>(`/v1/customers/${p(id)}`, params);
757
916
  }
758
917
 
759
918
  /**
@@ -772,7 +931,7 @@ export class Customers extends BaseResource {
772
931
 
773
932
  /** Walk every page of `list()` and yield each customer. */
774
933
  iter<T = unknown>(options: { pageSize?: number } = {}): AsyncIterableIterator<T> {
775
- return paginate<T>((p) => this.get("/v1/customers", p), { pageSize: options.pageSize });
934
+ return paginate<T>((page) => this.get("/v1/customers", page), { pageSize: options.pageSize });
776
935
  }
777
936
 
778
937
  /**
@@ -781,7 +940,7 @@ export class Customers extends BaseResource {
781
940
  * reflecting whether VIES confirmed the number.
782
941
  */
783
942
  setVatNumber<T = unknown>(id: string, params: SetCustomerVatNumberParams): Promise<T> {
784
- return this.post<T, SetCustomerVatNumberParams>(`/v1/customers/${id}/vat_number`, params);
943
+ return this.post<T, SetCustomerVatNumberParams>(`/v1/customers/${p(id)}/vat_number`, params);
785
944
  }
786
945
 
787
946
  /**
@@ -794,7 +953,7 @@ export class Customers extends BaseResource {
794
953
  */
795
954
  purge<T = unknown>(id: string, params: PurgeCustomerParams = {}): Promise<T> {
796
955
  const { confirmed = true, idempotencyKey } = params;
797
- return this.postFixed<T>(`/v1/customers/${id}/purge`, { confirmed }, { idempotencyKey });
956
+ return this.postFixed<T>(`/v1/customers/${p(id)}/purge`, { confirmed }, { idempotencyKey });
798
957
  }
799
958
  }
800
959
 
@@ -804,8 +963,9 @@ export class Products extends BaseResource {
804
963
  return this.post<T, CreateProductParams>("/v1/products", params);
805
964
  }
806
965
 
807
- retrieve<T = unknown>(id: string): Promise<T> {
808
- return this.get<T>(`/v1/products/${id}`);
966
+ /** Expandable: `prices` (every price on the product), `stats`. */
967
+ retrieve<T = unknown>(id: string, options: ExpandOptions = {}): Promise<T> {
968
+ return this.get<T>(`/v1/products/${p(id)}`, options);
809
969
  }
810
970
 
811
971
  /**
@@ -818,15 +978,15 @@ export class Products extends BaseResource {
818
978
  * `active: true` un-archives.
819
979
  */
820
980
  update<T = unknown>(id: string, params: UpdateProductParams): Promise<T> {
821
- return this.post<T, UpdateProductParams>(`/v1/products/${id}`, params);
981
+ return this.post<T, UpdateProductParams>(`/v1/products/${p(id)}`, params);
822
982
  }
823
983
 
824
- list<T = unknown>(params: BaseListParams = {}): Promise<ListResponseEnvelope<T>> {
984
+ list<T = unknown>(params: ProductsListParams = {}): Promise<ListResponseEnvelope<T>> {
825
985
  return this.get<ListResponseEnvelope<T>>("/v1/products", params);
826
986
  }
827
987
 
828
988
  iter<T = unknown>(options: { pageSize?: number } = {}): AsyncIterableIterator<T> {
829
- return paginate<T>((p) => this.get("/v1/products", p), { pageSize: options.pageSize });
989
+ return paginate<T>((page) => this.get("/v1/products", page), { pageSize: options.pageSize });
830
990
  }
831
991
  }
832
992
 
@@ -851,7 +1011,7 @@ export class Prices extends BaseResource {
851
1011
  }
852
1012
 
853
1013
  retrieve<T = unknown>(id: string): Promise<T> {
854
- return this.get<T>(`/v1/prices/${id}`);
1014
+ return this.get<T>(`/v1/prices/${p(id)}`);
855
1015
  }
856
1016
 
857
1017
  /**
@@ -875,7 +1035,7 @@ export class Prices extends BaseResource {
875
1035
  * `price.archived`; putting one back emits `price.updated`.
876
1036
  */
877
1037
  update<T = unknown>(id: string, params: UpdatePriceParams): Promise<T> {
878
- return this.post<T, UpdatePriceParams>(`/v1/prices/${id}`, params);
1038
+ return this.post<T, UpdatePriceParams>(`/v1/prices/${p(id)}`, params);
879
1039
  }
880
1040
 
881
1041
  list<T = unknown>(params: PricesListParams = {}): Promise<ListResponseEnvelope<T>> {
@@ -886,7 +1046,7 @@ export class Prices extends BaseResource {
886
1046
  options: { pageSize?: number; product_id?: string } = {},
887
1047
  ): AsyncIterableIterator<T> {
888
1048
  const filter = options.product_id === undefined ? {} : { product_id: options.product_id };
889
- return paginate<T>((p) => this.get("/v1/prices", { ...filter, ...p }), {
1049
+ return paginate<T>((page) => this.get("/v1/prices", { ...filter, ...page }), {
890
1050
  pageSize: options.pageSize,
891
1051
  });
892
1052
  }
@@ -898,7 +1058,7 @@ export class CheckoutSessions extends BaseResource {
898
1058
  }
899
1059
 
900
1060
  retrieve<T = unknown>(id: string): Promise<T> {
901
- return this.get<T>(`/v1/checkout/sessions/${id}`);
1061
+ return this.get<T>(`/v1/checkout/sessions/${p(id)}`);
902
1062
  }
903
1063
  }
904
1064
 
@@ -918,13 +1078,14 @@ export class OneShotPayments extends BaseResource {
918
1078
  }
919
1079
 
920
1080
  retrieve<T = unknown>(id: string): Promise<T> {
921
- return this.get<T>(`/v1/checkout/one_shot/${id}`);
1081
+ return this.get<T>(`/v1/checkout/one_shot/${p(id)}`);
922
1082
  }
923
1083
  }
924
1084
 
925
1085
  export class Subscriptions extends BaseResource {
926
- retrieve<T = unknown>(id: string): Promise<T> {
927
- return this.get<T>(`/v1/subscriptions/${id}`);
1086
+ /** Expandable: `customer`, `price`, `refund_eligibility`. */
1087
+ retrieve<T = unknown>(id: string, options: ExpandOptions = {}): Promise<T> {
1088
+ return this.get<T>(`/v1/subscriptions/${p(id)}`, options);
928
1089
  }
929
1090
 
930
1091
  /**
@@ -953,19 +1114,19 @@ export class Subscriptions extends BaseResource {
953
1114
  // `undefined` query values are pruned by the transport, so the
954
1115
  // filter can be spread as-is without a conditional per key.
955
1116
  const { pageSize, ...filter } = options;
956
- return paginate<T>((p) => this.get("/v1/subscriptions", { ...filter, ...p }), { pageSize });
1117
+ return paginate<T>((page) => this.get("/v1/subscriptions", { ...filter, ...page }), { pageSize });
957
1118
  }
958
1119
 
959
1120
  cancel<T = unknown>(id: string, params: IdempotencyOptions = {}): Promise<T> {
960
- return this.postEmpty<T>(`/v1/subscriptions/${id}/cancel`, params);
1121
+ return this.postEmpty<T>(`/v1/subscriptions/${p(id)}/cancel`, params);
961
1122
  }
962
1123
 
963
1124
  pause<T = unknown>(id: string, params: IdempotencyOptions = {}): Promise<T> {
964
- return this.postEmpty<T>(`/v1/subscriptions/${id}/pause`, params);
1125
+ return this.postEmpty<T>(`/v1/subscriptions/${p(id)}/pause`, params);
965
1126
  }
966
1127
 
967
1128
  resume<T = unknown>(id: string, params: IdempotencyOptions = {}): Promise<T> {
968
- return this.postEmpty<T>(`/v1/subscriptions/${id}/resume`, params);
1129
+ return this.postEmpty<T>(`/v1/subscriptions/${p(id)}/resume`, params);
969
1130
  }
970
1131
 
971
1132
  /**
@@ -977,11 +1138,11 @@ export class Subscriptions extends BaseResource {
977
1138
  * Returns `409` if the period has already elapsed.
978
1139
  */
979
1140
  reactivate<T = unknown>(id: string, params: IdempotencyOptions = {}): Promise<T> {
980
- return this.postEmpty<T>(`/v1/subscriptions/${id}/reactivate`, params);
1141
+ return this.postEmpty<T>(`/v1/subscriptions/${p(id)}/reactivate`, params);
981
1142
  }
982
1143
 
983
1144
  previewUpdate<T = unknown>(id: string, params: { target_price_id: string }): Promise<T> {
984
- return this.postFixed<T>(`/v1/subscriptions/${id}/preview_update`, {
1145
+ return this.postFixed<T>(`/v1/subscriptions/${p(id)}/preview_update`, {
985
1146
  target_price_id: params.target_price_id,
986
1147
  });
987
1148
  }
@@ -991,7 +1152,7 @@ export class Subscriptions extends BaseResource {
991
1152
  params: { target_price_id: string } & IdempotencyOptions,
992
1153
  ): Promise<T> {
993
1154
  return this.postFixed<T>(
994
- `/v1/subscriptions/${id}/update`,
1155
+ `/v1/subscriptions/${p(id)}/update`,
995
1156
  { target_price_id: params.target_price_id },
996
1157
  { idempotencyKey: params.idempotencyKey },
997
1158
  );
@@ -1002,7 +1163,7 @@ export class Subscriptions extends BaseResource {
1002
1163
  params: { return_url: string } & IdempotencyOptions,
1003
1164
  ): Promise<T> {
1004
1165
  return this.postFixed<T>(
1005
- `/v1/subscriptions/${id}/reauthorize_payment_method`,
1166
+ `/v1/subscriptions/${p(id)}/reauthorize_payment_method`,
1006
1167
  { return_url: params.return_url },
1007
1168
  { idempotencyKey: params.idempotencyKey },
1008
1169
  );
@@ -1025,7 +1186,7 @@ export class Subscriptions extends BaseResource {
1025
1186
  * See {@link CreateUsageRecordParams.identifier}.
1026
1187
  */
1027
1188
  createUsageRecord<T = unknown>(id: string, params: CreateUsageRecordParams): Promise<T> {
1028
- return this.post<T, CreateUsageRecordParams>(`/v1/subscriptions/${id}/usage_records`, params);
1189
+ return this.post<T, CreateUsageRecordParams>(`/v1/subscriptions/${p(id)}/usage_records`, params);
1029
1190
  }
1030
1191
 
1031
1192
  /**
@@ -1039,7 +1200,7 @@ export class Subscriptions extends BaseResource {
1039
1200
  id: string,
1040
1201
  params: UsageRecordsListParams = {},
1041
1202
  ): Promise<ListResponseEnvelope<T>> {
1042
- return this.get<ListResponseEnvelope<T>>(`/v1/subscriptions/${id}/usage_records`, params);
1203
+ return this.get<ListResponseEnvelope<T>>(`/v1/subscriptions/${p(id)}/usage_records`, params);
1043
1204
  }
1044
1205
 
1045
1206
  /** Walk every page of `listUsageRecords()` for one subscription. */
@@ -1047,7 +1208,7 @@ export class Subscriptions extends BaseResource {
1047
1208
  id: string,
1048
1209
  options: { pageSize?: number; invoice_id?: string } = {},
1049
1210
  ): AsyncIterableIterator<T> {
1050
- return paginate<T>((p) => this.get(`/v1/subscriptions/${id}/usage_records`, p), {
1211
+ return paginate<T>((page) => this.get(`/v1/subscriptions/${p(id)}/usage_records`, page), {
1051
1212
  pageSize: options.pageSize,
1052
1213
  filters: { invoice_id: options.invoice_id },
1053
1214
  });
@@ -1072,7 +1233,7 @@ export class Subscriptions extends BaseResource {
1072
1233
  * unsettled; while one is open, this period cannot be charged.
1073
1234
  */
1074
1235
  retrieveUsageSummary<T = unknown>(id: string): Promise<T> {
1075
- return this.get<T>(`/v1/subscriptions/${id}/usage_summary`);
1236
+ return this.get<T>(`/v1/subscriptions/${p(id)}/usage_summary`);
1076
1237
  }
1077
1238
  }
1078
1239
 
@@ -1082,7 +1243,7 @@ export class Refunds extends BaseResource {
1082
1243
  }
1083
1244
 
1084
1245
  retrieve<T = unknown>(id: string): Promise<T> {
1085
- return this.get<T>(`/v1/refunds/${id}`);
1246
+ return this.get<T>(`/v1/refunds/${p(id)}`);
1086
1247
  }
1087
1248
 
1088
1249
  list<T = unknown>(params: BaseListParams = {}): Promise<ListResponseEnvelope<T>> {
@@ -1090,7 +1251,7 @@ export class Refunds extends BaseResource {
1090
1251
  }
1091
1252
 
1092
1253
  iter<T = unknown>(options: { pageSize?: number } = {}): AsyncIterableIterator<T> {
1093
- return paginate<T>((p) => this.get("/v1/refunds", p), { pageSize: options.pageSize });
1254
+ return paginate<T>((page) => this.get("/v1/refunds", page), { pageSize: options.pageSize });
1094
1255
  }
1095
1256
  }
1096
1257
 
@@ -1105,25 +1266,41 @@ export class Refunds extends BaseResource {
1105
1266
  */
1106
1267
  export class Disputes extends BaseResource {
1107
1268
  retrieve<T = unknown>(id: string): Promise<T> {
1108
- return this.get<T>(`/v1/disputes/${id}`);
1269
+ return this.get<T>(`/v1/disputes/${p(id)}`);
1109
1270
  }
1110
1271
 
1111
- list<T = unknown>(params: BaseListParams = {}): Promise<ListResponseEnvelope<T>> {
1272
+ list<T = unknown>(params: DisputesListParams = {}): Promise<ListResponseEnvelope<T>> {
1112
1273
  return this.get<ListResponseEnvelope<T>>("/v1/disputes", params);
1113
1274
  }
1114
1275
 
1115
- iter<T = unknown>(options: { pageSize?: number } = {}): AsyncIterableIterator<T> {
1116
- return paginate<T>((p) => this.get("/v1/disputes", p), { pageSize: options.pageSize });
1276
+ iter<T = unknown>(
1277
+ options: { pageSize?: number; status?: string; payment_id?: string } = {},
1278
+ ): AsyncIterableIterator<T> {
1279
+ return paginate<T>((page) => this.get("/v1/disputes", page), {
1280
+ pageSize: options.pageSize,
1281
+ filters: { status: options.status, payment_id: options.payment_id },
1282
+ });
1117
1283
  }
1118
1284
  }
1119
1285
 
1120
1286
  export class WebhookEndpoints extends BaseResource {
1287
+ /**
1288
+ * Every event type this deployment can deliver, plus the wildcard.
1289
+ *
1290
+ * `enabled_events` rejects anything not on this list, so read it rather
1291
+ * than hard-coding a set: a name that is not on it fails at
1292
+ * registration and leaves you with an endpoint that never fires.
1293
+ */
1294
+ listEventTypes<T = unknown>(): Promise<T> {
1295
+ return this.get<T>("/v1/webhook_endpoints/event_types");
1296
+ }
1297
+
1121
1298
  create<T = unknown>(params: CreateWebhookEndpointParams): Promise<T> {
1122
1299
  return this.post<T, CreateWebhookEndpointParams>("/v1/webhook_endpoints", params);
1123
1300
  }
1124
1301
 
1125
1302
  retrieve<T = unknown>(id: string): Promise<T> {
1126
- return this.get<T>(`/v1/webhook_endpoints/${id}`);
1303
+ return this.get<T>(`/v1/webhook_endpoints/${p(id)}`);
1127
1304
  }
1128
1305
 
1129
1306
  /**
@@ -1135,7 +1312,7 @@ export class WebhookEndpoints extends BaseResource {
1135
1312
  * disabling is reversible and deleting is not.
1136
1313
  */
1137
1314
  update<T = unknown>(id: string, params: UpdateWebhookEndpointParams): Promise<T> {
1138
- return this.post<T, UpdateWebhookEndpointParams>(`/v1/webhook_endpoints/${id}`, params);
1315
+ return this.post<T, UpdateWebhookEndpointParams>(`/v1/webhook_endpoints/${p(id)}`, params);
1139
1316
  }
1140
1317
 
1141
1318
  /**
@@ -1150,12 +1327,12 @@ export class WebhookEndpoints extends BaseResource {
1150
1327
  * were sent stays on record.
1151
1328
  */
1152
1329
  delete<T = unknown>(id: string, params: IdempotencyOptions = {}): Promise<T> {
1153
- return this.del<T>(`/v1/webhook_endpoints/${id}`, params);
1330
+ return this.del<T>(`/v1/webhook_endpoints/${p(id)}`, params);
1154
1331
  }
1155
1332
 
1156
1333
  /** Rotate the signing secret. The new `bkwhsec_...` is returned once. */
1157
1334
  rotateSecret<T = unknown>(id: string, params: IdempotencyOptions = {}): Promise<T> {
1158
- return this.postEmpty<T>(`/v1/webhook_endpoints/${id}/rotate_secret`, params);
1335
+ return this.postEmpty<T>(`/v1/webhook_endpoints/${p(id)}/rotate_secret`, params);
1159
1336
  }
1160
1337
 
1161
1338
  list<T = unknown>(params: BaseListParams = {}): Promise<ListResponseEnvelope<T>> {
@@ -1163,7 +1340,7 @@ export class WebhookEndpoints extends BaseResource {
1163
1340
  }
1164
1341
 
1165
1342
  iter<T = unknown>(options: { pageSize?: number } = {}): AsyncIterableIterator<T> {
1166
- return paginate<T>((p) => this.get("/v1/webhook_endpoints", p), {
1343
+ return paginate<T>((page) => this.get("/v1/webhook_endpoints", page), {
1167
1344
  pageSize: options.pageSize,
1168
1345
  });
1169
1346
  }
@@ -1180,7 +1357,7 @@ export class WebhookEndpoints extends BaseResource {
1180
1357
  params: BaseListParams = {},
1181
1358
  ): Promise<ListResponseEnvelope<T>> {
1182
1359
  return this.get<ListResponseEnvelope<T>>(
1183
- `/v1/webhook_endpoints/${endpointId}/deliveries`,
1360
+ `/v1/webhook_endpoints/${p(endpointId)}/deliveries`,
1184
1361
  params,
1185
1362
  );
1186
1363
  }
@@ -1191,14 +1368,14 @@ export class WebhookEndpoints extends BaseResource {
1191
1368
  options: { pageSize?: number } = {},
1192
1369
  ): AsyncIterableIterator<T> {
1193
1370
  return paginate<T>(
1194
- (p) => this.get(`/v1/webhook_endpoints/${endpointId}/deliveries`, p),
1371
+ (page) => this.get(`/v1/webhook_endpoints/${p(endpointId)}/deliveries`, page),
1195
1372
  { pageSize: options.pageSize },
1196
1373
  );
1197
1374
  }
1198
1375
 
1199
1376
  /** Fetch one delivery row for inspection before deciding to redeliver. */
1200
1377
  retrieveDelivery<T = unknown>(endpointId: string, deliveryId: string): Promise<T> {
1201
- return this.get<T>(`/v1/webhook_endpoints/${endpointId}/deliveries/${deliveryId}`);
1378
+ return this.get<T>(`/v1/webhook_endpoints/${p(endpointId)}/deliveries/${p(deliveryId)}`);
1202
1379
  }
1203
1380
 
1204
1381
  /**
@@ -1229,7 +1406,7 @@ export class WebhookEndpoints extends BaseResource {
1229
1406
  params: IdempotencyOptions = {},
1230
1407
  ): Promise<T> {
1231
1408
  return this.postEmpty<T>(
1232
- `/v1/webhook_endpoints/${endpointId}/deliveries/${deliveryId}/redeliver`,
1409
+ `/v1/webhook_endpoints/${p(endpointId)}/deliveries/${p(deliveryId)}/redeliver`,
1233
1410
  params,
1234
1411
  );
1235
1412
  }
@@ -1237,7 +1414,7 @@ export class WebhookEndpoints extends BaseResource {
1237
1414
 
1238
1415
  export class Events extends BaseResource {
1239
1416
  retrieve<T = unknown>(id: string): Promise<T> {
1240
- return this.get<T>(`/v1/events/${id}`);
1417
+ return this.get<T>(`/v1/events/${p(id)}`);
1241
1418
  }
1242
1419
 
1243
1420
  list<T = unknown>(params: EventsListParams = {}): Promise<ListResponseEnvelope<T>> {
@@ -1248,7 +1425,7 @@ export class Events extends BaseResource {
1248
1425
  iter<T = unknown>(
1249
1426
  options: { pageSize?: number; type?: string } = {},
1250
1427
  ): AsyncIterableIterator<T> {
1251
- return paginate<T>((p) => this.get("/v1/events", p), {
1428
+ return paginate<T>((page) => this.get("/v1/events", page), {
1252
1429
  pageSize: options.pageSize,
1253
1430
  filters: { type: options.type },
1254
1431
  });
@@ -1268,6 +1445,49 @@ export class Tenant extends BaseResource {
1268
1445
  return this.get<T>("/v1/tenant/capabilities");
1269
1446
  }
1270
1447
 
1448
+ /**
1449
+ * Your registered country and VAT number: what your customers' VAT is
1450
+ * decided against.
1451
+ *
1452
+ * `country_code` is what you have stored and can be `null`;
1453
+ * `effective_country_code` is what the next charge will really use.
1454
+ * The two differ only when you have stored nothing, which is exactly
1455
+ * the case worth spotting before a first live payment.
1456
+ */
1457
+ billingProfile<T = unknown>(): Promise<T> {
1458
+ return this.get<T>("/v1/tenant/billing_profile");
1459
+ }
1460
+
1461
+ /**
1462
+ * Set the seller identity. `country_code` is required on every call;
1463
+ * every other field is partial-update, with an explicit `null` to
1464
+ * clear. Changes take effect on the next charge only. Tax is written
1465
+ * onto a payment and its invoice before money moves, and nothing goes
1466
+ * back and recalculates it.
1467
+ */
1468
+ setBillingProfile<T = unknown>(params: SetTenantBillingProfileParams): Promise<T> {
1469
+ return this.post<T, SetTenantBillingProfileParams>("/v1/tenant/billing_profile", params);
1470
+ }
1471
+
1472
+ /**
1473
+ * Download everything in the account as one JSON document, as raw bytes.
1474
+ *
1475
+ * ```ts
1476
+ * await writeFile("export.json", Buffer.from(await client.tenant.export()));
1477
+ * ```
1478
+ *
1479
+ * The GDPR Article 20 portability route, and the way to take a backup.
1480
+ * It is `application/json` streamed inline, with no redirect, and each
1481
+ * record has the same shape its `GET` route returns, with
1482
+ * `billkit_export_version` naming the shape. It can be large, so write
1483
+ * it to a file rather than holding it in memory. Test and live data
1484
+ * export separately: you get whichever mode the key belongs to. The
1485
+ * access is recorded in your audit log.
1486
+ */
1487
+ export(): Promise<ArrayBuffer> {
1488
+ return this.t.requestBinary({ method: "GET", path: "/v1/tenant/export" });
1489
+ }
1490
+
1271
1491
  /** Current portal branding row (business name, theme, capability flags). */
1272
1492
  portalBranding<T = unknown>(): Promise<T> {
1273
1493
  return this.get<T>("/v1/tenant/portal_branding");
@@ -1305,7 +1525,7 @@ export class Coupons extends BaseResource {
1305
1525
  }
1306
1526
 
1307
1527
  retrieve<T = unknown>(id: string): Promise<T> {
1308
- return this.get<T>(`/v1/coupons/${id}`);
1528
+ return this.get<T>(`/v1/coupons/${p(id)}`);
1309
1529
  }
1310
1530
 
1311
1531
  /**
@@ -1317,7 +1537,7 @@ export class Coupons extends BaseResource {
1317
1537
  * customer was charged. `active: true` brings the campaign back.
1318
1538
  */
1319
1539
  update<T = unknown>(id: string, params: UpdateCouponParams): Promise<T> {
1320
- return this.post<T, UpdateCouponParams>(`/v1/coupons/${id}`, params);
1540
+ return this.post<T, UpdateCouponParams>(`/v1/coupons/${p(id)}`, params);
1321
1541
  }
1322
1542
 
1323
1543
  /**
@@ -1341,7 +1561,7 @@ export class Coupons extends BaseResource {
1341
1561
  }
1342
1562
 
1343
1563
  iter<T = unknown>(options: { pageSize?: number } = {}): AsyncIterableIterator<T> {
1344
- return paginate<T>((p) => this.get("/v1/coupons", p), { pageSize: options.pageSize });
1564
+ return paginate<T>((page) => this.get("/v1/coupons", page), { pageSize: options.pageSize });
1345
1565
  }
1346
1566
  }
1347
1567
 
@@ -1351,7 +1571,7 @@ export class TaxRates extends BaseResource {
1351
1571
  }
1352
1572
 
1353
1573
  retrieve<T = unknown>(id: string): Promise<T> {
1354
- return this.get<T>(`/v1/tax_rates/${id}`);
1574
+ return this.get<T>(`/v1/tax_rates/${p(id)}`);
1355
1575
  }
1356
1576
 
1357
1577
  /**
@@ -1363,7 +1583,7 @@ export class TaxRates extends BaseResource {
1363
1583
  * is no `delete()`.
1364
1584
  */
1365
1585
  update<T = unknown>(id: string, params: UpdateTaxRateParams): Promise<T> {
1366
- return this.post<T, UpdateTaxRateParams>(`/v1/tax_rates/${id}`, params);
1586
+ return this.post<T, UpdateTaxRateParams>(`/v1/tax_rates/${p(id)}`, params);
1367
1587
  }
1368
1588
 
1369
1589
  list<T = unknown>(params: BaseListParams = {}): Promise<ListResponseEnvelope<T>> {
@@ -1371,7 +1591,7 @@ export class TaxRates extends BaseResource {
1371
1591
  }
1372
1592
 
1373
1593
  iter<T = unknown>(options: { pageSize?: number } = {}): AsyncIterableIterator<T> {
1374
- return paginate<T>((p) => this.get("/v1/tax_rates", p), { pageSize: options.pageSize });
1594
+ return paginate<T>((page) => this.get("/v1/tax_rates", page), { pageSize: options.pageSize });
1375
1595
  }
1376
1596
  }
1377
1597
 
@@ -1383,8 +1603,9 @@ export class TaxRates extends BaseResource {
1383
1603
  * {@link Invoices.retrievePdf}.
1384
1604
  */
1385
1605
  export class Invoices extends BaseResource {
1386
- retrieve<T = unknown>(id: string): Promise<T> {
1387
- return this.get<T>(`/v1/invoices/${id}`);
1606
+ /** Expandable: `customer`. */
1607
+ retrieve<T = unknown>(id: string, options: ExpandOptions = {}): Promise<T> {
1608
+ return this.get<T>(`/v1/invoices/${p(id)}`, options);
1388
1609
  }
1389
1610
 
1390
1611
  /**
@@ -1406,15 +1627,38 @@ export class Invoices extends BaseResource {
1406
1627
  * structured invoice for tenants who render their own.
1407
1628
  */
1408
1629
  retrievePdf(id: string): Promise<ArrayBuffer> {
1409
- return this.t.requestBinary({ method: "GET", path: `/v1/invoices/${id}/pdf` });
1630
+ return this.t.requestBinary({ method: "GET", path: `/v1/invoices/${p(id)}/pdf` });
1410
1631
  }
1411
1632
 
1412
- list<T = unknown>(params: BaseListParams = {}): Promise<ListResponseEnvelope<T>> {
1633
+ /**
1634
+ * Send the customer their invoice again.
1635
+ *
1636
+ * The same tenant-branded "your invoice is ready" email, with a fresh
1637
+ * portal link, because the one in the original may have expired. It
1638
+ * goes to the address captured **on the invoice**, not the customer's
1639
+ * current one: this is a copy of a document that was issued to
1640
+ * somebody. An invoice with no address on file is a
1641
+ * `InvalidRequestError` rather than a send that did not happen.
1642
+ */
1643
+ sendEmail<T = unknown>(id: string, params: IdempotencyOptions = {}): Promise<T> {
1644
+ return this.postEmpty<T>(`/v1/invoices/${p(id)}/email`, params);
1645
+ }
1646
+
1647
+ list<T = unknown>(params: InvoicesListParams = {}): Promise<ListResponseEnvelope<T>> {
1413
1648
  return this.get<ListResponseEnvelope<T>>("/v1/invoices", params);
1414
1649
  }
1415
1650
 
1416
- iter<T = unknown>(options: { pageSize?: number } = {}): AsyncIterableIterator<T> {
1417
- return paginate<T>((p) => this.get("/v1/invoices", p), { pageSize: options.pageSize });
1651
+ iter<T = unknown>(
1652
+ options: {
1653
+ pageSize?: number;
1654
+ customer_id?: string;
1655
+ subscription_id?: string;
1656
+ payment_id?: string;
1657
+ status?: string;
1658
+ } = {},
1659
+ ): AsyncIterableIterator<T> {
1660
+ const { pageSize, ...filters } = options;
1661
+ return paginate<T>((page) => this.get("/v1/invoices", page), { pageSize, filters });
1418
1662
  }
1419
1663
 
1420
1664
  /**
@@ -1433,7 +1677,7 @@ export class Invoices extends BaseResource {
1433
1677
  * Idempotent: re-voiding an already-void invoice returns it unchanged.
1434
1678
  */
1435
1679
  void<T = unknown>(id: string, params: VoidInvoiceParams = {}): Promise<T> {
1436
- return this.post<T, VoidInvoiceParams>(`/v1/invoices/${id}/void`, params);
1680
+ return this.post<T, VoidInvoiceParams>(`/v1/invoices/${p(id)}/void`, params);
1437
1681
  }
1438
1682
  }
1439
1683
 
@@ -1449,7 +1693,7 @@ export class Invoices extends BaseResource {
1449
1693
  */
1450
1694
  export class CreditNotes extends BaseResource {
1451
1695
  retrieve<T = unknown>(id: string): Promise<T> {
1452
- return this.get<T>(`/v1/credit_notes/${id}`);
1696
+ return this.get<T>(`/v1/credit_notes/${p(id)}`);
1453
1697
  }
1454
1698
 
1455
1699
  /**
@@ -1458,7 +1702,7 @@ export class CreditNotes extends BaseResource {
1458
1702
  * `302`, and `501 rendering_pending` on a deployment with no renderer.
1459
1703
  */
1460
1704
  retrievePdf(id: string): Promise<ArrayBuffer> {
1461
- return this.t.requestBinary({ method: "GET", path: `/v1/credit_notes/${id}/pdf` });
1705
+ return this.t.requestBinary({ method: "GET", path: `/v1/credit_notes/${p(id)}/pdf` });
1462
1706
  }
1463
1707
 
1464
1708
  list<T = unknown>(params: CreditNotesListParams = {}): Promise<ListResponseEnvelope<T>> {
@@ -1468,7 +1712,7 @@ export class CreditNotes extends BaseResource {
1468
1712
  iter<T = unknown>(
1469
1713
  options: { pageSize?: number; invoice_id?: string; customer_id?: string } = {},
1470
1714
  ): AsyncIterableIterator<T> {
1471
- return paginate<T>((p) => this.get("/v1/credit_notes", p), {
1715
+ return paginate<T>((page) => this.get("/v1/credit_notes", page), {
1472
1716
  pageSize: options.pageSize,
1473
1717
  filters: { invoice_id: options.invoice_id, customer_id: options.customer_id },
1474
1718
  });
@@ -1484,7 +1728,7 @@ export class CreditNotes extends BaseResource {
1484
1728
  */
1485
1729
  export class AuditLogs extends BaseResource {
1486
1730
  retrieve<T = unknown>(id: string): Promise<T> {
1487
- return this.get<T>(`/v1/audit_logs/${id}`);
1731
+ return this.get<T>(`/v1/audit_logs/${p(id)}`);
1488
1732
  }
1489
1733
 
1490
1734
  list<T = unknown>(params: AuditLogsListParams = {}): Promise<ListResponseEnvelope<T>> {
@@ -1500,7 +1744,7 @@ export class AuditLogs extends BaseResource {
1500
1744
  actor_id?: string;
1501
1745
  } = {},
1502
1746
  ): AsyncIterableIterator<T> {
1503
- return paginate<T>((p) => this.get("/v1/audit_logs", p), {
1747
+ return paginate<T>((page) => this.get("/v1/audit_logs", page), {
1504
1748
  pageSize: options.pageSize,
1505
1749
  filters: {
1506
1750
  action: options.action,
@@ -1520,16 +1764,36 @@ export class AuditLogs extends BaseResource {
1520
1764
  * refunds and disputes are separate flows.
1521
1765
  */
1522
1766
  export class Payments extends BaseResource {
1523
- retrieve<T = unknown>(id: string): Promise<T> {
1524
- return this.get<T>(`/v1/payments/${id}`);
1767
+ /** Expandable: `customer`, `subscription`. */
1768
+ retrieve<T = unknown>(id: string, options: ExpandOptions = {}): Promise<T> {
1769
+ return this.get<T>(`/v1/payments/${p(id)}`, options);
1525
1770
  }
1526
1771
 
1527
- list<T = unknown>(params: BaseListParams = {}): Promise<ListResponseEnvelope<T>> {
1772
+ /**
1773
+ * Fetch the provider's own record of this charge, live.
1774
+ *
1775
+ * Reads Mollie at request time rather than a stored copy, so it carries
1776
+ * what BillKit deliberately does not keep: the card BIN, the iDEAL
1777
+ * bank, the provider's own status string. Reading live means it can
1778
+ * fail: a provider outage or a charge old enough to have aged out
1779
+ * answers `200` with `available: false` and a short reason, so render
1780
+ * the rest of the page regardless.
1781
+ */
1782
+ retrieveProvider<T = unknown>(id: string): Promise<T> {
1783
+ return this.get<T>(`/v1/payments/${p(id)}/provider`);
1784
+ }
1785
+
1786
+ list<T = unknown>(params: PaymentsListParams = {}): Promise<ListResponseEnvelope<T>> {
1528
1787
  return this.get<ListResponseEnvelope<T>>("/v1/payments", params);
1529
1788
  }
1530
1789
 
1531
- iter<T = unknown>(options: { pageSize?: number } = {}): AsyncIterableIterator<T> {
1532
- return paginate<T>((p) => this.get("/v1/payments", p), { pageSize: options.pageSize });
1790
+ iter<T = unknown>(
1791
+ options: { pageSize?: number; customer_id?: string } = {},
1792
+ ): AsyncIterableIterator<T> {
1793
+ return paginate<T>((page) => this.get("/v1/payments", page), {
1794
+ pageSize: options.pageSize,
1795
+ filters: { customer_id: options.customer_id },
1796
+ });
1533
1797
  }
1534
1798
  }
1535
1799
 
@@ -1543,18 +1807,56 @@ export class Payments extends BaseResource {
1543
1807
  */
1544
1808
  export class BillingPortalSessions extends BaseResource {
1545
1809
  create<T = unknown>(params: CreateBillingPortalSessionParams): Promise<T> {
1546
- return this.postFixed<T>(
1547
- "/v1/billing_portal/sessions",
1548
- {
1549
- subscription_id: params.subscription_id,
1550
- return_url: params.return_url,
1551
- },
1552
- { idempotencyKey: params.idempotencyKey },
1553
- );
1810
+ return this.post<T, CreateBillingPortalSessionParams>("/v1/billing_portal/sessions", params);
1554
1811
  }
1555
1812
 
1556
1813
  /** Kill an in-the-wild portal session. Idempotent. */
1557
1814
  revoke<T = unknown>(id: string, params: IdempotencyOptions = {}): Promise<T> {
1558
- return this.postEmpty<T>(`/v1/billing_portal/sessions/${id}/revoke`, params);
1815
+ return this.postEmpty<T>(`/v1/billing_portal/sessions/${p(id)}/revoke`, params);
1816
+ }
1817
+ }
1818
+
1819
+ /**
1820
+ * Issue, inspect and revoke API keys.
1821
+ *
1822
+ * A key is issued in the same mode as the key that created it, so a test
1823
+ * key can only mint test keys, and it can never grant scopes it does not
1824
+ * hold itself. The secret is returned **once**, on
1825
+ * {@link ApiKeys.create}; every later read carries only the prefix.
1826
+ */
1827
+ export class ApiKeys extends BaseResource {
1828
+ /**
1829
+ * Issue a new key. The response's `secret` is the only time the full
1830
+ * key exists outside the caller's own storage, so record it now.
1831
+ */
1832
+ create<T = unknown>(params: CreateApiKeyParams = {}): Promise<T> {
1833
+ return this.post<T, CreateApiKeyParams>("/v1/api_keys", params);
1834
+ }
1835
+
1836
+ /**
1837
+ * One key's metadata: prefix, label, scopes, `revoked_at`, and
1838
+ * `last_used_at`, which is the field to read before revoking one.
1839
+ */
1840
+ retrieve<T = unknown>(id: string): Promise<T> {
1841
+ return this.get<T>(`/v1/api_keys/${p(id)}`);
1842
+ }
1843
+
1844
+ /**
1845
+ * Revoke a key so it stops working. Immediate and irreversible; issue a
1846
+ * new key instead. Revoking an already-revoked key returns it
1847
+ * unchanged, so a retry is safe, and a key may revoke itself, which is
1848
+ * what you want when the leaked key is the one you are calling with.
1849
+ */
1850
+ revoke<T = unknown>(id: string, params: IdempotencyOptions = {}): Promise<T> {
1851
+ return this.postEmpty<T>(`/v1/api_keys/${p(id)}/revoke`, params);
1852
+ }
1853
+
1854
+ /** List keys, newest first. Revoked ones are included; check `revoked_at`. */
1855
+ list<T = unknown>(params: BaseListParams = {}): Promise<ListResponseEnvelope<T>> {
1856
+ return this.get<ListResponseEnvelope<T>>("/v1/api_keys", params);
1857
+ }
1858
+
1859
+ iter<T = unknown>(options: { pageSize?: number } = {}): AsyncIterableIterator<T> {
1860
+ return paginate<T>((page) => this.get("/v1/api_keys", page), { pageSize: options.pageSize });
1559
1861
  }
1560
1862
  }