@billkit-eu/sdk 0.5.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
  /**
@@ -257,7 +340,9 @@ export interface CreatePriceParams extends IdempotencyOptions {
257
340
  metadata?: Record<string, string>;
258
341
  trial_days?: number;
259
342
  trial_verification_cents?: number;
260
- payment_methods?: Array<"creditcard" | "directdebit" | "ideal" | "applepay" | (string & {})>;
343
+ payment_methods?: Array<
344
+ "creditcard" | "directdebit" | "ideal" | "eps" | "applepay" | "paypal" | (string & {})
345
+ >;
261
346
  /**
262
347
  * What a cancellation refunds without being asked. `"none"` (the default)
263
348
  * nothing; `"full"` the whole last charge; `"prorated"` the unused part
@@ -367,12 +452,33 @@ export interface CreateCheckoutSessionParams extends IdempotencyOptions {
367
452
  price_id: string;
368
453
  success_url: string;
369
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;
370
463
  /**
371
464
  * Pin the Mollie payment method. `undefined` lets Mollie pick from
372
465
  * the customer's available methods; when set, must be in the price's
373
466
  * `payment_methods` allowlist.
467
+ *
468
+ * Subscription-starting only, so this is deliberately NARROWER than the
469
+ * one-shot union: `bancontact` and `banktransfer` are absent because
470
+ * neither can mint the mandate a renewal needs. Mollie refuses the
471
+ * latter outright with "The payment method does not support sequence
472
+ * type".
374
473
  */
375
- method?: "creditcard" | "directdebit" | "ideal" | "applepay" | (string & {});
474
+ method?:
475
+ | "creditcard"
476
+ | "directdebit"
477
+ | "ideal"
478
+ | "eps"
479
+ | "applepay"
480
+ | "paypal"
481
+ | (string & {});
376
482
  /** Optional coupon code applied at checkout; atomically claimed. */
377
483
  coupon_code?: string;
378
484
  /**
@@ -430,8 +536,12 @@ export interface CreateOneShotPaymentParams extends IdempotencyOptions {
430
536
  /**
431
537
  * Concrete Mollie method to charge with. Required, because a one-shot commits
432
538
  * up front). Validated against the tenant's capability allowlist for
433
- * `currency`; one-off methods like `bancontact`/`eps` are allowed here
434
- * even though they can't back a subscription.
539
+ * `currency`; one-off methods like `bancontact` and `banktransfer` are
540
+ * allowed here even though they can't back a subscription.
541
+ *
542
+ * `banktransfer` settles in DAYS, not seconds: the payer is handed bank
543
+ * details and Mollie holds the payment `open` for about a fortnight. Expect
544
+ * `one_shot_payment.paid` long after the call returns.
435
545
  *
436
546
  * `giropay` was removed: the scheme shut down at the end of 2024 and the
437
547
  * server now 422s it. The `(string & {})` tail keeps this open on
@@ -446,6 +556,8 @@ export interface CreateOneShotPaymentParams extends IdempotencyOptions {
446
556
  | "bancontact"
447
557
  | "eps"
448
558
  | "applepay"
559
+ | "paypal"
560
+ | "banktransfer"
449
561
  | (string & {});
450
562
  /** Where Mollie returns the payer after the hosted checkout. */
451
563
  success_url: string;
