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