@billkit-eu/sdk 0.6.0 → 0.7.1

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