@@ -491,6 +603,8 @@ export interface UpdateWebhookEndpointParams extends IdempotencyOptions {
491
603
  export interface EventsListParams extends BaseListParams {
492
604
  /** Server-side filter, e.g. `customer.created`. */
493
605
  type?: string;
606
+ /** Expandable here: `customer`. `events.retrieve` accepts none. */
607
+ expand?: string[];
494
608
  }
495
609
 
496
610
  export interface SetPortalBrandingParams extends IdempotencyOptions {
@@ -512,7 +626,12 @@ export interface RotateProviderCredentialParams extends IdempotencyOptions {
512
626
 
513
627
  export interface CreateCouponParams extends IdempotencyOptions {
514
628
  code: string;
515
- 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 & {});
516
635
  discount_value: number;
517
636
  duration: "once" | "repeating" | "forever" | (string & {});
518
637
  duration_in_months?: number;
@@ -560,9 +679,18 @@ export interface UpdateTaxRateParams extends IdempotencyOptions {
560
679
  active?: boolean;
561
680
  }
562
681
 
682
+ /**
683
+ * `auditLogs.list` params. All four filters match exactly and combine.
684
+ *
685
+ * `resource_type` narrows to a kind (`"customer"`, `"price"`);
686
+ * `resource_id` narrows to one row, which is the "everything that ever
687
+ * happened to this customer" question an audit log mostly exists for.
688
+ * Pair them or use `resource_id` alone — ids are already unique.
689
+ */
563
690
  export interface AuditLogsListParams extends BaseListParams {
564
691
  action?: string;
565
692
  resource_type?: string;
693
+ resource_id?: string;
566
694
  actor_id?: string;
567
695
  }
568
696
 
@@ -578,6 +706,42 @@ export interface CreditNotesListParams extends BaseListParams {
578
706
  customer_id?: string;
579
707
  }
580
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
+
581
745
  /** `invoices.void` params. `reason` is recorded on the audit row only. */
582
746
  export interface VoidInvoiceParams extends IdempotencyOptions {
583
747
  reason?: string;
@@ -586,10 +750,29 @@ export interface VoidInvoiceParams extends IdempotencyOptions {
586
750
  export interface CreateBillingPortalSessionParams extends IdempotencyOptions {
587
751
  subscription_id: string;
588
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;
589
759
  }
590
760
 
591
761
  // ─── Internals ─────────────────────────────────────────────────────
592
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
+
593
776
  function dropUndefined<T extends Record<string, unknown>>(obj: T): Record<string, unknown> {
594
777
  const out: Record<string, unknown> = {};
595
778
  for (const [k, v] of Object.entries(obj)) {
@@ -653,7 +836,13 @@ function assertPriceRatesAreStrings(params: CreatePriceParams): CreatePriceParam
653
836
  abstract class BaseResource {
654
837
  constructor(protected readonly t: Transport) {}
655
838
 
656
- 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> {
657
846
  return this.t.request<T>({ method: "GET", path, query });
658
847
  }
659
848
 
@@ -705,11 +894,11 @@ export class Customers extends BaseResource {
705
894
  }
706
895
 
707
896
  retrieve<T = unknown>(id: string): Promise<T> {
708
- return this.get<T>(`/v1/customers/${id}`);
897
+ return this.get<T>(`/v1/customers/${p(id)}`);
709
898
  }
710
899
 
711
900
  update<T = unknown>(id: string, params: UpdateCustomerParams = {}): Promise<T> {
712
- return this.post<T, UpdateCustomerParams>(`/v1/customers/${id}`, params);
901
+ return this.post<T, UpdateCustomerParams>(`/v1/customers/${p(id)}`, params);
713
902
  }
714
903
 
715
904
  /**
@@ -723,7 +912,7 @@ export class Customers extends BaseResource {
723
912
  * charge them.
724
913
  */
725
914
  delete<T = unknown>(id: string, params: IdempotencyOptions = {}): Promise<T> {
726
- return this.del<T>(`/v1/customers/${id}`, params);
915
+ return this.del<T>(`/v1/customers/${p(id)}`, params);
727
916
  }
728
917
 
729
918
  /**
@@ -742,7 +931,7 @@ export class Customers extends BaseResource {
742
931
 
743
932
  /** Walk every page of `list()` and yield each customer. */
744
933
  iter<T = unknown>(options: { pageSize?: number } = {}): AsyncIterableIterator<T> {
745
- 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 });
746
935
  }
747
936
 
748
937
  /**
@@ -751,7 +940,7 @@ export class Customers extends BaseResource {
751
940
  * reflecting whether VIES confirmed the number.
752
941
  */
753
942
  setVatNumber<T = unknown>(id: string, params: SetCustomerVatNumberParams): Promise<T> {
754
- return this.post<T, SetCustomerVatNumberParams>(`/v1/customers/${id}/vat_number`, params);
943
+ return this.post<T, SetCustomerVatNumberParams>(`/v1/customers/${p(id)}/vat_number`, params);
755
944
  }
756
945
 
757
946
  /**
@@ -764,7 +953,7 @@ export class Customers extends BaseResource {
764
953
  */
765
954
  purge<T = unknown>(id: string, params: PurgeCustomerParams = {}): Promise<T> {
766
955
  const { confirmed = true, idempotencyKey } = params;
767
- return this.postFixed<T>(`/v1/customers/${id}/purge`, { confirmed }, { idempotencyKey });
956
+ return this.postFixed<T>(`/v1/customers/${p(id)}/purge`, { confirmed }, { idempotencyKey });
768
957
  }
769
958
  }
770
959
 
@@ -774,8 +963,9 @@ export class Products extends BaseResource {
774
963
  return this.post<T, CreateProductParams>("/v1/products", params);
775
964
  }
776
965
 
777
- retrieve<T = unknown>(id: string): Promise<T> {
778
- 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);
779
969
  }
780
970
 
781
971
  /**
@@ -788,15 +978,15 @@ export class Products extends BaseResource {
788
978
  * `active: true` un-archives.
789
979
  */
790
980
  update<T = unknown>(id: string, params: UpdateProductParams): Promise<T> {
791
- return this.post<T, UpdateProductParams>(`/v1/products/${id}`, params);
981
+ return this.post<T, UpdateProductParams>(`/v1/products/${p(id)}`, params);
792
982
  }
793
983
 
794
- list<T = unknown>(params: BaseListParams = {}): Promise<ListResponseEnvelope<T>> {
984
+ list<T = unknown>(params: ProductsListParams = {}): Promise<ListResponseEnvelope<T>> {
795
985
  return this.get<ListResponseEnvelope<T>>("/v1/products", params);
796
986
  }
797
987
 
798
988
  iter<T = unknown>(options: { pageSize?: number } = {}): AsyncIterableIterator<T> {
799
- 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 });
800
990
  }
801
991
  }
802
992
 
@@ -821,7 +1011,7 @@ export class Prices extends BaseResource {
821
1011
  }
822
1012
 
823
1013
  retrieve<T = unknown>(id: string): Promise<T> {
824
- return this.get<T>(`/v1/prices/${id}`);
1014
+ return this.get<T>(`/v1/prices/${p(id)}`);
825
1015
  }
826
1016
 
827
1017
  /**
@@ -845,7 +1035,7 @@ export class Prices extends BaseResource {
845
1035
  * `price.archived`; putting one back emits `price.updated`.
846
1036
  */
847
1037
  update<T = unknown>(id: string, params: UpdatePriceParams): Promise<T> {
848
- return this.post<T, UpdatePriceParams>(`/v1/prices/${id}`, params);
1038
+ return this.post<T, UpdatePriceParams>(`/v1/prices/${p(id)}`, params);
849
1039
  }
850
1040
 
851
1041
  list<T = unknown>(params: PricesListParams = {}): Promise<ListResponseEnvelope<T>> {
@@ -856,7 +1046,7 @@ export class Prices extends BaseResource {
856
1046
  options: { pageSize?: number; product_id?: string } = {},
857
1047
  ): AsyncIterableIterator<T> {
858
1048
  const filter = options.product_id === undefined ? {} : { product_id: options.product_id };
859
- return paginate<T>((p) => this.get("/v1/prices", { ...filter, ...p }), {
1049
+ return paginate<T>((page) => this.get("/v1/prices", { ...filter, ...page }), {
860
1050
  pageSize: options.pageSize,
861
1051
  });
862
1052
  }
@@ -868,7 +1058,7 @@ export class CheckoutSessions extends BaseResource {
868
1058
  }
869
1059
 
870
1060
  retrieve<T = unknown>(id: string): Promise<T> {
871
- return this.get<T>(`/v1/checkout/sessions/${id}`);
1061
+ return this.get<T>(`/v1/checkout/sessions/${p(id)}`);
872
1062
  }
873
1063
  }
874
1064
 
@@ -888,13 +1078,14 @@ export class OneShotPayments extends BaseResource {
888
1078
  }
889
1079
 
890
1080
  retrieve<T = unknown>(id: string): Promise<T> {
891
- return this.get<T>(`/v1/checkout/one_shot/${id}`);
1081
+ return this.get<T>(`/v1/checkout/one_shot/${p(id)}`);
892
1082
  }
893
1083
  }
894
1084
 
895
1085
  export class Subscriptions extends BaseResource {
896
- retrieve<T = unknown>(id: string): Promise<T> {
897
- 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);
898
1089
  }
899
1090
 
900
1091
  /**
@@ -923,19 +1114,19 @@ export class Subscriptions extends BaseResource {
923
1114
  // `undefined` query values are pruned by the transport, so the
924
1115
  // filter can be spread as-is without a conditional per key.
925
1116
  const { pageSize, ...filter } = options;
926
- return paginate<T>((p) => this.get("/v1/subscriptions", { ...filter, ...p }), { pageSize });
1117
+ return paginate<T>((page) => this.get("/v1/subscriptions", { ...filter, ...page }), { pageSize });
927
1118
  }
928
1119
 
929
1120
  cancel<T = unknown>(id: string, params: IdempotencyOptions = {}): Promise<T> {
930
- return this.postEmpty<T>(`/v1/subscriptions/${id}/cancel`, params);
1121
+ return this.postEmpty<T>(`/v1/subscriptions/${p(id)}/cancel`, params);
931
1122
  }
932
1123
 
933
1124
  pause<T = unknown>(id: string, params: IdempotencyOptions = {}): Promise<T> {
934
- return this.postEmpty<T>(`/v1/subscriptions/${id}/pause`, params);
1125
+ return this.postEmpty<T>(`/v1/subscriptions/${p(id)}/pause`, params);
935
1126
  }
936
1127
 
937
1128
  resume<T = unknown>(id: string, params: IdempotencyOptions = {}): Promise<T> {
938
- return this.postEmpty<T>(`/v1/subscriptions/${id}/resume`, params);
1129
+ return this.postEmpty<T>(`/v1/subscriptions/${p(id)}/resume`, params);
939
1130
  }
940
1131
 
941
1132
  /**
@@ -947,11 +1138,11 @@ export class Subscriptions extends BaseResource {
947
1138
  * Returns `409` if the period has already elapsed.
948
1139
  */
949
1140
  reactivate<T = unknown>(id: string, params: IdempotencyOptions = {}): Promise<T> {
950
- return this.postEmpty<T>(`/v1/subscriptions/${id}/reactivate`, params);
1141
+ return this.postEmpty<T>(`/v1/subscriptions/${p(id)}/reactivate`, params);
951
1142
  }
952
1143
 
953
1144
  previewUpdate<T = unknown>(id: string, params: { target_price_id: string }): Promise<T> {
954
- return this.postFixed<T>(`/v1/subscriptions/${id}/preview_update`, {
1145
+ return this.postFixed<T>(`/v1/subscriptions/${p(id)}/preview_update`, {
955
1146
  target_price_id: params.target_price_id,
956
1147
  });
957
1148
  }
@@ -961,7 +1152,7 @@ export class Subscriptions extends BaseResource {
961
1152
  params: { target_price_id: string } & IdempotencyOptions,
962
1153
  ): Promise<T> {
963
1154
  return this.postFixed<T>(
964
- `/v1/subscriptions/${id}/update`,
1155
+ `/v1/subscriptions/${p(id)}/update`,
965
1156
  { target_price_id: params.target_price_id },
966
1157
  { idempotencyKey: params.idempotencyKey },
967
1158
  );
@@ -972,7 +1163,7 @@ export class Subscriptions extends BaseResource {
972
1163
  params: { return_url: string } & IdempotencyOptions,
973
1164
  ): Promise<T> {
974
1165
  return this.postFixed<T>(
975
- `/v1/subscriptions/${id}/reauthorize_payment_method`,
1166
+ `/v1/subscriptions/${p(id)}/reauthorize_payment_method`,
976
1167
  { return_url: params.return_url },
977
1168
  { idempotencyKey: params.idempotencyKey },
978
1169
  );
@@ -995,7 +1186,7 @@ export class Subscriptions extends BaseResource {
995
1186
  * See {@link CreateUsageRecordParams.identifier}.
996
1187
  */
997
1188
  createUsageRecord<T = unknown>(id: string, params: CreateUsageRecordParams): Promise<T> {
998
- return this.post<T, CreateUsageRecordParams>(`/v1/subscriptions/${id}/usage_records`, params);
1189
+ return this.post<T, CreateUsageRecordParams>(`/v1/subscriptions/${p(id)}/usage_records`, params);
999
1190
  }
1000
1191
 
1001
1192
  /**
@@ -1009,7 +1200,7 @@ export class Subscriptions extends BaseResource {
1009
1200
  id: string,
1010
1201
  params: UsageRecordsListParams = {},
1011
1202
  ): Promise<ListResponseEnvelope<T>> {
1012
- return this.get<ListResponseEnvelope<T>>(`/v1/subscriptions/${id}/usage_records`, params);
1203
+ return this.get<ListResponseEnvelope<T>>(`/v1/subscriptions/${p(id)}/usage_records`, params);
1013
1204
  }
1014
1205
 
1015
1206
  /** Walk every page of `listUsageRecords()` for one subscription. */
@@ -1017,7 +1208,7 @@ export class Subscriptions extends BaseResource {
1017
1208
  id: string,
1018
1209
  options: { pageSize?: number; invoice_id?: string } = {},
1019
1210
  ): AsyncIterableIterator<T> {
1020
- 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), {
1021
1212
  pageSize: options.pageSize,
1022
1213
  filters: { invoice_id: options.invoice_id },
1023
1214
  });
@@ -1042,7 +1233,7 @@ export class Subscriptions extends BaseResource {
1042
1233
  * unsettled; while one is open, this period cannot be charged.
1043
1234
  */
1044
1235
  retrieveUsageSummary<T = unknown>(id: string): Promise<T> {
1045
- return this.get<T>(`/v1/subscriptions/${id}/usage_summary`);
1236
+ return this.get<T>(`/v1/subscriptions/${p(id)}/usage_summary`);
1046
1237
  }
1047
1238
  }
1048
1239
 
@@ -1052,7 +1243,7 @@ export class Refunds extends BaseResource {
1052
1243
  }
1053
1244
 
1054
1245
  retrieve<T = unknown>(id: string): Promise<T> {
1055
- return this.get<T>(`/v1/refunds/${id}`);
1246
+ return this.get<T>(`/v1/refunds/${p(id)}`);
1056
1247
  }
1057
1248
 
1058
1249
  list<T = unknown>(params: BaseListParams = {}): Promise<ListResponseEnvelope<T>> {
@@ -1060,7 +1251,7 @@ export class Refunds extends BaseResource {
1060
1251
  }
1061
1252
 
1062
1253
  iter<T = unknown>(options: { pageSize?: number } = {}): AsyncIterableIterator<T> {
1063
- 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 });
1064
1255
  }
1065
1256
  }
1066
1257
 
@@ -1075,25 +1266,41 @@ export class Refunds extends BaseResource {
1075
1266
  */
1076
1267
  export class Disputes extends BaseResource {
1077
1268
  retrieve<T = unknown>(id: string): Promise<T> {
1078
- return this.get<T>(`/v1/disputes/${id}`);
1269
+ return this.get<T>(`/v1/disputes/${p(id)}`);
1079
1270
  }
1080
1271
 
1081
- list<T = unknown>(params: BaseListParams = {}): Promise<ListResponseEnvelope<T>> {
1272
+ list<T = unknown>(params: DisputesListParams = {}): Promise<ListResponseEnvelope<T>> {
1082
1273
  return this.get<ListResponseEnvelope<T>>("/v1/disputes", params);
1083
1274
  }
1084
1275
 
1085
- iter<T = unknown>(options: { pageSize?: number } = {}): AsyncIterableIterator<T> {
1086
- 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
+ });
1087
1283
  }
1088
1284
  }
1089
1285
 
1090
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
+
1091
1298
  create<T = unknown>(params: CreateWebhookEndpointParams): Promise<T> {
1092
1299
  return this.post<T, CreateWebhookEndpointParams>("/v1/webhook_endpoints", params);
1093
1300
  }
1094
1301
 
1095
1302
  retrieve<T = unknown>(id: string): Promise<T> {
1096
- return this.get<T>(`/v1/webhook_endpoints/${id}`);
1303
+ return this.get<T>(`/v1/webhook_endpoints/${p(id)}`);
1097
1304
  }
1098
1305
 
1099
1306
  /**
@@ -1105,7 +1312,7 @@ export class WebhookEndpoints extends BaseResource {
1105
1312
  * disabling is reversible and deleting is not.
1106
1313
  */
1107
1314
  update<T = unknown>(id: string, params: UpdateWebhookEndpointParams): Promise<T> {
1108
- return this.post<T, UpdateWebhookEndpointParams>(`/v1/webhook_endpoints/${id}`, params);
1315
+ return this.post<T, UpdateWebhookEndpointParams>(`/v1/webhook_endpoints/${p(id)}`, params);
1109
1316
  }
1110
1317
 
1111
1318
  /**
@@ -1120,12 +1327,12 @@ export class WebhookEndpoints extends BaseResource {
1120
1327
  * were sent stays on record.
1121
1328
  */
1122
1329
  delete<T = unknown>(id: string, params: IdempotencyOptions = {}): Promise<T> {
1123
- return this.del<T>(`/v1/webhook_endpoints/${id}`, params);
1330
+ return this.del<T>(`/v1/webhook_endpoints/${p(id)}`, params);
1124
1331
  }
1125
1332
 
1126
1333
  /** Rotate the signing secret. The new `bkwhsec_...` is returned once. */
1127
1334
  rotateSecret<T = unknown>(id: string, params: IdempotencyOptions = {}): Promise<T> {
1128
- return this.postEmpty<T>(`/v1/webhook_endpoints/${id}/rotate_secret`, params);
1335
+ return this.postEmpty<T>(`/v1/webhook_endpoints/${p(id)}/rotate_secret`, params);
1129
1336
  }
1130
1337
 
1131
1338
  list<T = unknown>(params: BaseListParams = {}): Promise<ListResponseEnvelope<T>> {
@@ -1133,7 +1340,7 @@ export class WebhookEndpoints extends BaseResource {
1133
1340
  }
1134
1341
 
1135
1342
  iter<T = unknown>(options: { pageSize?: number } = {}): AsyncIterableIterator<T> {
1136
- return paginate<T>((p) => this.get("/v1/webhook_endpoints", p), {
1343
+ return paginate<T>((page) => this.get("/v1/webhook_endpoints", page), {
1137
1344
  pageSize: options.pageSize,
1138
1345
  });
1139
1346
  }
@@ -1150,7 +1357,7 @@ export class WebhookEndpoints extends BaseResource {
1150
1357
  params: BaseListParams = {},
1151
1358
  ): Promise<ListResponseEnvelope<T>> {
1152
1359
  return this.get<ListResponseEnvelope<T>>(
1153
- `/v1/webhook_endpoints/${endpointId}/deliveries`,
1360
+ `/v1/webhook_endpoints/${p(endpointId)}/deliveries`,
1154
1361
  params,
1155
1362
  );
1156
1363
  }
@@ -1161,14 +1368,14 @@ export class WebhookEndpoints extends BaseResource {
1161
1368
  options: { pageSize?: number } = {},
1162
1369
  ): AsyncIterableIterator<T> {
1163
1370
  return paginate<T>(
1164
- (p) => this.get(`/v1/webhook_endpoints/${endpointId}/deliveries`, p),
1371
+ (page) => this.get(`/v1/webhook_endpoints/${p(endpointId)}/deliveries`, page),
1165
1372
  { pageSize: options.pageSize },
1166
1373
  );
1167
1374
  }
1168
1375
 
1169
1376
  /** Fetch one delivery row for inspection before deciding to redeliver. */
1170
1377
  retrieveDelivery<T = unknown>(endpointId: string, deliveryId: string): Promise<T> {
1171
- return this.get<T>(`/v1/webhook_endpoints/${endpointId}/deliveries/${deliveryId}`);
1378
+ return this.get<T>(`/v1/webhook_endpoints/${p(endpointId)}/deliveries/${p(deliveryId)}`);
1172
1379
  }
1173
1380
 
1174
1381
  /**
@@ -1199,7 +1406,7 @@ export class WebhookEndpoints extends BaseResource {
1199
1406
  params: IdempotencyOptions = {},
1200
1407
  ): Promise<T> {
1201
1408
  return this.postEmpty<T>(
1202
- `/v1/webhook_endpoints/${endpointId}/deliveries/${deliveryId}/redeliver`,
1409
+ `/v1/webhook_endpoints/${p(endpointId)}/deliveries/${p(deliveryId)}/redeliver`,
1203
1410
  params,
1204
1411
  );
1205
1412
  }
@@ -1207,7 +1414,7 @@ export class WebhookEndpoints extends BaseResource {
1207
1414
 
1208
1415
  export class Events extends BaseResource {
1209
1416
  retrieve<T = unknown>(id: string): Promise<T> {
1210
- return this.get<T>(`/v1/events/${id}`);
1417
+ return this.get<T>(`/v1/events/${p(id)}`);
1211
1418
  }
1212
1419
 
1213
1420
  list<T = unknown>(params: EventsListParams = {}): Promise<ListResponseEnvelope<T>> {
@@ -1218,7 +1425,7 @@ export class Events extends BaseResource {
1218
1425
  iter<T = unknown>(
1219
1426
  options: { pageSize?: number; type?: string } = {},
1220
1427
  ): AsyncIterableIterator<T> {
1221
- return paginate<T>((p) => this.get("/v1/events", p), {
1428
+ return paginate<T>((page) => this.get("/v1/events", page), {
1222
1429
  pageSize: options.pageSize,
1223
1430
  filters: { type: options.type },
1224
1431
  });
@@ -1238,6 +1445,49 @@ export class Tenant extends BaseResource {
1238
1445
  return this.get<T>("/v1/tenant/capabilities");
1239
1446
  }
1240
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
+
1241
1491
  /** Current portal branding row (business name, theme, capability flags). */
1242
1492
  portalBranding<T = unknown>(): Promise<T> {
1243
1493
  return this.get<T>("/v1/tenant/portal_branding");
@@ -1275,7 +1525,7 @@ export class Coupons extends BaseResource {
1275
1525
  }
1276
1526
 
1277
1527
  retrieve<T = unknown>(id: string): Promise<T> {
1278
- return this.get<T>(`/v1/coupons/${id}`);
1528
+ return this.get<T>(`/v1/coupons/${p(id)}`);
1279
1529
  }
1280
1530
 
1281
1531
  /**
@@ -1287,7 +1537,7 @@ export class Coupons extends BaseResource {
1287
1537
  * customer was charged. `active: true` brings the campaign back.
1288
1538
  */
1289
1539
  update<T = unknown>(id: string, params: UpdateCouponParams): Promise<T> {
1290
- return this.post<T, UpdateCouponParams>(`/v1/coupons/${id}`, params);
1540
+ return this.post<T, UpdateCouponParams>(`/v1/coupons/${p(id)}`, params);
1291
1541
  }
1292
1542
 
1293
1543
  /**
@@ -1311,7 +1561,7 @@ export class Coupons extends BaseResource {
1311
1561
  }
1312
1562
 
1313
1563
  iter<T = unknown>(options: { pageSize?: number } = {}): AsyncIterableIterator<T> {
1314
- 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 });
1315
1565
  }
1316
1566
  }
1317
1567
 
@@ -1321,7 +1571,7 @@ export class TaxRates extends BaseResource {
1321
1571
  }
1322
1572
 
1323
1573
  retrieve<T = unknown>(id: string): Promise<T> {
1324
- return this.get<T>(`/v1/tax_rates/${id}`);
1574
+ return this.get<T>(`/v1/tax_rates/${p(id)}`);
1325
1575
  }
1326
1576
 
1327
1577
  /**
@@ -1333,7 +1583,7 @@ export class TaxRates extends BaseResource {
1333
1583
  * is no `delete()`.
1334
1584
  */
1335
1585
  update<T = unknown>(id: string, params: UpdateTaxRateParams): Promise<T> {
1336
- return this.post<T, UpdateTaxRateParams>(`/v1/tax_rates/${id}`, params);
1586
+ return this.post<T, UpdateTaxRateParams>(`/v1/tax_rates/${p(id)}`, params);
1337
1587
  }
1338
1588
 
1339
1589
  list<T = unknown>(params: BaseListParams = {}): Promise<ListResponseEnvelope<T>> {
@@ -1341,7 +1591,7 @@ export class TaxRates extends BaseResource {
1341
1591
  }
1342
1592
 
1343
1593
  iter<T = unknown>(options: { pageSize?: number } = {}): AsyncIterableIterator<T> {
1344
- 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 });
1345
1595
  }
1346
1596
  }
1347
1597
 
@@ -1353,8 +1603,9 @@ export class TaxRates extends BaseResource {
1353
1603
  * {@link Invoices.retrievePdf}.
1354
1604
  */
1355
1605
  export class Invoices extends BaseResource {
1356
- retrieve<T = unknown>(id: string): Promise<T> {
1357
- 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);
1358
1609
  }
1359
1610
 
1360
1611
  /**
@@ -1376,15 +1627,38 @@ export class Invoices extends BaseResource {
1376
1627
  * structured invoice for tenants who render their own.
1377
1628
  */
1378
1629
  retrievePdf(id: string): Promise<ArrayBuffer> {
1379
- return this.t.requestBinary({ method: "GET", path: `/v1/invoices/${id}/pdf` });
1630
+ return this.t.requestBinary({ method: "GET", path: `/v1/invoices/${p(id)}/pdf` });
1380
1631
  }
1381
1632
 
1382
- 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>> {
1383
1648
  return this.get<ListResponseEnvelope<T>>("/v1/invoices", params);
1384
1649
  }
1385
1650
 
1386
- iter<T = unknown>(options: { pageSize?: number } = {}): AsyncIterableIterator<T> {
1387
- 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 });
1388
1662
  }
1389
1663
 
1390
1664
  /**
@@ -1403,7 +1677,7 @@ export class Invoices extends BaseResource {
1403
1677
  * Idempotent: re-voiding an already-void invoice returns it unchanged.
1404
1678
  */
1405
1679
  void<T = unknown>(id: string, params: VoidInvoiceParams = {}): Promise<T> {
1406
- return this.post<T, VoidInvoiceParams>(`/v1/invoices/${id}/void`, params);
1680
+ return this.post<T, VoidInvoiceParams>(`/v1/invoices/${p(id)}/void`, params);
1407
1681
  }
1408
1682
  }
1409
1683
 
@@ -1419,7 +1693,7 @@ export class Invoices extends BaseResource {
1419
1693
  */
1420
1694
  export class CreditNotes extends BaseResource {
1421
1695
  retrieve<T = unknown>(id: string): Promise<T> {
1422
- return this.get<T>(`/v1/credit_notes/${id}`);
1696
+ return this.get<T>(`/v1/credit_notes/${p(id)}`);
1423
1697
  }
1424
1698
 
1425
1699
  /**
@@ -1428,7 +1702,7 @@ export class CreditNotes extends BaseResource {
1428
1702
  * `302`, and `501 rendering_pending` on a deployment with no renderer.
1429
1703
  */
1430
1704
  retrievePdf(id: string): Promise<ArrayBuffer> {
1431
- 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` });
1432
1706
  }
1433
1707
 
1434
1708
  list<T = unknown>(params: CreditNotesListParams = {}): Promise<ListResponseEnvelope<T>> {
@@ -1438,7 +1712,7 @@ export class CreditNotes extends BaseResource {
1438
1712
  iter<T = unknown>(
1439
1713
  options: { pageSize?: number; invoice_id?: string; customer_id?: string } = {},
1440
1714
  ): AsyncIterableIterator<T> {
1441
- return paginate<T>((p) => this.get("/v1/credit_notes", p), {
1715
+ return paginate<T>((page) => this.get("/v1/credit_notes", page), {
1442
1716
  pageSize: options.pageSize,
1443
1717
  filters: { invoice_id: options.invoice_id, customer_id: options.customer_id },
1444
1718
  });
@@ -1454,7 +1728,7 @@ export class CreditNotes extends BaseResource {
1454
1728
  */
1455
1729
  export class AuditLogs extends BaseResource {
1456
1730
  retrieve<T = unknown>(id: string): Promise<T> {
1457
- return this.get<T>(`/v1/audit_logs/${id}`);
1731
+ return this.get<T>(`/v1/audit_logs/${p(id)}`);
1458
1732
  }
1459
1733
 
1460
1734
  list<T = unknown>(params: AuditLogsListParams = {}): Promise<ListResponseEnvelope<T>> {
@@ -1462,13 +1736,20 @@ export class AuditLogs extends BaseResource {
1462
1736
  }
1463
1737
 
1464
1738
  iter<T = unknown>(
1465
- options: { pageSize?: number; action?: string; resource_type?: string; actor_id?: string } = {},
1739
+ options: {
1740
+ pageSize?: number;
1741
+ action?: string;
1742
+ resource_type?: string;
1743
+ resource_id?: string;
1744
+ actor_id?: string;
1745
+ } = {},
1466
1746
  ): AsyncIterableIterator<T> {
1467
- return paginate<T>((p) => this.get("/v1/audit_logs", p), {
1747
+ return paginate<T>((page) => this.get("/v1/audit_logs", page), {
1468
1748
  pageSize: options.pageSize,
1469
1749
  filters: {
1470
1750
  action: options.action,
1471
1751
  resource_type: options.resource_type,
1752
+ resource_id: options.resource_id,
1472
1753
  actor_id: options.actor_id,
1473
1754
  },
1474
1755
  });
@@ -1483,16 +1764,36 @@ export class AuditLogs extends BaseResource {
1483
1764
  * refunds and disputes are separate flows.
1484
1765
  */
1485
1766
  export class Payments extends BaseResource {
1486
- retrieve<T = unknown>(id: string): Promise<T> {
1487
- 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);
1488
1770
  }
1489
1771
 
1490
- 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>> {
1491
1787
  return this.get<ListResponseEnvelope<T>>("/v1/payments", params);
1492
1788
  }
1493
1789
 
1494
- iter<T = unknown>(options: { pageSize?: number } = {}): AsyncIterableIterator<T> {
1495
- 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
+ });
1496
1797
  }
1497
1798
  }
1498
1799
 
@@ -1506,18 +1807,56 @@ export class Payments extends BaseResource {
1506
1807
  */
1507
1808
  export class BillingPortalSessions extends BaseResource {
1508
1809
  create<T = unknown>(params: CreateBillingPortalSessionParams): Promise<T> {
1509
- return this.postFixed<T>(
1510
- "/v1/billing_portal/sessions",
1511
- {
1512
- subscription_id: params.subscription_id,
1513
- return_url: params.return_url,
1514
- },
1515
- { idempotencyKey: params.idempotencyKey },
1516
- );
1810
+ return this.post<T, CreateBillingPortalSessionParams>("/v1/billing_portal/sessions", params);
1517
1811
  }
1518
1812
 
1519
1813
  /** Kill an in-the-wild portal session. Idempotent. */
1520
1814
  revoke<T = unknown>(id: string, params: IdempotencyOptions = {}): Promise<T> {
1521
- 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 });
1522
1861
  }
1523
1862
  }