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