@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/dist/index.js CHANGED
@@ -24,6 +24,9 @@ async function* paginate(listFn, options = {}) {
24
24
  }
25
25
 
26
26
  // src/resources.ts
27
+ function p(id) {
28
+ return encodeURIComponent(id);
29
+ }
27
30
  function dropUndefined(obj) {
28
31
  const out = {};
29
32
  for (const [k, v] of Object.entries(obj)) {
@@ -56,6 +59,12 @@ var BaseResource = class {
56
59
  this.t = t;
57
60
  }
58
61
  t;
62
+ /**
63
+ * `query` is a plain object rather than an index-signature type: TypeScript
64
+ * only gives an implicit index signature to type *aliases*, so a closed
65
+ * `*ListParams` interface would otherwise need a cast at every call site.
66
+ * The transport prunes `undefined`/`null` and joins arrays with commas.
67
+ */
59
68
  get(path, query) {
60
69
  return this.t.request({ method: "GET", path, query });
61
70
  }
@@ -96,10 +105,10 @@ var Customers = class extends BaseResource {
96
105
  return this.post("/v1/customers", params);
97
106
  }
98
107
  retrieve(id) {
99
- return this.get(`/v1/customers/${id}`);
108
+ return this.get(`/v1/customers/${p(id)}`);
100
109
  }
101
110
  update(id, params = {}) {
102
- return this.post(`/v1/customers/${id}`, params);
111
+ return this.post(`/v1/customers/${p(id)}`, params);
103
112
  }
104
113
  /**
105
114
  * Delete a customer. Resolves to `{ id, object: "customer", deleted:
@@ -112,7 +121,7 @@ var Customers = class extends BaseResource {
112
121
  * charge them.
113
122
  */
114
123
  delete(id, params = {}) {
115
- return this.del(`/v1/customers/${id}`, params);
124
+ return this.del(`/v1/customers/${p(id)}`, params);
116
125
  }
117
126
  /**
118
127
  * List customers, newest first.
@@ -129,7 +138,7 @@ var Customers = class extends BaseResource {
129
138
  }
130
139
  /** Walk every page of `list()` and yield each customer. */
131
140
  iter(options = {}) {
132
- return paginate((p) => this.get("/v1/customers", p), { pageSize: options.pageSize });
141
+ return paginate((page) => this.get("/v1/customers", page), { pageSize: options.pageSize });
133
142
  }
134
143
  /**
135
144
  * Attach or replace the customer's VAT number; triggers server-side
@@ -137,7 +146,7 @@ var Customers = class extends BaseResource {
137
146
  * reflecting whether VIES confirmed the number.
138
147
  */
139
148
  setVatNumber(id, params) {
140
- return this.post(`/v1/customers/${id}/vat_number`, params);
149
+ return this.post(`/v1/customers/${p(id)}/vat_number`, params);
141
150
  }
142
151
  /**
143
152
  * Hard-purge a customer's PII for GDPR erasure. Distinct from
@@ -149,7 +158,7 @@ var Customers = class extends BaseResource {
149
158
  */
150
159
  purge(id, params = {}) {
151
160
  const { confirmed = true, idempotencyKey } = params;
152
- return this.postFixed(`/v1/customers/${id}/purge`, { confirmed }, { idempotencyKey });
161
+ return this.postFixed(`/v1/customers/${p(id)}/purge`, { confirmed }, { idempotencyKey });
153
162
  }
154
163
  };
155
164
  var Products = class extends BaseResource {
@@ -157,8 +166,9 @@ var Products = class extends BaseResource {
157
166
  create(params) {
158
167
  return this.post("/v1/products", params);
159
168
  }
160
- retrieve(id) {
161
- return this.get(`/v1/products/${id}`);
169
+ /** Expandable: `prices` (every price on the product), `stats`. */
170
+ retrieve(id, options = {}) {
171
+ return this.get(`/v1/products/${p(id)}`, options);
162
172
  }
163
173
  /**
164
174
  * Patch mutable Product fields, or archive it with `active: false`.
@@ -170,13 +180,13 @@ var Products = class extends BaseResource {
170
180
  * `active: true` un-archives.
171
181
  */
172
182
  update(id, params) {
173
- return this.post(`/v1/products/${id}`, params);
183
+ return this.post(`/v1/products/${p(id)}`, params);
174
184
  }
175
185
  list(params = {}) {
176
186
  return this.get("/v1/products", params);
177
187
  }
178
188
  iter(options = {}) {
179
- return paginate((p) => this.get("/v1/products", p), { pageSize: options.pageSize });
189
+ return paginate((page) => this.get("/v1/products", page), { pageSize: options.pageSize });
180
190
  }
181
191
  };
182
192
  var Prices = class extends BaseResource {
@@ -199,7 +209,7 @@ var Prices = class extends BaseResource {
199
209
  return this.post("/v1/prices", assertPriceRatesAreStrings(params));
200
210
  }
201
211
  retrieve(id) {
202
- return this.get(`/v1/prices/${id}`);
212
+ return this.get(`/v1/prices/${p(id)}`);
203
213
  }
204
214
  /**
205
215
  * Archive a Price so it stops selling, or put it back on sale.
@@ -222,14 +232,14 @@ var Prices = class extends BaseResource {
222
232
  * `price.archived`; putting one back emits `price.updated`.
223
233
  */
224
234
  update(id, params) {
225
- return this.post(`/v1/prices/${id}`, params);
235
+ return this.post(`/v1/prices/${p(id)}`, params);
226
236
  }
227
237
  list(params = {}) {
228
238
  return this.get("/v1/prices", params);
229
239
  }
230
240
  iter(options = {}) {
231
241
  const filter = options.product_id === void 0 ? {} : { product_id: options.product_id };
232
- return paginate((p) => this.get("/v1/prices", { ...filter, ...p }), {
242
+ return paginate((page) => this.get("/v1/prices", { ...filter, ...page }), {
233
243
  pageSize: options.pageSize
234
244
  });
235
245
  }
@@ -239,7 +249,7 @@ var CheckoutSessions = class extends BaseResource {
239
249
  return this.post("/v1/checkout/sessions", params);
240
250
  }
241
251
  retrieve(id) {
242
- return this.get(`/v1/checkout/sessions/${id}`);
252
+ return this.get(`/v1/checkout/sessions/${p(id)}`);
243
253
  }
244
254
  };
245
255
  var OneShotPayments = class extends BaseResource {
@@ -248,12 +258,13 @@ var OneShotPayments = class extends BaseResource {
248
258
  return this.post("/v1/checkout/one_shot", params);
249
259
  }
250
260
  retrieve(id) {
251
- return this.get(`/v1/checkout/one_shot/${id}`);
261
+ return this.get(`/v1/checkout/one_shot/${p(id)}`);
252
262
  }
253
263
  };
254
264
  var Subscriptions = class extends BaseResource {
255
- retrieve(id) {
256
- return this.get(`/v1/subscriptions/${id}`);
265
+ /** Expandable: `customer`, `price`, `refund_eligibility`. */
266
+ retrieve(id, options = {}) {
267
+ return this.get(`/v1/subscriptions/${p(id)}`, options);
257
268
  }
258
269
  /**
259
270
  * List subscriptions, newest first, optionally filtered.
@@ -271,16 +282,16 @@ var Subscriptions = class extends BaseResource {
271
282
  */
272
283
  iter(options = {}) {
273
284
  const { pageSize, ...filter } = options;
274
- return paginate((p) => this.get("/v1/subscriptions", { ...filter, ...p }), { pageSize });
285
+ return paginate((page) => this.get("/v1/subscriptions", { ...filter, ...page }), { pageSize });
275
286
  }
276
287
  cancel(id, params = {}) {
277
- return this.postEmpty(`/v1/subscriptions/${id}/cancel`, params);
288
+ return this.postEmpty(`/v1/subscriptions/${p(id)}/cancel`, params);
278
289
  }
279
290
  pause(id, params = {}) {
280
- return this.postEmpty(`/v1/subscriptions/${id}/pause`, params);
291
+ return this.postEmpty(`/v1/subscriptions/${p(id)}/pause`, params);
281
292
  }
282
293
  resume(id, params = {}) {
283
- return this.postEmpty(`/v1/subscriptions/${id}/resume`, params);
294
+ return this.postEmpty(`/v1/subscriptions/${p(id)}/resume`, params);
284
295
  }
285
296
  /**
286
297
  * Reactivate a canceled-but-still-in-period subscription.
@@ -291,23 +302,23 @@ var Subscriptions = class extends BaseResource {
291
302
  * Returns `409` if the period has already elapsed.
292
303
  */
293
304
  reactivate(id, params = {}) {
294
- return this.postEmpty(`/v1/subscriptions/${id}/reactivate`, params);
305
+ return this.postEmpty(`/v1/subscriptions/${p(id)}/reactivate`, params);
295
306
  }
296
307
  previewUpdate(id, params) {
297
- return this.postFixed(`/v1/subscriptions/${id}/preview_update`, {
308
+ return this.postFixed(`/v1/subscriptions/${p(id)}/preview_update`, {
298
309
  target_price_id: params.target_price_id
299
310
  });
300
311
  }
301
312
  update(id, params) {
302
313
  return this.postFixed(
303
- `/v1/subscriptions/${id}/update`,
314
+ `/v1/subscriptions/${p(id)}/update`,
304
315
  { target_price_id: params.target_price_id },
305
316
  { idempotencyKey: params.idempotencyKey }
306
317
  );
307
318
  }
308
319
  reauthorizePaymentMethod(id, params) {
309
320
  return this.postFixed(
310
- `/v1/subscriptions/${id}/reauthorize_payment_method`,
321
+ `/v1/subscriptions/${p(id)}/reauthorize_payment_method`,
311
322
  { return_url: params.return_url },
312
323
  { idempotencyKey: params.idempotencyKey }
313
324
  );
@@ -329,7 +340,7 @@ var Subscriptions = class extends BaseResource {
329
340
  * See {@link CreateUsageRecordParams.identifier}.
330
341
  */
331
342
  createUsageRecord(id, params) {
332
- return this.post(`/v1/subscriptions/${id}/usage_records`, params);
343
+ return this.post(`/v1/subscriptions/${p(id)}/usage_records`, params);
333
344
  }
334
345
  /**
335
346
  * List usage records for one subscription.
@@ -339,11 +350,11 @@ var Subscriptions = class extends BaseResource {
339
350
  * invoice charged for.
340
351
  */
341
352
  listUsageRecords(id, params = {}) {
342
- return this.get(`/v1/subscriptions/${id}/usage_records`, params);
353
+ return this.get(`/v1/subscriptions/${p(id)}/usage_records`, params);
343
354
  }
344
355
  /** Walk every page of `listUsageRecords()` for one subscription. */
345
356
  iterUsageRecords(id, options = {}) {
346
- return paginate((p) => this.get(`/v1/subscriptions/${id}/usage_records`, p), {
357
+ return paginate((page) => this.get(`/v1/subscriptions/${p(id)}/usage_records`, page), {
347
358
  pageSize: options.pageSize,
348
359
  filters: { invoice_id: options.invoice_id }
349
360
  });
@@ -367,7 +378,7 @@ var Subscriptions = class extends BaseResource {
367
378
  * unsettled; while one is open, this period cannot be charged.
368
379
  */
369
380
  retrieveUsageSummary(id) {
370
- return this.get(`/v1/subscriptions/${id}/usage_summary`);
381
+ return this.get(`/v1/subscriptions/${p(id)}/usage_summary`);
371
382
  }
372
383
  };
373
384
  var Refunds = class extends BaseResource {
@@ -375,32 +386,45 @@ var Refunds = class extends BaseResource {
375
386
  return this.post("/v1/refunds", params);
376
387
  }
377
388
  retrieve(id) {
378
- return this.get(`/v1/refunds/${id}`);
389
+ return this.get(`/v1/refunds/${p(id)}`);
379
390
  }
380
391
  list(params = {}) {
381
392
  return this.get("/v1/refunds", params);
382
393
  }
383
394
  iter(options = {}) {
384
- return paginate((p) => this.get("/v1/refunds", p), { pageSize: options.pageSize });
395
+ return paginate((page) => this.get("/v1/refunds", page), { pageSize: options.pageSize });
385
396
  }
386
397
  };
387
398
  var Disputes = class extends BaseResource {
388
399
  retrieve(id) {
389
- return this.get(`/v1/disputes/${id}`);
400
+ return this.get(`/v1/disputes/${p(id)}`);
390
401
  }
391
402
  list(params = {}) {
392
403
  return this.get("/v1/disputes", params);
393
404
  }
394
405
  iter(options = {}) {
395
- return paginate((p) => this.get("/v1/disputes", p), { pageSize: options.pageSize });
406
+ return paginate((page) => this.get("/v1/disputes", page), {
407
+ pageSize: options.pageSize,
408
+ filters: { status: options.status, payment_id: options.payment_id }
409
+ });
396
410
  }
397
411
  };
398
412
  var WebhookEndpoints = class extends BaseResource {
413
+ /**
414
+ * Every event type this deployment can deliver, plus the wildcard.
415
+ *
416
+ * `enabled_events` rejects anything not on this list, so read it rather
417
+ * than hard-coding a set: a name that is not on it fails at
418
+ * registration and leaves you with an endpoint that never fires.
419
+ */
420
+ listEventTypes() {
421
+ return this.get("/v1/webhook_endpoints/event_types");
422
+ }
399
423
  create(params) {
400
424
  return this.post("/v1/webhook_endpoints", params);
401
425
  }
402
426
  retrieve(id) {
403
- return this.get(`/v1/webhook_endpoints/${id}`);
427
+ return this.get(`/v1/webhook_endpoints/${p(id)}`);
404
428
  }
405
429
  /**
406
430
  * Update an endpoint, or stop delivery with `status: "disabled"`.
@@ -411,7 +435,7 @@ var WebhookEndpoints = class extends BaseResource {
411
435
  * disabling is reversible and deleting is not.
412
436
  */
413
437
  update(id, params) {
414
- return this.post(`/v1/webhook_endpoints/${id}`, params);
438
+ return this.post(`/v1/webhook_endpoints/${p(id)}`, params);
415
439
  }
416
440
  /**
417
441
  * Delete an endpoint. Resolves to `{ id, object: "webhook_endpoint",
@@ -425,17 +449,17 @@ var WebhookEndpoints = class extends BaseResource {
425
449
  * were sent stays on record.
426
450
  */
427
451
  delete(id, params = {}) {
428
- return this.del(`/v1/webhook_endpoints/${id}`, params);
452
+ return this.del(`/v1/webhook_endpoints/${p(id)}`, params);
429
453
  }
430
454
  /** Rotate the signing secret. The new `bkwhsec_...` is returned once. */
431
455
  rotateSecret(id, params = {}) {
432
- return this.postEmpty(`/v1/webhook_endpoints/${id}/rotate_secret`, params);
456
+ return this.postEmpty(`/v1/webhook_endpoints/${p(id)}/rotate_secret`, params);
433
457
  }
434
458
  list(params = {}) {
435
459
  return this.get("/v1/webhook_endpoints", params);
436
460
  }
437
461
  iter(options = {}) {
438
- return paginate((p) => this.get("/v1/webhook_endpoints", p), {
462
+ return paginate((page) => this.get("/v1/webhook_endpoints", page), {
439
463
  pageSize: options.pageSize
440
464
  });
441
465
  }
@@ -448,20 +472,20 @@ var WebhookEndpoints = class extends BaseResource {
448
472
  */
449
473
  listDeliveries(endpointId, params = {}) {
450
474
  return this.get(
451
- `/v1/webhook_endpoints/${endpointId}/deliveries`,
475
+ `/v1/webhook_endpoints/${p(endpointId)}/deliveries`,
452
476
  params
453
477
  );
454
478
  }
455
479
  /** Walk every page of `listDeliveries()` for one endpoint. */
456
480
  iterDeliveries(endpointId, options = {}) {
457
481
  return paginate(
458
- (p) => this.get(`/v1/webhook_endpoints/${endpointId}/deliveries`, p),
482
+ (page) => this.get(`/v1/webhook_endpoints/${p(endpointId)}/deliveries`, page),
459
483
  { pageSize: options.pageSize }
460
484
  );
461
485
  }
462
486
  /** Fetch one delivery row for inspection before deciding to redeliver. */
463
487
  retrieveDelivery(endpointId, deliveryId) {
464
- return this.get(`/v1/webhook_endpoints/${endpointId}/deliveries/${deliveryId}`);
488
+ return this.get(`/v1/webhook_endpoints/${p(endpointId)}/deliveries/${p(deliveryId)}`);
465
489
  }
466
490
  /**
467
491
  * @deprecated Renamed to {@link WebhookEndpoints.retrieveDelivery}.
@@ -486,21 +510,21 @@ var WebhookEndpoints = class extends BaseResource {
486
510
  */
487
511
  redeliver(endpointId, deliveryId, params = {}) {
488
512
  return this.postEmpty(
489
- `/v1/webhook_endpoints/${endpointId}/deliveries/${deliveryId}/redeliver`,
513
+ `/v1/webhook_endpoints/${p(endpointId)}/deliveries/${p(deliveryId)}/redeliver`,
490
514
  params
491
515
  );
492
516
  }
493
517
  };
494
518
  var Events = class extends BaseResource {
495
519
  retrieve(id) {
496
- return this.get(`/v1/events/${id}`);
520
+ return this.get(`/v1/events/${p(id)}`);
497
521
  }
498
522
  list(params = {}) {
499
523
  return this.get("/v1/events", params);
500
524
  }
501
525
  /** Walk every page of `list()`. Pass `type` to filter at the server. */
502
526
  iter(options = {}) {
503
- return paginate((p) => this.get("/v1/events", p), {
527
+ return paginate((page) => this.get("/v1/events", page), {
504
528
  pageSize: options.pageSize,
505
529
  filters: { type: options.type }
506
530
  });
@@ -511,6 +535,46 @@ var Tenant = class extends BaseResource {
511
535
  capabilities() {
512
536
  return this.get("/v1/tenant/capabilities");
513
537
  }
538
+ /**
539
+ * Your registered country and VAT number: what your customers' VAT is
540
+ * decided against.
541
+ *
542
+ * `country_code` is what you have stored and can be `null`;
543
+ * `effective_country_code` is what the next charge will really use.
544
+ * The two differ only when you have stored nothing, which is exactly
545
+ * the case worth spotting before a first live payment.
546
+ */
547
+ billingProfile() {
548
+ return this.get("/v1/tenant/billing_profile");
549
+ }
550
+ /**
551
+ * Set the seller identity. `country_code` is required on every call;
552
+ * every other field is partial-update, with an explicit `null` to
553
+ * clear. Changes take effect on the next charge only. Tax is written
554
+ * onto a payment and its invoice before money moves, and nothing goes
555
+ * back and recalculates it.
556
+ */
557
+ setBillingProfile(params) {
558
+ return this.post("/v1/tenant/billing_profile", params);
559
+ }
560
+ /**
561
+ * Download everything in the account as one JSON document, as raw bytes.
562
+ *
563
+ * ```ts
564
+ * await writeFile("export.json", Buffer.from(await client.tenant.export()));
565
+ * ```
566
+ *
567
+ * The GDPR Article 20 portability route, and the way to take a backup.
568
+ * It is `application/json` streamed inline, with no redirect, and each
569
+ * record has the same shape its `GET` route returns, with
570
+ * `billkit_export_version` naming the shape. It can be large, so write
571
+ * it to a file rather than holding it in memory. Test and live data
572
+ * export separately: you get whichever mode the key belongs to. The
573
+ * access is recorded in your audit log.
574
+ */
575
+ export() {
576
+ return this.t.requestBinary({ method: "GET", path: "/v1/tenant/export" });
577
+ }
514
578
  /** Current portal branding row (business name, theme, capability flags). */
515
579
  portalBranding() {
516
580
  return this.get("/v1/tenant/portal_branding");
@@ -544,7 +608,7 @@ var Coupons = class extends BaseResource {
544
608
  return this.post("/v1/coupons", params);
545
609
  }
546
610
  retrieve(id) {
547
- return this.get(`/v1/coupons/${id}`);
611
+ return this.get(`/v1/coupons/${p(id)}`);
548
612
  }
549
613
  /**
550
614
  * Update a coupon's limits, or withdraw it with `active: false`.
@@ -555,7 +619,7 @@ var Coupons = class extends BaseResource {
555
619
  * customer was charged. `active: true` brings the campaign back.
556
620
  */
557
621
  update(id, params) {
558
- return this.post(`/v1/coupons/${id}`, params);
622
+ return this.post(`/v1/coupons/${p(id)}`, params);
559
623
  }
560
624
  /**
561
625
  * Server-side dry-run of a coupon redemption.
@@ -573,7 +637,7 @@ var Coupons = class extends BaseResource {
573
637
  return this.get("/v1/coupons", params);
574
638
  }
575
639
  iter(options = {}) {
576
- return paginate((p) => this.get("/v1/coupons", p), { pageSize: options.pageSize });
640
+ return paginate((page) => this.get("/v1/coupons", page), { pageSize: options.pageSize });
577
641
  }
578
642
  };
579
643
  var TaxRates = class extends BaseResource {
@@ -581,7 +645,7 @@ var TaxRates = class extends BaseResource {
581
645
  return this.post("/v1/tax_rates", params);
582
646
  }
583
647
  retrieve(id) {
584
- return this.get(`/v1/tax_rates/${id}`);
648
+ return this.get(`/v1/tax_rates/${p(id)}`);
585
649
  }
586
650
  /**
587
651
  * Correct a rate, retire it with `active: false`, or bring one back.
@@ -592,18 +656,19 @@ var TaxRates = class extends BaseResource {
592
656
  * is no `delete()`.
593
657
  */
594
658
  update(id, params) {
595
- return this.post(`/v1/tax_rates/${id}`, params);
659
+ return this.post(`/v1/tax_rates/${p(id)}`, params);
596
660
  }
597
661
  list(params = {}) {
598
662
  return this.get("/v1/tax_rates", params);
599
663
  }
600
664
  iter(options = {}) {
601
- return paginate((p) => this.get("/v1/tax_rates", p), { pageSize: options.pageSize });
665
+ return paginate((page) => this.get("/v1/tax_rates", page), { pageSize: options.pageSize });
602
666
  }
603
667
  };
604
668
  var Invoices = class extends BaseResource {
605
- retrieve(id) {
606
- return this.get(`/v1/invoices/${id}`);
669
+ /** Expandable: `customer`. */
670
+ retrieve(id, options = {}) {
671
+ return this.get(`/v1/invoices/${p(id)}`, options);
607
672
  }
608
673
  /**
609
674
  * Download the rendered invoice PDF as raw bytes.
@@ -624,13 +689,27 @@ var Invoices = class extends BaseResource {
624
689
  * structured invoice for tenants who render their own.
625
690
  */
626
691
  retrievePdf(id) {
627
- return this.t.requestBinary({ method: "GET", path: `/v1/invoices/${id}/pdf` });
692
+ return this.t.requestBinary({ method: "GET", path: `/v1/invoices/${p(id)}/pdf` });
693
+ }
694
+ /**
695
+ * Send the customer their invoice again.
696
+ *
697
+ * The same tenant-branded "your invoice is ready" email, with a fresh
698
+ * portal link, because the one in the original may have expired. It
699
+ * goes to the address captured **on the invoice**, not the customer's
700
+ * current one: this is a copy of a document that was issued to
701
+ * somebody. An invoice with no address on file is a
702
+ * `InvalidRequestError` rather than a send that did not happen.
703
+ */
704
+ sendEmail(id, params = {}) {
705
+ return this.postEmpty(`/v1/invoices/${p(id)}/email`, params);
628
706
  }
629
707
  list(params = {}) {
630
708
  return this.get("/v1/invoices", params);
631
709
  }
632
710
  iter(options = {}) {
633
- return paginate((p) => this.get("/v1/invoices", p), { pageSize: options.pageSize });
711
+ const { pageSize, ...filters } = options;
712
+ return paginate((page) => this.get("/v1/invoices", page), { pageSize, filters });
634
713
  }
635
714
  /**
636
715
  * Void an invoice: state that the sale was never owed.
@@ -648,12 +727,12 @@ var Invoices = class extends BaseResource {
648
727
  * Idempotent: re-voiding an already-void invoice returns it unchanged.
649
728
  */
650
729
  void(id, params = {}) {
651
- return this.post(`/v1/invoices/${id}/void`, params);
730
+ return this.post(`/v1/invoices/${p(id)}/void`, params);
652
731
  }
653
732
  };
654
733
  var CreditNotes = class extends BaseResource {
655
734
  retrieve(id) {
656
- return this.get(`/v1/credit_notes/${id}`);
735
+ return this.get(`/v1/credit_notes/${p(id)}`);
657
736
  }
658
737
  /**
659
738
  * Download the rendered credit note PDF as raw bytes. Same storage
@@ -661,13 +740,13 @@ var CreditNotes = class extends BaseResource {
661
740
  * `302`, and `501 rendering_pending` on a deployment with no renderer.
662
741
  */
663
742
  retrievePdf(id) {
664
- return this.t.requestBinary({ method: "GET", path: `/v1/credit_notes/${id}/pdf` });
743
+ return this.t.requestBinary({ method: "GET", path: `/v1/credit_notes/${p(id)}/pdf` });
665
744
  }
666
745
  list(params = {}) {
667
746
  return this.get("/v1/credit_notes", params);
668
747
  }
669
748
  iter(options = {}) {
670
- return paginate((p) => this.get("/v1/credit_notes", p), {
749
+ return paginate((page) => this.get("/v1/credit_notes", page), {
671
750
  pageSize: options.pageSize,
672
751
  filters: { invoice_id: options.invoice_id, customer_id: options.customer_id }
673
752
  });
@@ -675,13 +754,13 @@ var CreditNotes = class extends BaseResource {
675
754
  };
676
755
  var AuditLogs = class extends BaseResource {
677
756
  retrieve(id) {
678
- return this.get(`/v1/audit_logs/${id}`);
757
+ return this.get(`/v1/audit_logs/${p(id)}`);
679
758
  }
680
759
  list(params = {}) {
681
760
  return this.get("/v1/audit_logs", params);
682
761
  }
683
762
  iter(options = {}) {
684
- return paginate((p) => this.get("/v1/audit_logs", p), {
763
+ return paginate((page) => this.get("/v1/audit_logs", page), {
685
764
  pageSize: options.pageSize,
686
765
  filters: {
687
766
  action: options.action,
@@ -693,30 +772,72 @@ var AuditLogs = class extends BaseResource {
693
772
  }
694
773
  };
695
774
  var Payments = class extends BaseResource {
696
- retrieve(id) {
697
- return this.get(`/v1/payments/${id}`);
775
+ /** Expandable: `customer`, `subscription`. */
776
+ retrieve(id, options = {}) {
777
+ return this.get(`/v1/payments/${p(id)}`, options);
778
+ }
779
+ /**
780
+ * Fetch the provider's own record of this charge, live.
781
+ *
782
+ * Reads Mollie at request time rather than a stored copy, so it carries
783
+ * what BillKit deliberately does not keep: the card BIN, the iDEAL
784
+ * bank, the provider's own status string. Reading live means it can
785
+ * fail: a provider outage or a charge old enough to have aged out
786
+ * answers `200` with `available: false` and a short reason, so render
787
+ * the rest of the page regardless.
788
+ */
789
+ retrieveProvider(id) {
790
+ return this.get(`/v1/payments/${p(id)}/provider`);
698
791
  }
699
792
  list(params = {}) {
700
793
  return this.get("/v1/payments", params);
701
794
  }
702
795
  iter(options = {}) {
703
- return paginate((p) => this.get("/v1/payments", p), { pageSize: options.pageSize });
796
+ return paginate((page) => this.get("/v1/payments", page), {
797
+ pageSize: options.pageSize,
798
+ filters: { customer_id: options.customer_id }
799
+ });
704
800
  }
705
801
  };
706
802
  var BillingPortalSessions = class extends BaseResource {
707
803
  create(params) {
708
- return this.postFixed(
709
- "/v1/billing_portal/sessions",
710
- {
711
- subscription_id: params.subscription_id,
712
- return_url: params.return_url
713
- },
714
- { idempotencyKey: params.idempotencyKey }
715
- );
804
+ return this.post("/v1/billing_portal/sessions", params);
716
805
  }
717
806
  /** Kill an in-the-wild portal session. Idempotent. */
718
807
  revoke(id, params = {}) {
719
- return this.postEmpty(`/v1/billing_portal/sessions/${id}/revoke`, params);
808
+ return this.postEmpty(`/v1/billing_portal/sessions/${p(id)}/revoke`, params);
809
+ }
810
+ };
811
+ var ApiKeys = class extends BaseResource {
812
+ /**
813
+ * Issue a new key. The response's `secret` is the only time the full
814
+ * key exists outside the caller's own storage, so record it now.
815
+ */
816
+ create(params = {}) {
817
+ return this.post("/v1/api_keys", params);
818
+ }
819
+ /**
820
+ * One key's metadata: prefix, label, scopes, `revoked_at`, and
821
+ * `last_used_at`, which is the field to read before revoking one.
822
+ */
823
+ retrieve(id) {
824
+ return this.get(`/v1/api_keys/${p(id)}`);
825
+ }
826
+ /**
827
+ * Revoke a key so it stops working. Immediate and irreversible; issue a
828
+ * new key instead. Revoking an already-revoked key returns it
829
+ * unchanged, so a retry is safe, and a key may revoke itself, which is
830
+ * what you want when the leaked key is the one you are calling with.
831
+ */
832
+ revoke(id, params = {}) {
833
+ return this.postEmpty(`/v1/api_keys/${p(id)}/revoke`, params);
834
+ }
835
+ /** List keys, newest first. Revoked ones are included; check `revoked_at`. */
836
+ list(params = {}) {
837
+ return this.get("/v1/api_keys", params);
838
+ }
839
+ iter(options = {}) {
840
+ return paginate((page) => this.get("/v1/api_keys", page), { pageSize: options.pageSize });
720
841
  }
721
842
  };
722
843
 
@@ -730,7 +851,7 @@ var BillKitError = class extends Error {
730
851
  requestId;
731
852
  rawBody;
732
853
  constructor(message, options = {}) {
733
- super(message);
854
+ super(message, options.cause === void 0 ? void 0 : { cause: options.cause });
734
855
  this.type = options.type;
735
856
  this.code = options.code;
736
857
  this.param = options.param;
@@ -850,7 +971,7 @@ function sleep(ms) {
850
971
  }
851
972
 
852
973
  // src/version.ts
853
- var VERSION = "0.6.0";
974
+ var VERSION = "0.7.0";
854
975
 
855
976
  // src/transport.ts
856
977
  var DEFAULT_BASE_URL = "https://api.billkit.eu";
@@ -878,9 +999,8 @@ function buildUrl(baseUrl, path, query) {
878
999
  const url = new URL(baseUrl.replace(/\/$/, "") + normalised);
879
1000
  if (query) {
880
1001
  for (const [k, v] of Object.entries(query)) {
881
- if (v !== null && v !== void 0) {
882
- url.searchParams.set(k, String(v));
883
- }
1002
+ if (v === null || v === void 0) continue;
1003
+ url.searchParams.set(k, Array.isArray(v) ? v.join(",") : String(v));
884
1004
  }
885
1005
  }
886
1006
  return url.toString();
@@ -934,9 +1054,11 @@ function retryDelayMs(status, attempt, policy, retryAfterMs) {
934
1054
  function connectionError(err, timeoutMs) {
935
1055
  const e = err;
936
1056
  if (e?.name === "TimeoutError" || e?.name === "AbortError") {
937
- return new APIConnectionError(`BillKit request timed out after ${timeoutMs}ms.`);
1057
+ return new APIConnectionError(`BillKit request timed out after ${timeoutMs}ms.`, {
1058
+ cause: err
1059
+ });
938
1060
  }
939
- return new APIConnectionError(e?.message ?? "Network request failed.");
1061
+ return new APIConnectionError(e?.message ?? "Network request failed.", { cause: err });
940
1062
  }
941
1063
  var Transport = class {
942
1064
  apiKey;
@@ -1070,6 +1192,7 @@ function resolveApiKey(supplied) {
1070
1192
  );
1071
1193
  }
1072
1194
  var BillKit = class {
1195
+ apiKeys;
1073
1196
  customers;
1074
1197
  products;
1075
1198
  prices;
@@ -1093,6 +1216,7 @@ var BillKit = class {
1093
1216
  ...options,
1094
1217
  apiKey: resolveApiKey(options.apiKey)
1095
1218
  });
1219
+ this.apiKeys = new ApiKeys(transport);
1096
1220
  this.customers = new Customers(transport);
1097
1221
  this.products = new Products(transport);
1098
1222
  this.prices = new Prices(transport);