@ophelio/sdk 0.2.0 → 0.2.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/README.md CHANGED
@@ -66,10 +66,12 @@ GETs are retried automatically on network errors / 429 / 502 / 503 / 504
66
66
  (exponential backoff with jitter, `Retry-After` honoured; configure with
67
67
  `maxRetries`, default 2). Plain mutations are **never** retried.
68
68
 
69
- `entitlements.redeem` and `entitlements.recordUsage` are idempotency-keyed:
70
- the SDK generates a key per call and reuses it across its own retries, so
71
- they're safely retryable. If you retry at the application level (offline
72
- gate queues), pass your own key:
69
+ The idempotency-keyed mutations — `memberships.create`, `memberships.renew`,
70
+ `memberships.createTransaction`, `entitlements.redeem`, and
71
+ `entitlements.recordUsage` — each generate a key per call and reuse it across
72
+ the SDK's own retries, so they're safely retried on transient failures. If you
73
+ retry at the application level (offline gate queues, at-least-once job runners),
74
+ pass your own key so replays are recognised across processes:
73
75
 
74
76
  ```ts
75
77
  await ophelio.entitlements.redeem(
@@ -170,7 +172,8 @@ Everything reachable with an API key is covered:
170
172
  | `webhookSubscriptions` | `list()`, `get(id)`, `create(...)`, `update(id, ...)`, `delete(id)`, `listDeliveries(id, params?)` |
171
173
 
172
174
  Every method accepts a trailing `options` argument: `{ signal?, headers? }`
173
- (plus `idempotencyKey?` on `redeem` / `recordUsage`).
175
+ (plus `idempotencyKey?` on the idempotency-keyed mutations: `memberships.create`
176
+ / `renew` / `createTransaction` and `entitlements.redeem` / `recordUsage`).
174
177
 
175
178
  Endpoints requiring a user session (API key management, organisation and
176
179
  project admin) are intentionally not in the SDK — API keys cannot call them.
package/dist/index.cjs CHANGED
@@ -117,6 +117,15 @@ function sleep(ms) {
117
117
  const DEFAULT_BASE_URL = "https://api.ophel.io";
118
118
  const DEFAULT_TIMEOUT_MS = 3e4;
119
119
  const DEFAULT_MAX_RETRIES = 2;
120
+ /**
121
+ * Resolves the caller-supplied idempotency key to the value sent on the wire.
122
+ * Trims it and falls back to a generated key when it is absent, empty, or
123
+ * whitespace-only — a blank key must never leave an idempotency-keyed mutation
124
+ * retryable but unkeyed, or a retried write could duplicate.
125
+ */
126
+ function resolveIdempotencyKey(idempotencyKey) {
127
+ return idempotencyKey?.trim() || crypto.randomUUID();
128
+ }
120
129
  var Transport = class {
121
130
  apiKey;
122
131
  baseUrl;
@@ -384,7 +393,7 @@ var EntitlementsResource = class extends APIResource {
384
393
  method: "POST",
385
394
  path: "/api/v1/entitlements/redeem",
386
395
  body: params,
387
- idempotencyKey: idempotencyKey ?? crypto.randomUUID(),
396
+ idempotencyKey: resolveIdempotencyKey(idempotencyKey),
388
397
  retryable: true,
389
398
  options: requestOptions
390
399
  });
@@ -399,7 +408,7 @@ var EntitlementsResource = class extends APIResource {
399
408
  method: "POST",
400
409
  path: "/api/v1/entitlements/usage",
401
410
  body: params,
402
- idempotencyKey: idempotencyKey ?? crypto.randomUUID(),
411
+ idempotencyKey: resolveIdempotencyKey(idempotencyKey),
403
412
  retryable: true,
404
413
  options: requestOptions
405
414
  })).usage;
@@ -562,13 +571,22 @@ var MembershipsResource = class extends APIResource {
562
571
  options
563
572
  });
564
573
  }
565
- create(params, options) {
574
+ /**
575
+ * Create a membership atomically (record, members, first term, optional
576
+ * payment). Idempotent: the Idempotency-Key is generated once per call (and
577
+ * reused across the SDK's internal retries), so a retried create returns the
578
+ * original membership instead of creating a duplicate. Supply your own key to
579
+ * dedupe application-level retries across processes.
580
+ */
581
+ create(params, options = {}) {
582
+ const { idempotencyKey, ...requestOptions } = options;
566
583
  return this.transport.request({
567
584
  method: "POST",
568
585
  path: "/api/v1/memberships",
569
586
  body: params,
570
- retryable: false,
571
- options
587
+ idempotencyKey: resolveIdempotencyKey(idempotencyKey),
588
+ retryable: true,
589
+ options: requestOptions
572
590
  });
573
591
  }
574
592
  update(id, params, options) {
@@ -615,14 +633,19 @@ var MembershipsResource = class extends APIResource {
615
633
  /**
616
634
  * Report a payment outcome. Ophel.io never processes payments — your
617
635
  * payment provider does; this records the result and drives dunning.
636
+ * Idempotent: the Idempotency-Key is generated once per call (and reused
637
+ * across the SDK's internal retries), so a retried report records the
638
+ * transaction once and does not advance dunning twice.
618
639
  */
619
- createTransaction(id, params, options) {
640
+ createTransaction(id, params, options = {}) {
641
+ const { idempotencyKey, ...requestOptions } = options;
620
642
  return this.transport.request({
621
643
  method: "POST",
622
644
  path: `/api/v1/memberships/${encodeURIComponent(id)}/transactions`,
623
645
  body: params,
624
- retryable: false,
625
- options
646
+ idempotencyKey: resolveIdempotencyKey(idempotencyKey),
647
+ retryable: true,
648
+ options: requestOptions
626
649
  });
627
650
  }
628
651
  async listMembers(id, options) {
@@ -633,14 +656,21 @@ var MembershipsResource = class extends APIResource {
633
656
  options
634
657
  })).members;
635
658
  }
636
- /** Open the next term. Rejected (409) while cancelled or cancellation-pending. */
637
- renew(id, params, options) {
659
+ /**
660
+ * Open the next term. Rejected (409) while cancelled or cancellation-pending.
661
+ * Idempotent: the Idempotency-Key is generated once per call (and reused
662
+ * across the SDK's internal retries), so a retried renewal returns the
663
+ * original result instead of opening a second term.
664
+ */
665
+ renew(id, params, options = {}) {
666
+ const { idempotencyKey, ...requestOptions } = options;
638
667
  return this.transport.request({
639
668
  method: "POST",
640
669
  path: `/api/v1/memberships/${encodeURIComponent(id)}/renew`,
641
670
  body: params,
642
- retryable: false,
643
- options
671
+ idempotencyKey: resolveIdempotencyKey(idempotencyKey),
672
+ retryable: true,
673
+ options: requestOptions
644
674
  });
645
675
  }
646
676
  /**
package/dist/index.d.cts CHANGED
@@ -2916,8 +2916,7 @@ interface operations {
2916
2916
  parameters: {
2917
2917
  query?: never;
2918
2918
  header: {
2919
- "idempotency-key": string;
2920
- "Idempotency-Key": string;
2919
+ /** @description Required idempotency key, up to 255 characters. Deduplicated per member: repeating a request with the same key and member returns the original redemption, and reusing that key for the same member with a different entitlement returns 409. A longer key is rejected with 400. */"Idempotency-Key": string;
2921
2920
  };
2922
2921
  path?: never;
2923
2922
  cookie?: never;
@@ -2952,14 +2951,21 @@ interface operations {
2952
2951
  "application/json": components["schemas"]["ErrorResponse"];
2953
2952
  };
2954
2953
  };
2954
+ 409: {
2955
+ headers: {
2956
+ [name: string]: unknown;
2957
+ };
2958
+ content: {
2959
+ "application/json": components["schemas"]["ErrorResponse"];
2960
+ };
2961
+ };
2955
2962
  };
2956
2963
  };
2957
2964
  EntitlementsController_usage: {
2958
2965
  parameters: {
2959
2966
  query?: never;
2960
2967
  header: {
2961
- "idempotency-key": string;
2962
- "Idempotency-Key": string;
2968
+ /** @description Required idempotency key, up to 255 characters. One usage row is recorded per member, so the same key may carry different members across calls. A longer key is rejected with 400. */"Idempotency-Key": string;
2963
2969
  };
2964
2970
  path?: never;
2965
2971
  cookie?: never;
@@ -3654,7 +3660,9 @@ interface operations {
3654
3660
  MembershipsController_create: {
3655
3661
  parameters: {
3656
3662
  query?: never;
3657
- header?: never;
3663
+ header?: {
3664
+ /** @description Optional idempotency key, up to 255 characters. Repeating a request with the same key returns the original result; reusing a key with a different request body returns 409. A longer key is rejected with 400, and an empty or whitespace-only value is treated as no key. */"Idempotency-Key"?: string;
3665
+ };
3658
3666
  path?: never;
3659
3667
  cookie?: never;
3660
3668
  };
@@ -3680,6 +3688,14 @@ interface operations {
3680
3688
  "application/json": components["schemas"]["ErrorResponse"];
3681
3689
  };
3682
3690
  };
3691
+ 409: {
3692
+ headers: {
3693
+ [name: string]: unknown;
3694
+ };
3695
+ content: {
3696
+ "application/json": components["schemas"]["ErrorResponse"];
3697
+ };
3698
+ };
3683
3699
  };
3684
3700
  };
3685
3701
  MembershipsController_get: {
@@ -3828,7 +3844,9 @@ interface operations {
3828
3844
  MembershipsController_renew: {
3829
3845
  parameters: {
3830
3846
  query?: never;
3831
- header?: never;
3847
+ header?: {
3848
+ /** @description Optional idempotency key, up to 255 characters. Repeating a request with the same key returns the original result; reusing a key with a different request body returns 409. A longer key is rejected with 400, and an empty or whitespace-only value is treated as no key. */"Idempotency-Key"?: string;
3849
+ };
3832
3850
  path?: never;
3833
3851
  cookie?: never;
3834
3852
  };
@@ -3854,6 +3872,14 @@ interface operations {
3854
3872
  "application/json": components["schemas"]["ErrorResponse"];
3855
3873
  };
3856
3874
  };
3875
+ 409: {
3876
+ headers: {
3877
+ [name: string]: unknown;
3878
+ };
3879
+ content: {
3880
+ "application/json": components["schemas"]["ErrorResponse"];
3881
+ };
3882
+ };
3857
3883
  };
3858
3884
  };
3859
3885
  MembershipsController_upgrade: {
@@ -3999,7 +4025,9 @@ interface operations {
3999
4025
  MembershipsController_recordTransaction: {
4000
4026
  parameters: {
4001
4027
  query?: never;
4002
- header?: never;
4028
+ header?: {
4029
+ /** @description Optional idempotency key, up to 255 characters. Repeating a request with the same key returns the original result; reusing a key with a different request body returns 409. A longer key is rejected with 400, and an empty or whitespace-only value is treated as no key. */"Idempotency-Key"?: string;
4030
+ };
4003
4031
  path?: never;
4004
4032
  cookie?: never;
4005
4033
  };
@@ -4025,6 +4053,14 @@ interface operations {
4025
4053
  "application/json": components["schemas"]["ErrorResponse"];
4026
4054
  };
4027
4055
  };
4056
+ 409: {
4057
+ headers: {
4058
+ [name: string]: unknown;
4059
+ };
4060
+ content: {
4061
+ "application/json": components["schemas"]["ErrorResponse"];
4062
+ };
4063
+ };
4028
4064
  };
4029
4065
  };
4030
4066
  MembershipsController_listTermTransactions: {
@@ -4523,7 +4559,14 @@ declare class MembersResource extends APIResource {
4523
4559
  declare class MembershipsResource extends APIResource {
4524
4560
  list(params?: PageParams, options?: RequestOptions): Promise<Page<Membership>>;
4525
4561
  get(id: string, options?: RequestOptions): Promise<Membership>;
4526
- create(params: CreateMembershipParams, options?: RequestOptions): Promise<Membership>;
4562
+ /**
4563
+ * Create a membership atomically (record, members, first term, optional
4564
+ * payment). Idempotent: the Idempotency-Key is generated once per call (and
4565
+ * reused across the SDK's internal retries), so a retried create returns the
4566
+ * original membership instead of creating a duplicate. Supply your own key to
4567
+ * dedupe application-level retries across processes.
4568
+ */
4569
+ create(params: CreateMembershipParams, options?: IdempotentRequestOptions): Promise<Membership>;
4527
4570
  update(id: string, params: UpdateMembershipParams, options?: RequestOptions): Promise<Membership>;
4528
4571
  /** Cancellation is a state change, not a delete — see also `uncancel`. */
4529
4572
  cancel(id: string, params: CancelMembershipParams, options?: RequestOptions): Promise<Membership>;
@@ -4532,11 +4575,19 @@ declare class MembershipsResource extends APIResource {
4532
4575
  /**
4533
4576
  * Report a payment outcome. Ophel.io never processes payments — your
4534
4577
  * payment provider does; this records the result and drives dunning.
4578
+ * Idempotent: the Idempotency-Key is generated once per call (and reused
4579
+ * across the SDK's internal retries), so a retried report records the
4580
+ * transaction once and does not advance dunning twice.
4535
4581
  */
4536
- createTransaction(id: string, params: CreateTransactionParams, options?: RequestOptions): Promise<Transaction>;
4582
+ createTransaction(id: string, params: CreateTransactionParams, options?: IdempotentRequestOptions): Promise<Transaction>;
4537
4583
  listMembers(id: string, options?: RequestOptions): Promise<Member[]>;
4538
- /** Open the next term. Rejected (409) while cancelled or cancellation-pending. */
4539
- renew(id: string, params: RenewMembershipParams, options?: RequestOptions): Promise<Membership>;
4584
+ /**
4585
+ * Open the next term. Rejected (409) while cancelled or cancellation-pending.
4586
+ * Idempotent: the Idempotency-Key is generated once per call (and reused
4587
+ * across the SDK's internal retries), so a retried renewal returns the
4588
+ * original result instead of opening a second term.
4589
+ */
4590
+ renew(id: string, params: RenewMembershipParams, options?: IdempotentRequestOptions): Promise<Membership>;
4540
4591
  /**
4541
4592
  * Move to a linked higher plan. Applies immediately or schedules at term
4542
4593
  * end — `scheduled_change` is non-null in the deferred case.
package/dist/index.d.mts CHANGED
@@ -2916,8 +2916,7 @@ interface operations {
2916
2916
  parameters: {
2917
2917
  query?: never;
2918
2918
  header: {
2919
- "idempotency-key": string;
2920
- "Idempotency-Key": string;
2919
+ /** @description Required idempotency key, up to 255 characters. Deduplicated per member: repeating a request with the same key and member returns the original redemption, and reusing that key for the same member with a different entitlement returns 409. A longer key is rejected with 400. */"Idempotency-Key": string;
2921
2920
  };
2922
2921
  path?: never;
2923
2922
  cookie?: never;
@@ -2952,14 +2951,21 @@ interface operations {
2952
2951
  "application/json": components["schemas"]["ErrorResponse"];
2953
2952
  };
2954
2953
  };
2954
+ 409: {
2955
+ headers: {
2956
+ [name: string]: unknown;
2957
+ };
2958
+ content: {
2959
+ "application/json": components["schemas"]["ErrorResponse"];
2960
+ };
2961
+ };
2955
2962
  };
2956
2963
  };
2957
2964
  EntitlementsController_usage: {
2958
2965
  parameters: {
2959
2966
  query?: never;
2960
2967
  header: {
2961
- "idempotency-key": string;
2962
- "Idempotency-Key": string;
2968
+ /** @description Required idempotency key, up to 255 characters. One usage row is recorded per member, so the same key may carry different members across calls. A longer key is rejected with 400. */"Idempotency-Key": string;
2963
2969
  };
2964
2970
  path?: never;
2965
2971
  cookie?: never;
@@ -3654,7 +3660,9 @@ interface operations {
3654
3660
  MembershipsController_create: {
3655
3661
  parameters: {
3656
3662
  query?: never;
3657
- header?: never;
3663
+ header?: {
3664
+ /** @description Optional idempotency key, up to 255 characters. Repeating a request with the same key returns the original result; reusing a key with a different request body returns 409. A longer key is rejected with 400, and an empty or whitespace-only value is treated as no key. */"Idempotency-Key"?: string;
3665
+ };
3658
3666
  path?: never;
3659
3667
  cookie?: never;
3660
3668
  };
@@ -3680,6 +3688,14 @@ interface operations {
3680
3688
  "application/json": components["schemas"]["ErrorResponse"];
3681
3689
  };
3682
3690
  };
3691
+ 409: {
3692
+ headers: {
3693
+ [name: string]: unknown;
3694
+ };
3695
+ content: {
3696
+ "application/json": components["schemas"]["ErrorResponse"];
3697
+ };
3698
+ };
3683
3699
  };
3684
3700
  };
3685
3701
  MembershipsController_get: {
@@ -3828,7 +3844,9 @@ interface operations {
3828
3844
  MembershipsController_renew: {
3829
3845
  parameters: {
3830
3846
  query?: never;
3831
- header?: never;
3847
+ header?: {
3848
+ /** @description Optional idempotency key, up to 255 characters. Repeating a request with the same key returns the original result; reusing a key with a different request body returns 409. A longer key is rejected with 400, and an empty or whitespace-only value is treated as no key. */"Idempotency-Key"?: string;
3849
+ };
3832
3850
  path?: never;
3833
3851
  cookie?: never;
3834
3852
  };
@@ -3854,6 +3872,14 @@ interface operations {
3854
3872
  "application/json": components["schemas"]["ErrorResponse"];
3855
3873
  };
3856
3874
  };
3875
+ 409: {
3876
+ headers: {
3877
+ [name: string]: unknown;
3878
+ };
3879
+ content: {
3880
+ "application/json": components["schemas"]["ErrorResponse"];
3881
+ };
3882
+ };
3857
3883
  };
3858
3884
  };
3859
3885
  MembershipsController_upgrade: {
@@ -3999,7 +4025,9 @@ interface operations {
3999
4025
  MembershipsController_recordTransaction: {
4000
4026
  parameters: {
4001
4027
  query?: never;
4002
- header?: never;
4028
+ header?: {
4029
+ /** @description Optional idempotency key, up to 255 characters. Repeating a request with the same key returns the original result; reusing a key with a different request body returns 409. A longer key is rejected with 400, and an empty or whitespace-only value is treated as no key. */"Idempotency-Key"?: string;
4030
+ };
4003
4031
  path?: never;
4004
4032
  cookie?: never;
4005
4033
  };
@@ -4025,6 +4053,14 @@ interface operations {
4025
4053
  "application/json": components["schemas"]["ErrorResponse"];
4026
4054
  };
4027
4055
  };
4056
+ 409: {
4057
+ headers: {
4058
+ [name: string]: unknown;
4059
+ };
4060
+ content: {
4061
+ "application/json": components["schemas"]["ErrorResponse"];
4062
+ };
4063
+ };
4028
4064
  };
4029
4065
  };
4030
4066
  MembershipsController_listTermTransactions: {
@@ -4523,7 +4559,14 @@ declare class MembersResource extends APIResource {
4523
4559
  declare class MembershipsResource extends APIResource {
4524
4560
  list(params?: PageParams, options?: RequestOptions): Promise<Page<Membership>>;
4525
4561
  get(id: string, options?: RequestOptions): Promise<Membership>;
4526
- create(params: CreateMembershipParams, options?: RequestOptions): Promise<Membership>;
4562
+ /**
4563
+ * Create a membership atomically (record, members, first term, optional
4564
+ * payment). Idempotent: the Idempotency-Key is generated once per call (and
4565
+ * reused across the SDK's internal retries), so a retried create returns the
4566
+ * original membership instead of creating a duplicate. Supply your own key to
4567
+ * dedupe application-level retries across processes.
4568
+ */
4569
+ create(params: CreateMembershipParams, options?: IdempotentRequestOptions): Promise<Membership>;
4527
4570
  update(id: string, params: UpdateMembershipParams, options?: RequestOptions): Promise<Membership>;
4528
4571
  /** Cancellation is a state change, not a delete — see also `uncancel`. */
4529
4572
  cancel(id: string, params: CancelMembershipParams, options?: RequestOptions): Promise<Membership>;
@@ -4532,11 +4575,19 @@ declare class MembershipsResource extends APIResource {
4532
4575
  /**
4533
4576
  * Report a payment outcome. Ophel.io never processes payments — your
4534
4577
  * payment provider does; this records the result and drives dunning.
4578
+ * Idempotent: the Idempotency-Key is generated once per call (and reused
4579
+ * across the SDK's internal retries), so a retried report records the
4580
+ * transaction once and does not advance dunning twice.
4535
4581
  */
4536
- createTransaction(id: string, params: CreateTransactionParams, options?: RequestOptions): Promise<Transaction>;
4582
+ createTransaction(id: string, params: CreateTransactionParams, options?: IdempotentRequestOptions): Promise<Transaction>;
4537
4583
  listMembers(id: string, options?: RequestOptions): Promise<Member[]>;
4538
- /** Open the next term. Rejected (409) while cancelled or cancellation-pending. */
4539
- renew(id: string, params: RenewMembershipParams, options?: RequestOptions): Promise<Membership>;
4584
+ /**
4585
+ * Open the next term. Rejected (409) while cancelled or cancellation-pending.
4586
+ * Idempotent: the Idempotency-Key is generated once per call (and reused
4587
+ * across the SDK's internal retries), so a retried renewal returns the
4588
+ * original result instead of opening a second term.
4589
+ */
4590
+ renew(id: string, params: RenewMembershipParams, options?: IdempotentRequestOptions): Promise<Membership>;
4540
4591
  /**
4541
4592
  * Move to a linked higher plan. Applies immediately or schedules at term
4542
4593
  * end — `scheduled_change` is non-null in the deferred case.
package/dist/index.mjs CHANGED
@@ -116,6 +116,15 @@ function sleep(ms) {
116
116
  const DEFAULT_BASE_URL = "https://api.ophel.io";
117
117
  const DEFAULT_TIMEOUT_MS = 3e4;
118
118
  const DEFAULT_MAX_RETRIES = 2;
119
+ /**
120
+ * Resolves the caller-supplied idempotency key to the value sent on the wire.
121
+ * Trims it and falls back to a generated key when it is absent, empty, or
122
+ * whitespace-only — a blank key must never leave an idempotency-keyed mutation
123
+ * retryable but unkeyed, or a retried write could duplicate.
124
+ */
125
+ function resolveIdempotencyKey(idempotencyKey) {
126
+ return idempotencyKey?.trim() || crypto.randomUUID();
127
+ }
119
128
  var Transport = class {
120
129
  apiKey;
121
130
  baseUrl;
@@ -383,7 +392,7 @@ var EntitlementsResource = class extends APIResource {
383
392
  method: "POST",
384
393
  path: "/api/v1/entitlements/redeem",
385
394
  body: params,
386
- idempotencyKey: idempotencyKey ?? crypto.randomUUID(),
395
+ idempotencyKey: resolveIdempotencyKey(idempotencyKey),
387
396
  retryable: true,
388
397
  options: requestOptions
389
398
  });
@@ -398,7 +407,7 @@ var EntitlementsResource = class extends APIResource {
398
407
  method: "POST",
399
408
  path: "/api/v1/entitlements/usage",
400
409
  body: params,
401
- idempotencyKey: idempotencyKey ?? crypto.randomUUID(),
410
+ idempotencyKey: resolveIdempotencyKey(idempotencyKey),
402
411
  retryable: true,
403
412
  options: requestOptions
404
413
  })).usage;
@@ -561,13 +570,22 @@ var MembershipsResource = class extends APIResource {
561
570
  options
562
571
  });
563
572
  }
564
- create(params, options) {
573
+ /**
574
+ * Create a membership atomically (record, members, first term, optional
575
+ * payment). Idempotent: the Idempotency-Key is generated once per call (and
576
+ * reused across the SDK's internal retries), so a retried create returns the
577
+ * original membership instead of creating a duplicate. Supply your own key to
578
+ * dedupe application-level retries across processes.
579
+ */
580
+ create(params, options = {}) {
581
+ const { idempotencyKey, ...requestOptions } = options;
565
582
  return this.transport.request({
566
583
  method: "POST",
567
584
  path: "/api/v1/memberships",
568
585
  body: params,
569
- retryable: false,
570
- options
586
+ idempotencyKey: resolveIdempotencyKey(idempotencyKey),
587
+ retryable: true,
588
+ options: requestOptions
571
589
  });
572
590
  }
573
591
  update(id, params, options) {
@@ -614,14 +632,19 @@ var MembershipsResource = class extends APIResource {
614
632
  /**
615
633
  * Report a payment outcome. Ophel.io never processes payments — your
616
634
  * payment provider does; this records the result and drives dunning.
635
+ * Idempotent: the Idempotency-Key is generated once per call (and reused
636
+ * across the SDK's internal retries), so a retried report records the
637
+ * transaction once and does not advance dunning twice.
617
638
  */
618
- createTransaction(id, params, options) {
639
+ createTransaction(id, params, options = {}) {
640
+ const { idempotencyKey, ...requestOptions } = options;
619
641
  return this.transport.request({
620
642
  method: "POST",
621
643
  path: `/api/v1/memberships/${encodeURIComponent(id)}/transactions`,
622
644
  body: params,
623
- retryable: false,
624
- options
645
+ idempotencyKey: resolveIdempotencyKey(idempotencyKey),
646
+ retryable: true,
647
+ options: requestOptions
625
648
  });
626
649
  }
627
650
  async listMembers(id, options) {
@@ -632,14 +655,21 @@ var MembershipsResource = class extends APIResource {
632
655
  options
633
656
  })).members;
634
657
  }
635
- /** Open the next term. Rejected (409) while cancelled or cancellation-pending. */
636
- renew(id, params, options) {
658
+ /**
659
+ * Open the next term. Rejected (409) while cancelled or cancellation-pending.
660
+ * Idempotent: the Idempotency-Key is generated once per call (and reused
661
+ * across the SDK's internal retries), so a retried renewal returns the
662
+ * original result instead of opening a second term.
663
+ */
664
+ renew(id, params, options = {}) {
665
+ const { idempotencyKey, ...requestOptions } = options;
637
666
  return this.transport.request({
638
667
  method: "POST",
639
668
  path: `/api/v1/memberships/${encodeURIComponent(id)}/renew`,
640
669
  body: params,
641
- retryable: false,
642
- options
670
+ idempotencyKey: resolveIdempotencyKey(idempotencyKey),
671
+ retryable: true,
672
+ options: requestOptions
643
673
  });
644
674
  }
645
675
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ophelio/sdk",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Official JavaScript / TypeScript SDK for the Ophel.io membership & entitlement API.",
5
5
  "license": "MIT",
6
6
  "author": "Ophel.io",