@paykit-sdk/gopay 1.3.0 → 1.3.2

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.
@@ -44,6 +44,7 @@ declare class GoPayProvider extends AbstractPayKitProvider implements PayKitProv
44
44
  private tokenManager;
45
45
  get _native(): null;
46
46
  constructor(opts: GoPayOptions);
47
+ private submitPayment;
47
48
  createCheckout: (params: CreateCheckoutSchema<GoPayMetadata["checkout"]>) => Promise<Checkout>;
48
49
  retrieveCheckout: (id: string) => Promise<Checkout | null>;
49
50
  updateCheckout: (id: string, params: UpdateCheckoutSchema) => Promise<Checkout>;
@@ -53,7 +54,22 @@ declare class GoPayProvider extends AbstractPayKitProvider implements PayKitProv
53
54
  deleteCustomer: (id: string) => Promise<null>;
54
55
  retrieveCustomer: (id: string) => Promise<Customer | null>;
55
56
  createSubscription: (params: CreateSubscriptionSchema<GoPayMetadata["subscription"]>) => Promise<Subscription>;
56
- updateSubscription: (id: string, params: UpdateSubscriptionSchema) => Promise<Subscription>;
57
+ /**
58
+ * `updateSubscription` is GoPay's real mechanism for collecting each
59
+ * charge on an ON_DEMAND recurring mandate - GoPay never charges these
60
+ * automatically, so every subsequent payment has to be triggered
61
+ * explicitly via `POST /payments/payment/{id}/create-recurrence`.
62
+ * Supply the charge via `provider_metadata.amount` (and optionally
63
+ * `currency`/`order_number`/`order_description`/`items`); `params.metadata`
64
+ * is stored on that charge the same way createCheckout/createPayment do.
65
+ *
66
+ * For AUTO cycles (DAY/WEEK/MONTH), GoPay already charges on its own
67
+ * schedule and there's nothing to trigger - calling this without
68
+ * `provider_metadata.amount` just re-fetches the current subscription.
69
+ *
70
+ * @see https://doc.gopay.com/#recurring-payments
71
+ */
72
+ updateSubscription: (id: string, params: UpdateSubscriptionSchema<GoPayMetadata["subscription"]>) => Promise<Subscription>;
57
73
  cancelSubscription: (id: string) => Promise<Subscription>;
58
74
  deleteSubscription: (id: string) => Promise<null>;
59
75
  retrieveSubscription: (id: string) => Promise<Subscription | null>;
@@ -44,6 +44,7 @@ declare class GoPayProvider extends AbstractPayKitProvider implements PayKitProv
44
44
  private tokenManager;
45
45
  get _native(): null;
46
46
  constructor(opts: GoPayOptions);
47
+ private submitPayment;
47
48
  createCheckout: (params: CreateCheckoutSchema<GoPayMetadata["checkout"]>) => Promise<Checkout>;
48
49
  retrieveCheckout: (id: string) => Promise<Checkout | null>;
49
50
  updateCheckout: (id: string, params: UpdateCheckoutSchema) => Promise<Checkout>;
@@ -53,7 +54,22 @@ declare class GoPayProvider extends AbstractPayKitProvider implements PayKitProv
53
54
  deleteCustomer: (id: string) => Promise<null>;
54
55
  retrieveCustomer: (id: string) => Promise<Customer | null>;
55
56
  createSubscription: (params: CreateSubscriptionSchema<GoPayMetadata["subscription"]>) => Promise<Subscription>;
56
- updateSubscription: (id: string, params: UpdateSubscriptionSchema) => Promise<Subscription>;
57
+ /**
58
+ * `updateSubscription` is GoPay's real mechanism for collecting each
59
+ * charge on an ON_DEMAND recurring mandate - GoPay never charges these
60
+ * automatically, so every subsequent payment has to be triggered
61
+ * explicitly via `POST /payments/payment/{id}/create-recurrence`.
62
+ * Supply the charge via `provider_metadata.amount` (and optionally
63
+ * `currency`/`order_number`/`order_description`/`items`); `params.metadata`
64
+ * is stored on that charge the same way createCheckout/createPayment do.
65
+ *
66
+ * For AUTO cycles (DAY/WEEK/MONTH), GoPay already charges on its own
67
+ * schedule and there's nothing to trigger - calling this without
68
+ * `provider_metadata.amount` just re-fetches the current subscription.
69
+ *
70
+ * @see https://doc.gopay.com/#recurring-payments
71
+ */
72
+ updateSubscription: (id: string, params: UpdateSubscriptionSchema<GoPayMetadata["subscription"]>) => Promise<Subscription>;
57
73
  cancelSubscription: (id: string) => Promise<Subscription>;
58
74
  deleteSubscription: (id: string) => Promise<null>;
59
75
  retrieveSubscription: (id: string) => Promise<Subscription | null>;
@@ -4178,7 +4178,7 @@ var Invoice$inboundSchema = (data, isSubscription) => {
4178
4178
  };
4179
4179
  };
4180
4180
  var Subscription$inboundSchema = (data) => {
4181
- const { item } = JSON.parse(
4181
+ const { item, billing_interval: storedBillingInterval } = JSON.parse(
4182
4182
  decodeHtmlEntities(
4183
4183
  data.additional_params?.find(
4184
4184
  (param) => param.name === core.PAYKIT_METADATA_KEY
@@ -4191,7 +4191,18 @@ var Subscription$inboundSchema = (data) => {
4191
4191
  MONTH: "month",
4192
4192
  ON_DEMAND: "month"
4193
4193
  };
4194
- const billingInterval = billingIntervalMap[data.recurrence?.recurrence_cycle ?? "ON_DEMAND"];
4194
+ const parseStoredBillingInterval = (stored) => {
4195
+ if (typeof stored !== "string") return null;
4196
+ if (stored === "day" || stored === "week" || stored === "month" || stored === "year") {
4197
+ return stored;
4198
+ }
4199
+ const customMatch = /^custom:(\d+)ms$/.exec(stored);
4200
+ if (customMatch) {
4201
+ return { type: "custom", durationMs: Number(customMatch[1]) };
4202
+ }
4203
+ return null;
4204
+ };
4205
+ const billingInterval = parseStoredBillingInterval(storedBillingInterval) ?? billingIntervalMap[data.recurrence?.recurrence_cycle ?? "ON_DEMAND"];
4195
4206
  const recurrencePeriod = data.recurrence?.recurrence_period ?? 1;
4196
4207
  const recurrenceCycle = data.recurrence?.recurrence_cycle;
4197
4208
  const currentPeriodEnd = new Date(
@@ -4313,7 +4324,7 @@ var GoPayProvider = class extends core.AbstractPayKitProvider {
4313
4324
  });
4314
4325
  }
4315
4326
  providerName = providerName;
4316
- providerVersion = "1.3.0";
4327
+ providerVersion = "1.3.2";
4317
4328
  _client;
4318
4329
  baseUrl;
4319
4330
  isSandbox;
@@ -4321,6 +4332,23 @@ var GoPayProvider = class extends core.AbstractPayKitProvider {
4321
4332
  get _native() {
4322
4333
  return null;
4323
4334
  }
4335
+ async submitPayment(request, method) {
4336
+ const response = await this._client.post(
4337
+ "/payments/payment",
4338
+ {
4339
+ body: JSON.stringify(request),
4340
+ headers: await this.tokenManager.getAuthHeaders()
4341
+ }
4342
+ );
4343
+ if (!response.ok) {
4344
+ throw new core.OperationFailedError(method, this.providerName, {
4345
+ cause: new Error(
4346
+ `Failed to submit payment: ${JSON.stringify(response.error ?? response)}`
4347
+ )
4348
+ });
4349
+ }
4350
+ return response.value;
4351
+ }
4324
4352
  createCheckout = async (params) => {
4325
4353
  const { error, data } = core.createCheckoutSchema.safeParse(params);
4326
4354
  if (error) {
@@ -4343,7 +4371,7 @@ var GoPayProvider = class extends core.AbstractPayKitProvider {
4343
4371
  }
4344
4372
  const { amount, currency = "CZK" } = core.validateRequiredKeys(
4345
4373
  ["amount", "currency"],
4346
- data.provider_metadata,
4374
+ data.provider_metadata ?? {},
4347
4375
  "The following fields must be present in the provider_metadata of createCheckout: {keys}"
4348
4376
  );
4349
4377
  if (this.opts.debug) {
@@ -4396,23 +4424,11 @@ var GoPayProvider = class extends core.AbstractPayKitProvider {
4396
4424
  value: String(value)
4397
4425
  }))
4398
4426
  };
4399
- const response = await this._client.post(
4400
- "/payments/payment",
4401
- {
4402
- body: JSON.stringify(goPayRequest),
4403
- headers: await this.tokenManager.getAuthHeaders()
4404
- }
4427
+ const responseValue = await this.submitPayment(
4428
+ goPayRequest,
4429
+ "createCheckout"
4405
4430
  );
4406
- if (!response.ok) {
4407
- throw new core.OperationFailedError(
4408
- "createCheckout",
4409
- this.providerName,
4410
- {
4411
- cause: new Error("Failed to create checkout")
4412
- }
4413
- );
4414
- }
4415
- return Checkout$inboundSchema(response.value);
4431
+ return Checkout$inboundSchema(responseValue);
4416
4432
  };
4417
4433
  retrieveCheckout = async (id) => {
4418
4434
  const response = await this._client.get(
@@ -4491,7 +4507,7 @@ var GoPayProvider = class extends core.AbstractPayKitProvider {
4491
4507
  }
4492
4508
  const { success_url } = core.validateRequiredKeys(
4493
4509
  ["success_url"],
4494
- data.provider_metadata,
4510
+ data.provider_metadata ?? {},
4495
4511
  "The following fields must be present in the provider_metadata of createSubscription: {keys}"
4496
4512
  );
4497
4513
  const billingInterval = data.billing_interval;
@@ -4508,22 +4524,22 @@ var GoPayProvider = class extends core.AbstractPayKitProvider {
4508
4524
  if (this.opts.debug) {
4509
4525
  if (isYear) {
4510
4526
  console.info(
4511
- "[PayKit/GoPay] GoPay does not support yearly recurrence. Falling back to ON_DEMAND \u2014 you must trigger each charge manually via createRecurrence(parentPaymentId, ...). See: https://doc.gopay.com/#recurring-on-demand"
4527
+ "[PayKit/GoPay] GoPay does not support yearly recurrence. Falling back to ON_DEMAND \u2014 trigger each charge via paykit.subscriptions.update(id, { provider_metadata: { amount } }). See: https://doc.gopay.com/#recurring-on-demand"
4512
4528
  );
4513
4529
  }
4514
4530
  if (isCustom) {
4515
4531
  const durationMs = billingInterval.durationMs;
4516
4532
  console.info(
4517
- `[PayKit/GoPay] Custom interval (${durationMs}ms) is not supported by GoPay. Falling back to ON_DEMAND \u2014 trigger charges manually via createRecurrence(parentPaymentId, ...). See: https://doc.gopay.com/#recurring-on-demand`
4533
+ `[PayKit/GoPay] Custom interval (${durationMs}ms) is not supported by GoPay. Falling back to ON_DEMAND \u2014 trigger each charge via paykit.subscriptions.update(id, { provider_metadata: { amount } }). See: https://doc.gopay.com/#recurring-on-demand`
4518
4534
  );
4519
4535
  }
4520
4536
  if (!isOnDemand) {
4521
4537
  console.info(
4522
- `[PayKit/GoPay] AUTO recurrence (${recurrenceCycle}) \u2014 GoPay charges automatically each cycle. No createRecurrence() call is needed. Set provider_metadata.end_date (ISO string, e.g. "2027-01-01") to control when the subscription ends. Defaults to 1 year from today.`
4538
+ `[PayKit/GoPay] AUTO recurrence (${recurrenceCycle}) \u2014 GoPay charges automatically each cycle. No manual update() call is needed to collect charges. Set provider_metadata.end_date (ISO string, e.g. "2027-01-01") to control when the subscription ends. Defaults to 1 year from today.`
4523
4539
  );
4524
4540
  } else {
4525
4541
  console.info(
4526
- "[PayKit/GoPay] ON_DEMAND recurrence \u2014 you MUST call createRecurrence(parentPaymentId, ...) for each subsequent charge. GoPay will NOT charge automatically. Set provider_metadata.end_date (ISO string) to control the authorization window. Defaults to 1 year (custom interval) or 5 years (yearly interval)."
4542
+ "[PayKit/GoPay] ON_DEMAND recurrence \u2014 you MUST call paykit.subscriptions.update(id, { provider_metadata: { amount } }) for each subsequent charge. GoPay will NOT charge automatically. Set provider_metadata.end_date (ISO string) to control the authorization window. Defaults to 1 year (custom interval) or 5 years (yearly interval)."
4527
4543
  );
4528
4544
  }
4529
4545
  if (!data.provider_metadata?.description) {
@@ -4576,7 +4592,8 @@ var GoPayProvider = class extends core.AbstractPayKitProvider {
4576
4592
  {
4577
4593
  name: data.item_id,
4578
4594
  amount: Number(data.amount),
4579
- count: data.quantity ?? 1
4595
+ count: data.quantity ?? 1,
4596
+ type: "ITEM"
4580
4597
  }
4581
4598
  ],
4582
4599
  recurrence,
@@ -4589,7 +4606,9 @@ var GoPayProvider = class extends core.AbstractPayKitProvider {
4589
4606
  [core.PAYKIT_METADATA_KEY]: JSON.stringify({
4590
4607
  item: data.item_id,
4591
4608
  qty: data.quantity,
4592
- // Store the original interval so createRecurrence callers know the intended cadence
4609
+ // Store the original interval so Subscription$inboundSchema can
4610
+ // recover it later - GoPay's recurrence_cycle collapses year and
4611
+ // custom intervals down to ON_DEMAND and can't tell them apart.
4593
4612
  billing_interval: isCustom ? `custom:${billingInterval.durationMs}ms` : billingInterval
4594
4613
  })
4595
4614
  }).map(([name, value]) => ({ name, value: String(value) }))
@@ -4614,15 +4633,80 @@ var GoPayProvider = class extends core.AbstractPayKitProvider {
4614
4633
  }
4615
4634
  return Subscription$inboundSchema(response.value);
4616
4635
  };
4636
+ /**
4637
+ * `updateSubscription` is GoPay's real mechanism for collecting each
4638
+ * charge on an ON_DEMAND recurring mandate - GoPay never charges these
4639
+ * automatically, so every subsequent payment has to be triggered
4640
+ * explicitly via `POST /payments/payment/{id}/create-recurrence`.
4641
+ * Supply the charge via `provider_metadata.amount` (and optionally
4642
+ * `currency`/`order_number`/`order_description`/`items`); `params.metadata`
4643
+ * is stored on that charge the same way createCheckout/createPayment do.
4644
+ *
4645
+ * For AUTO cycles (DAY/WEEK/MONTH), GoPay already charges on its own
4646
+ * schedule and there's nothing to trigger - calling this without
4647
+ * `provider_metadata.amount` just re-fetches the current subscription.
4648
+ *
4649
+ * @see https://doc.gopay.com/#recurring-payments
4650
+ */
4617
4651
  updateSubscription = async (id, params) => {
4652
+ const chargeParams = params.provider_metadata;
4653
+ if (chargeParams?.amount) {
4654
+ const body = {
4655
+ amount: chargeParams.amount,
4656
+ currency: (chargeParams.currency ?? "CZK").toUpperCase(),
4657
+ order_number: chargeParams.order_number ?? crypto__namespace.randomBytes(8).toString("hex").slice(0, 15),
4658
+ order_description: chargeParams.order_description ?? `Recurring charge for payment ${id}`,
4659
+ items: chargeParams.items ?? [
4660
+ {
4661
+ name: "recurring_charge",
4662
+ amount: chargeParams.amount,
4663
+ count: 1
4664
+ }
4665
+ ],
4666
+ additional_params: Object.entries(params.metadata ?? {}).map(
4667
+ ([name, value]) => ({ name, value: String(value) })
4668
+ )
4669
+ };
4670
+ const response = await this._client.post(
4671
+ `/payments/payment/${id}/create-recurrence`,
4672
+ {
4673
+ body: JSON.stringify(body),
4674
+ headers: await this.tokenManager.getAuthHeaders()
4675
+ }
4676
+ );
4677
+ if (!response.ok) {
4678
+ throw new core.OperationFailedError(
4679
+ "updateSubscription",
4680
+ this.providerName,
4681
+ {
4682
+ cause: new Error(
4683
+ `[PayKit/GoPay] Failed to create on-demand recurrence charge: ${JSON.stringify(response.error ?? response)}`
4684
+ )
4685
+ }
4686
+ );
4687
+ }
4688
+ const parent = await this.retrieveSubscription(id);
4689
+ if (!parent) {
4690
+ throw new core.OperationFailedError(
4691
+ "updateSubscription",
4692
+ this.providerName,
4693
+ {
4694
+ cause: new Error(
4695
+ "Failed to retrieve subscription after recurrence charge"
4696
+ )
4697
+ }
4698
+ );
4699
+ }
4700
+ return parent;
4701
+ }
4618
4702
  const subscription = await this.retrieveSubscription(id);
4619
4703
  if (!subscription) {
4620
4704
  throw new core.ProviderNotSupportedError(
4621
4705
  "updateSubscription",
4622
4706
  this.providerName,
4623
4707
  {
4624
- reason: "Gopay doesn't support updating subscriptions",
4625
- alternative: "Use the payment API instead and update the subscription manually"
4708
+ reason: "GoPay doesn't support updating subscription fields directly",
4709
+ alternative: "Pass provider_metadata.amount to trigger an on-demand recurrence charge instead"
4626
4710
  }
4627
4711
  );
4628
4712
  }
@@ -4749,8 +4833,11 @@ var GoPayProvider = class extends core.AbstractPayKitProvider {
4749
4833
  // automatically captures the payment
4750
4834
  additional_params: Object.entries({
4751
4835
  ...data.metadata,
4836
+ // Must use the same key ("item", not "itemId") that
4837
+ // Payment$inboundSchema reads back out of additional_params -
4838
+ // createCheckout uses this same key for the same reason.
4752
4839
  [core.PAYKIT_METADATA_KEY]: JSON.stringify({
4753
- itemId: data.item_id,
4840
+ item: data.item_id,
4754
4841
  qty: 1
4755
4842
  })
4756
4843
  }).map(([name, value]) => ({
@@ -4758,25 +4845,11 @@ var GoPayProvider = class extends core.AbstractPayKitProvider {
4758
4845
  value: String(value)
4759
4846
  }))
4760
4847
  };
4761
- const response = await this._client.post(
4762
- "/payments/payment",
4763
- {
4764
- body: JSON.stringify(goPayRequest),
4765
- headers: await this.tokenManager.getAuthHeaders()
4766
- }
4848
+ const responseValue = await this.submitPayment(
4849
+ goPayRequest,
4850
+ "createPayment"
4767
4851
  );
4768
- if (!response.ok) {
4769
- throw new core.OperationFailedError(
4770
- "createPayment",
4771
- this.providerName,
4772
- {
4773
- cause: new Error(
4774
- `Failed to create payment: ${JSON.stringify(response.error ?? response)}`
4775
- )
4776
- }
4777
- );
4778
- }
4779
- return Payment$inboundSchema(response.value);
4852
+ return Payment$inboundSchema(responseValue);
4780
4853
  };
4781
4854
  retrievePayment = async (id) => {
4782
4855
  const response = await this._client.get(
@@ -1,4 +1,4 @@
1
- import { schema, AbstractPayKitProvider, HTTPClient, OAuth2TokenManager, createCheckoutSchema, ValidationError, isEmailCustomer, InvalidTypeError, validateRequiredKeys, PAYKIT_METADATA_KEY, OperationFailedError, ProviderNotSupportedError, createSubscriptionSchema, isIdCustomer, createPaymentSchema, ConfigurationError, createRefundSchema, WebhookError, tryCatchAsync, paykitEvent$InboundSchema, omitInternalMetadata } from '@paykit-sdk/core';
1
+ import { schema, AbstractPayKitProvider, HTTPClient, OAuth2TokenManager, OperationFailedError, createCheckoutSchema, ValidationError, isEmailCustomer, InvalidTypeError, validateRequiredKeys, PAYKIT_METADATA_KEY, ProviderNotSupportedError, createSubscriptionSchema, isIdCustomer, createPaymentSchema, ConfigurationError, createRefundSchema, WebhookError, tryCatchAsync, paykitEvent$InboundSchema, omitInternalMetadata } from '@paykit-sdk/core';
2
2
  import * as crypto from 'crypto';
3
3
 
4
4
  var __defProp = Object.defineProperty;
@@ -4156,7 +4156,7 @@ var Invoice$inboundSchema = (data, isSubscription) => {
4156
4156
  };
4157
4157
  };
4158
4158
  var Subscription$inboundSchema = (data) => {
4159
- const { item } = JSON.parse(
4159
+ const { item, billing_interval: storedBillingInterval } = JSON.parse(
4160
4160
  decodeHtmlEntities(
4161
4161
  data.additional_params?.find(
4162
4162
  (param) => param.name === PAYKIT_METADATA_KEY
@@ -4169,7 +4169,18 @@ var Subscription$inboundSchema = (data) => {
4169
4169
  MONTH: "month",
4170
4170
  ON_DEMAND: "month"
4171
4171
  };
4172
- const billingInterval = billingIntervalMap[data.recurrence?.recurrence_cycle ?? "ON_DEMAND"];
4172
+ const parseStoredBillingInterval = (stored) => {
4173
+ if (typeof stored !== "string") return null;
4174
+ if (stored === "day" || stored === "week" || stored === "month" || stored === "year") {
4175
+ return stored;
4176
+ }
4177
+ const customMatch = /^custom:(\d+)ms$/.exec(stored);
4178
+ if (customMatch) {
4179
+ return { type: "custom", durationMs: Number(customMatch[1]) };
4180
+ }
4181
+ return null;
4182
+ };
4183
+ const billingInterval = parseStoredBillingInterval(storedBillingInterval) ?? billingIntervalMap[data.recurrence?.recurrence_cycle ?? "ON_DEMAND"];
4173
4184
  const recurrencePeriod = data.recurrence?.recurrence_period ?? 1;
4174
4185
  const recurrenceCycle = data.recurrence?.recurrence_cycle;
4175
4186
  const currentPeriodEnd = new Date(
@@ -4291,7 +4302,7 @@ var GoPayProvider = class extends AbstractPayKitProvider {
4291
4302
  });
4292
4303
  }
4293
4304
  providerName = providerName;
4294
- providerVersion = "1.3.0";
4305
+ providerVersion = "1.3.2";
4295
4306
  _client;
4296
4307
  baseUrl;
4297
4308
  isSandbox;
@@ -4299,6 +4310,23 @@ var GoPayProvider = class extends AbstractPayKitProvider {
4299
4310
  get _native() {
4300
4311
  return null;
4301
4312
  }
4313
+ async submitPayment(request, method) {
4314
+ const response = await this._client.post(
4315
+ "/payments/payment",
4316
+ {
4317
+ body: JSON.stringify(request),
4318
+ headers: await this.tokenManager.getAuthHeaders()
4319
+ }
4320
+ );
4321
+ if (!response.ok) {
4322
+ throw new OperationFailedError(method, this.providerName, {
4323
+ cause: new Error(
4324
+ `Failed to submit payment: ${JSON.stringify(response.error ?? response)}`
4325
+ )
4326
+ });
4327
+ }
4328
+ return response.value;
4329
+ }
4302
4330
  createCheckout = async (params) => {
4303
4331
  const { error, data } = createCheckoutSchema.safeParse(params);
4304
4332
  if (error) {
@@ -4321,7 +4349,7 @@ var GoPayProvider = class extends AbstractPayKitProvider {
4321
4349
  }
4322
4350
  const { amount, currency = "CZK" } = validateRequiredKeys(
4323
4351
  ["amount", "currency"],
4324
- data.provider_metadata,
4352
+ data.provider_metadata ?? {},
4325
4353
  "The following fields must be present in the provider_metadata of createCheckout: {keys}"
4326
4354
  );
4327
4355
  if (this.opts.debug) {
@@ -4374,23 +4402,11 @@ var GoPayProvider = class extends AbstractPayKitProvider {
4374
4402
  value: String(value)
4375
4403
  }))
4376
4404
  };
4377
- const response = await this._client.post(
4378
- "/payments/payment",
4379
- {
4380
- body: JSON.stringify(goPayRequest),
4381
- headers: await this.tokenManager.getAuthHeaders()
4382
- }
4405
+ const responseValue = await this.submitPayment(
4406
+ goPayRequest,
4407
+ "createCheckout"
4383
4408
  );
4384
- if (!response.ok) {
4385
- throw new OperationFailedError(
4386
- "createCheckout",
4387
- this.providerName,
4388
- {
4389
- cause: new Error("Failed to create checkout")
4390
- }
4391
- );
4392
- }
4393
- return Checkout$inboundSchema(response.value);
4409
+ return Checkout$inboundSchema(responseValue);
4394
4410
  };
4395
4411
  retrieveCheckout = async (id) => {
4396
4412
  const response = await this._client.get(
@@ -4469,7 +4485,7 @@ var GoPayProvider = class extends AbstractPayKitProvider {
4469
4485
  }
4470
4486
  const { success_url } = validateRequiredKeys(
4471
4487
  ["success_url"],
4472
- data.provider_metadata,
4488
+ data.provider_metadata ?? {},
4473
4489
  "The following fields must be present in the provider_metadata of createSubscription: {keys}"
4474
4490
  );
4475
4491
  const billingInterval = data.billing_interval;
@@ -4486,22 +4502,22 @@ var GoPayProvider = class extends AbstractPayKitProvider {
4486
4502
  if (this.opts.debug) {
4487
4503
  if (isYear) {
4488
4504
  console.info(
4489
- "[PayKit/GoPay] GoPay does not support yearly recurrence. Falling back to ON_DEMAND \u2014 you must trigger each charge manually via createRecurrence(parentPaymentId, ...). See: https://doc.gopay.com/#recurring-on-demand"
4505
+ "[PayKit/GoPay] GoPay does not support yearly recurrence. Falling back to ON_DEMAND \u2014 trigger each charge via paykit.subscriptions.update(id, { provider_metadata: { amount } }). See: https://doc.gopay.com/#recurring-on-demand"
4490
4506
  );
4491
4507
  }
4492
4508
  if (isCustom) {
4493
4509
  const durationMs = billingInterval.durationMs;
4494
4510
  console.info(
4495
- `[PayKit/GoPay] Custom interval (${durationMs}ms) is not supported by GoPay. Falling back to ON_DEMAND \u2014 trigger charges manually via createRecurrence(parentPaymentId, ...). See: https://doc.gopay.com/#recurring-on-demand`
4511
+ `[PayKit/GoPay] Custom interval (${durationMs}ms) is not supported by GoPay. Falling back to ON_DEMAND \u2014 trigger each charge via paykit.subscriptions.update(id, { provider_metadata: { amount } }). See: https://doc.gopay.com/#recurring-on-demand`
4496
4512
  );
4497
4513
  }
4498
4514
  if (!isOnDemand) {
4499
4515
  console.info(
4500
- `[PayKit/GoPay] AUTO recurrence (${recurrenceCycle}) \u2014 GoPay charges automatically each cycle. No createRecurrence() call is needed. Set provider_metadata.end_date (ISO string, e.g. "2027-01-01") to control when the subscription ends. Defaults to 1 year from today.`
4516
+ `[PayKit/GoPay] AUTO recurrence (${recurrenceCycle}) \u2014 GoPay charges automatically each cycle. No manual update() call is needed to collect charges. Set provider_metadata.end_date (ISO string, e.g. "2027-01-01") to control when the subscription ends. Defaults to 1 year from today.`
4501
4517
  );
4502
4518
  } else {
4503
4519
  console.info(
4504
- "[PayKit/GoPay] ON_DEMAND recurrence \u2014 you MUST call createRecurrence(parentPaymentId, ...) for each subsequent charge. GoPay will NOT charge automatically. Set provider_metadata.end_date (ISO string) to control the authorization window. Defaults to 1 year (custom interval) or 5 years (yearly interval)."
4520
+ "[PayKit/GoPay] ON_DEMAND recurrence \u2014 you MUST call paykit.subscriptions.update(id, { provider_metadata: { amount } }) for each subsequent charge. GoPay will NOT charge automatically. Set provider_metadata.end_date (ISO string) to control the authorization window. Defaults to 1 year (custom interval) or 5 years (yearly interval)."
4505
4521
  );
4506
4522
  }
4507
4523
  if (!data.provider_metadata?.description) {
@@ -4554,7 +4570,8 @@ var GoPayProvider = class extends AbstractPayKitProvider {
4554
4570
  {
4555
4571
  name: data.item_id,
4556
4572
  amount: Number(data.amount),
4557
- count: data.quantity ?? 1
4573
+ count: data.quantity ?? 1,
4574
+ type: "ITEM"
4558
4575
  }
4559
4576
  ],
4560
4577
  recurrence,
@@ -4567,7 +4584,9 @@ var GoPayProvider = class extends AbstractPayKitProvider {
4567
4584
  [PAYKIT_METADATA_KEY]: JSON.stringify({
4568
4585
  item: data.item_id,
4569
4586
  qty: data.quantity,
4570
- // Store the original interval so createRecurrence callers know the intended cadence
4587
+ // Store the original interval so Subscription$inboundSchema can
4588
+ // recover it later - GoPay's recurrence_cycle collapses year and
4589
+ // custom intervals down to ON_DEMAND and can't tell them apart.
4571
4590
  billing_interval: isCustom ? `custom:${billingInterval.durationMs}ms` : billingInterval
4572
4591
  })
4573
4592
  }).map(([name, value]) => ({ name, value: String(value) }))
@@ -4592,15 +4611,80 @@ var GoPayProvider = class extends AbstractPayKitProvider {
4592
4611
  }
4593
4612
  return Subscription$inboundSchema(response.value);
4594
4613
  };
4614
+ /**
4615
+ * `updateSubscription` is GoPay's real mechanism for collecting each
4616
+ * charge on an ON_DEMAND recurring mandate - GoPay never charges these
4617
+ * automatically, so every subsequent payment has to be triggered
4618
+ * explicitly via `POST /payments/payment/{id}/create-recurrence`.
4619
+ * Supply the charge via `provider_metadata.amount` (and optionally
4620
+ * `currency`/`order_number`/`order_description`/`items`); `params.metadata`
4621
+ * is stored on that charge the same way createCheckout/createPayment do.
4622
+ *
4623
+ * For AUTO cycles (DAY/WEEK/MONTH), GoPay already charges on its own
4624
+ * schedule and there's nothing to trigger - calling this without
4625
+ * `provider_metadata.amount` just re-fetches the current subscription.
4626
+ *
4627
+ * @see https://doc.gopay.com/#recurring-payments
4628
+ */
4595
4629
  updateSubscription = async (id, params) => {
4630
+ const chargeParams = params.provider_metadata;
4631
+ if (chargeParams?.amount) {
4632
+ const body = {
4633
+ amount: chargeParams.amount,
4634
+ currency: (chargeParams.currency ?? "CZK").toUpperCase(),
4635
+ order_number: chargeParams.order_number ?? crypto.randomBytes(8).toString("hex").slice(0, 15),
4636
+ order_description: chargeParams.order_description ?? `Recurring charge for payment ${id}`,
4637
+ items: chargeParams.items ?? [
4638
+ {
4639
+ name: "recurring_charge",
4640
+ amount: chargeParams.amount,
4641
+ count: 1
4642
+ }
4643
+ ],
4644
+ additional_params: Object.entries(params.metadata ?? {}).map(
4645
+ ([name, value]) => ({ name, value: String(value) })
4646
+ )
4647
+ };
4648
+ const response = await this._client.post(
4649
+ `/payments/payment/${id}/create-recurrence`,
4650
+ {
4651
+ body: JSON.stringify(body),
4652
+ headers: await this.tokenManager.getAuthHeaders()
4653
+ }
4654
+ );
4655
+ if (!response.ok) {
4656
+ throw new OperationFailedError(
4657
+ "updateSubscription",
4658
+ this.providerName,
4659
+ {
4660
+ cause: new Error(
4661
+ `[PayKit/GoPay] Failed to create on-demand recurrence charge: ${JSON.stringify(response.error ?? response)}`
4662
+ )
4663
+ }
4664
+ );
4665
+ }
4666
+ const parent = await this.retrieveSubscription(id);
4667
+ if (!parent) {
4668
+ throw new OperationFailedError(
4669
+ "updateSubscription",
4670
+ this.providerName,
4671
+ {
4672
+ cause: new Error(
4673
+ "Failed to retrieve subscription after recurrence charge"
4674
+ )
4675
+ }
4676
+ );
4677
+ }
4678
+ return parent;
4679
+ }
4596
4680
  const subscription = await this.retrieveSubscription(id);
4597
4681
  if (!subscription) {
4598
4682
  throw new ProviderNotSupportedError(
4599
4683
  "updateSubscription",
4600
4684
  this.providerName,
4601
4685
  {
4602
- reason: "Gopay doesn't support updating subscriptions",
4603
- alternative: "Use the payment API instead and update the subscription manually"
4686
+ reason: "GoPay doesn't support updating subscription fields directly",
4687
+ alternative: "Pass provider_metadata.amount to trigger an on-demand recurrence charge instead"
4604
4688
  }
4605
4689
  );
4606
4690
  }
@@ -4727,8 +4811,11 @@ var GoPayProvider = class extends AbstractPayKitProvider {
4727
4811
  // automatically captures the payment
4728
4812
  additional_params: Object.entries({
4729
4813
  ...data.metadata,
4814
+ // Must use the same key ("item", not "itemId") that
4815
+ // Payment$inboundSchema reads back out of additional_params -
4816
+ // createCheckout uses this same key for the same reason.
4730
4817
  [PAYKIT_METADATA_KEY]: JSON.stringify({
4731
- itemId: data.item_id,
4818
+ item: data.item_id,
4732
4819
  qty: 1
4733
4820
  })
4734
4821
  }).map(([name, value]) => ({
@@ -4736,25 +4823,11 @@ var GoPayProvider = class extends AbstractPayKitProvider {
4736
4823
  value: String(value)
4737
4824
  }))
4738
4825
  };
4739
- const response = await this._client.post(
4740
- "/payments/payment",
4741
- {
4742
- body: JSON.stringify(goPayRequest),
4743
- headers: await this.tokenManager.getAuthHeaders()
4744
- }
4826
+ const responseValue = await this.submitPayment(
4827
+ goPayRequest,
4828
+ "createPayment"
4745
4829
  );
4746
- if (!response.ok) {
4747
- throw new OperationFailedError(
4748
- "createPayment",
4749
- this.providerName,
4750
- {
4751
- cause: new Error(
4752
- `Failed to create payment: ${JSON.stringify(response.error ?? response)}`
4753
- )
4754
- }
4755
- );
4756
- }
4757
- return Payment$inboundSchema(response.value);
4830
+ return Payment$inboundSchema(responseValue);
4758
4831
  };
4759
4832
  retrievePayment = async (id) => {
4760
4833
  const response = await this._client.get(
package/dist/index.js CHANGED
@@ -4178,7 +4178,7 @@ var Invoice$inboundSchema = (data, isSubscription) => {
4178
4178
  };
4179
4179
  };
4180
4180
  var Subscription$inboundSchema = (data) => {
4181
- const { item } = JSON.parse(
4181
+ const { item, billing_interval: storedBillingInterval } = JSON.parse(
4182
4182
  decodeHtmlEntities(
4183
4183
  data.additional_params?.find(
4184
4184
  (param) => param.name === core.PAYKIT_METADATA_KEY
@@ -4191,7 +4191,18 @@ var Subscription$inboundSchema = (data) => {
4191
4191
  MONTH: "month",
4192
4192
  ON_DEMAND: "month"
4193
4193
  };
4194
- const billingInterval = billingIntervalMap[data.recurrence?.recurrence_cycle ?? "ON_DEMAND"];
4194
+ const parseStoredBillingInterval = (stored) => {
4195
+ if (typeof stored !== "string") return null;
4196
+ if (stored === "day" || stored === "week" || stored === "month" || stored === "year") {
4197
+ return stored;
4198
+ }
4199
+ const customMatch = /^custom:(\d+)ms$/.exec(stored);
4200
+ if (customMatch) {
4201
+ return { type: "custom", durationMs: Number(customMatch[1]) };
4202
+ }
4203
+ return null;
4204
+ };
4205
+ const billingInterval = parseStoredBillingInterval(storedBillingInterval) ?? billingIntervalMap[data.recurrence?.recurrence_cycle ?? "ON_DEMAND"];
4195
4206
  const recurrencePeriod = data.recurrence?.recurrence_period ?? 1;
4196
4207
  const recurrenceCycle = data.recurrence?.recurrence_cycle;
4197
4208
  const currentPeriodEnd = new Date(
@@ -4313,7 +4324,7 @@ var GoPayProvider = class extends core.AbstractPayKitProvider {
4313
4324
  });
4314
4325
  }
4315
4326
  providerName = providerName;
4316
- providerVersion = "1.3.0";
4327
+ providerVersion = "1.3.2";
4317
4328
  _client;
4318
4329
  baseUrl;
4319
4330
  isSandbox;
@@ -4321,6 +4332,23 @@ var GoPayProvider = class extends core.AbstractPayKitProvider {
4321
4332
  get _native() {
4322
4333
  return null;
4323
4334
  }
4335
+ async submitPayment(request, method) {
4336
+ const response = await this._client.post(
4337
+ "/payments/payment",
4338
+ {
4339
+ body: JSON.stringify(request),
4340
+ headers: await this.tokenManager.getAuthHeaders()
4341
+ }
4342
+ );
4343
+ if (!response.ok) {
4344
+ throw new core.OperationFailedError(method, this.providerName, {
4345
+ cause: new Error(
4346
+ `Failed to submit payment: ${JSON.stringify(response.error ?? response)}`
4347
+ )
4348
+ });
4349
+ }
4350
+ return response.value;
4351
+ }
4324
4352
  createCheckout = async (params) => {
4325
4353
  const { error, data } = core.createCheckoutSchema.safeParse(params);
4326
4354
  if (error) {
@@ -4343,7 +4371,7 @@ var GoPayProvider = class extends core.AbstractPayKitProvider {
4343
4371
  }
4344
4372
  const { amount, currency = "CZK" } = core.validateRequiredKeys(
4345
4373
  ["amount", "currency"],
4346
- data.provider_metadata,
4374
+ data.provider_metadata ?? {},
4347
4375
  "The following fields must be present in the provider_metadata of createCheckout: {keys}"
4348
4376
  );
4349
4377
  if (this.opts.debug) {
@@ -4396,23 +4424,11 @@ var GoPayProvider = class extends core.AbstractPayKitProvider {
4396
4424
  value: String(value)
4397
4425
  }))
4398
4426
  };
4399
- const response = await this._client.post(
4400
- "/payments/payment",
4401
- {
4402
- body: JSON.stringify(goPayRequest),
4403
- headers: await this.tokenManager.getAuthHeaders()
4404
- }
4427
+ const responseValue = await this.submitPayment(
4428
+ goPayRequest,
4429
+ "createCheckout"
4405
4430
  );
4406
- if (!response.ok) {
4407
- throw new core.OperationFailedError(
4408
- "createCheckout",
4409
- this.providerName,
4410
- {
4411
- cause: new Error("Failed to create checkout")
4412
- }
4413
- );
4414
- }
4415
- return Checkout$inboundSchema(response.value);
4431
+ return Checkout$inboundSchema(responseValue);
4416
4432
  };
4417
4433
  retrieveCheckout = async (id) => {
4418
4434
  const response = await this._client.get(
@@ -4491,7 +4507,7 @@ var GoPayProvider = class extends core.AbstractPayKitProvider {
4491
4507
  }
4492
4508
  const { success_url } = core.validateRequiredKeys(
4493
4509
  ["success_url"],
4494
- data.provider_metadata,
4510
+ data.provider_metadata ?? {},
4495
4511
  "The following fields must be present in the provider_metadata of createSubscription: {keys}"
4496
4512
  );
4497
4513
  const billingInterval = data.billing_interval;
@@ -4508,22 +4524,22 @@ var GoPayProvider = class extends core.AbstractPayKitProvider {
4508
4524
  if (this.opts.debug) {
4509
4525
  if (isYear) {
4510
4526
  console.info(
4511
- "[PayKit/GoPay] GoPay does not support yearly recurrence. Falling back to ON_DEMAND \u2014 you must trigger each charge manually via createRecurrence(parentPaymentId, ...). See: https://doc.gopay.com/#recurring-on-demand"
4527
+ "[PayKit/GoPay] GoPay does not support yearly recurrence. Falling back to ON_DEMAND \u2014 trigger each charge via paykit.subscriptions.update(id, { provider_metadata: { amount } }). See: https://doc.gopay.com/#recurring-on-demand"
4512
4528
  );
4513
4529
  }
4514
4530
  if (isCustom) {
4515
4531
  const durationMs = billingInterval.durationMs;
4516
4532
  console.info(
4517
- `[PayKit/GoPay] Custom interval (${durationMs}ms) is not supported by GoPay. Falling back to ON_DEMAND \u2014 trigger charges manually via createRecurrence(parentPaymentId, ...). See: https://doc.gopay.com/#recurring-on-demand`
4533
+ `[PayKit/GoPay] Custom interval (${durationMs}ms) is not supported by GoPay. Falling back to ON_DEMAND \u2014 trigger each charge via paykit.subscriptions.update(id, { provider_metadata: { amount } }). See: https://doc.gopay.com/#recurring-on-demand`
4518
4534
  );
4519
4535
  }
4520
4536
  if (!isOnDemand) {
4521
4537
  console.info(
4522
- `[PayKit/GoPay] AUTO recurrence (${recurrenceCycle}) \u2014 GoPay charges automatically each cycle. No createRecurrence() call is needed. Set provider_metadata.end_date (ISO string, e.g. "2027-01-01") to control when the subscription ends. Defaults to 1 year from today.`
4538
+ `[PayKit/GoPay] AUTO recurrence (${recurrenceCycle}) \u2014 GoPay charges automatically each cycle. No manual update() call is needed to collect charges. Set provider_metadata.end_date (ISO string, e.g. "2027-01-01") to control when the subscription ends. Defaults to 1 year from today.`
4523
4539
  );
4524
4540
  } else {
4525
4541
  console.info(
4526
- "[PayKit/GoPay] ON_DEMAND recurrence \u2014 you MUST call createRecurrence(parentPaymentId, ...) for each subsequent charge. GoPay will NOT charge automatically. Set provider_metadata.end_date (ISO string) to control the authorization window. Defaults to 1 year (custom interval) or 5 years (yearly interval)."
4542
+ "[PayKit/GoPay] ON_DEMAND recurrence \u2014 you MUST call paykit.subscriptions.update(id, { provider_metadata: { amount } }) for each subsequent charge. GoPay will NOT charge automatically. Set provider_metadata.end_date (ISO string) to control the authorization window. Defaults to 1 year (custom interval) or 5 years (yearly interval)."
4527
4543
  );
4528
4544
  }
4529
4545
  if (!data.provider_metadata?.description) {
@@ -4576,7 +4592,8 @@ var GoPayProvider = class extends core.AbstractPayKitProvider {
4576
4592
  {
4577
4593
  name: data.item_id,
4578
4594
  amount: Number(data.amount),
4579
- count: data.quantity ?? 1
4595
+ count: data.quantity ?? 1,
4596
+ type: "ITEM"
4580
4597
  }
4581
4598
  ],
4582
4599
  recurrence,
@@ -4589,7 +4606,9 @@ var GoPayProvider = class extends core.AbstractPayKitProvider {
4589
4606
  [core.PAYKIT_METADATA_KEY]: JSON.stringify({
4590
4607
  item: data.item_id,
4591
4608
  qty: data.quantity,
4592
- // Store the original interval so createRecurrence callers know the intended cadence
4609
+ // Store the original interval so Subscription$inboundSchema can
4610
+ // recover it later - GoPay's recurrence_cycle collapses year and
4611
+ // custom intervals down to ON_DEMAND and can't tell them apart.
4593
4612
  billing_interval: isCustom ? `custom:${billingInterval.durationMs}ms` : billingInterval
4594
4613
  })
4595
4614
  }).map(([name, value]) => ({ name, value: String(value) }))
@@ -4614,15 +4633,80 @@ var GoPayProvider = class extends core.AbstractPayKitProvider {
4614
4633
  }
4615
4634
  return Subscription$inboundSchema(response.value);
4616
4635
  };
4636
+ /**
4637
+ * `updateSubscription` is GoPay's real mechanism for collecting each
4638
+ * charge on an ON_DEMAND recurring mandate - GoPay never charges these
4639
+ * automatically, so every subsequent payment has to be triggered
4640
+ * explicitly via `POST /payments/payment/{id}/create-recurrence`.
4641
+ * Supply the charge via `provider_metadata.amount` (and optionally
4642
+ * `currency`/`order_number`/`order_description`/`items`); `params.metadata`
4643
+ * is stored on that charge the same way createCheckout/createPayment do.
4644
+ *
4645
+ * For AUTO cycles (DAY/WEEK/MONTH), GoPay already charges on its own
4646
+ * schedule and there's nothing to trigger - calling this without
4647
+ * `provider_metadata.amount` just re-fetches the current subscription.
4648
+ *
4649
+ * @see https://doc.gopay.com/#recurring-payments
4650
+ */
4617
4651
  updateSubscription = async (id, params) => {
4652
+ const chargeParams = params.provider_metadata;
4653
+ if (chargeParams?.amount) {
4654
+ const body = {
4655
+ amount: chargeParams.amount,
4656
+ currency: (chargeParams.currency ?? "CZK").toUpperCase(),
4657
+ order_number: chargeParams.order_number ?? crypto__namespace.randomBytes(8).toString("hex").slice(0, 15),
4658
+ order_description: chargeParams.order_description ?? `Recurring charge for payment ${id}`,
4659
+ items: chargeParams.items ?? [
4660
+ {
4661
+ name: "recurring_charge",
4662
+ amount: chargeParams.amount,
4663
+ count: 1
4664
+ }
4665
+ ],
4666
+ additional_params: Object.entries(params.metadata ?? {}).map(
4667
+ ([name, value]) => ({ name, value: String(value) })
4668
+ )
4669
+ };
4670
+ const response = await this._client.post(
4671
+ `/payments/payment/${id}/create-recurrence`,
4672
+ {
4673
+ body: JSON.stringify(body),
4674
+ headers: await this.tokenManager.getAuthHeaders()
4675
+ }
4676
+ );
4677
+ if (!response.ok) {
4678
+ throw new core.OperationFailedError(
4679
+ "updateSubscription",
4680
+ this.providerName,
4681
+ {
4682
+ cause: new Error(
4683
+ `[PayKit/GoPay] Failed to create on-demand recurrence charge: ${JSON.stringify(response.error ?? response)}`
4684
+ )
4685
+ }
4686
+ );
4687
+ }
4688
+ const parent = await this.retrieveSubscription(id);
4689
+ if (!parent) {
4690
+ throw new core.OperationFailedError(
4691
+ "updateSubscription",
4692
+ this.providerName,
4693
+ {
4694
+ cause: new Error(
4695
+ "Failed to retrieve subscription after recurrence charge"
4696
+ )
4697
+ }
4698
+ );
4699
+ }
4700
+ return parent;
4701
+ }
4618
4702
  const subscription = await this.retrieveSubscription(id);
4619
4703
  if (!subscription) {
4620
4704
  throw new core.ProviderNotSupportedError(
4621
4705
  "updateSubscription",
4622
4706
  this.providerName,
4623
4707
  {
4624
- reason: "Gopay doesn't support updating subscriptions",
4625
- alternative: "Use the payment API instead and update the subscription manually"
4708
+ reason: "GoPay doesn't support updating subscription fields directly",
4709
+ alternative: "Pass provider_metadata.amount to trigger an on-demand recurrence charge instead"
4626
4710
  }
4627
4711
  );
4628
4712
  }
@@ -4749,8 +4833,11 @@ var GoPayProvider = class extends core.AbstractPayKitProvider {
4749
4833
  // automatically captures the payment
4750
4834
  additional_params: Object.entries({
4751
4835
  ...data.metadata,
4836
+ // Must use the same key ("item", not "itemId") that
4837
+ // Payment$inboundSchema reads back out of additional_params -
4838
+ // createCheckout uses this same key for the same reason.
4752
4839
  [core.PAYKIT_METADATA_KEY]: JSON.stringify({
4753
- itemId: data.item_id,
4840
+ item: data.item_id,
4754
4841
  qty: 1
4755
4842
  })
4756
4843
  }).map(([name, value]) => ({
@@ -4758,25 +4845,11 @@ var GoPayProvider = class extends core.AbstractPayKitProvider {
4758
4845
  value: String(value)
4759
4846
  }))
4760
4847
  };
4761
- const response = await this._client.post(
4762
- "/payments/payment",
4763
- {
4764
- body: JSON.stringify(goPayRequest),
4765
- headers: await this.tokenManager.getAuthHeaders()
4766
- }
4848
+ const responseValue = await this.submitPayment(
4849
+ goPayRequest,
4850
+ "createPayment"
4767
4851
  );
4768
- if (!response.ok) {
4769
- throw new core.OperationFailedError(
4770
- "createPayment",
4771
- this.providerName,
4772
- {
4773
- cause: new Error(
4774
- `Failed to create payment: ${JSON.stringify(response.error ?? response)}`
4775
- )
4776
- }
4777
- );
4778
- }
4779
- return Payment$inboundSchema(response.value);
4852
+ return Payment$inboundSchema(responseValue);
4780
4853
  };
4781
4854
  retrievePayment = async (id) => {
4782
4855
  const response = await this._client.get(
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { schema, validateRequiredKeys, AbstractPayKitProvider, HTTPClient, OAuth2TokenManager, createCheckoutSchema, ValidationError, isEmailCustomer, InvalidTypeError, PAYKIT_METADATA_KEY, OperationFailedError, ProviderNotSupportedError, createSubscriptionSchema, isIdCustomer, createPaymentSchema, ConfigurationError, createRefundSchema, WebhookError, tryCatchAsync, paykitEvent$InboundSchema, omitInternalMetadata } from '@paykit-sdk/core';
1
+ import { schema, validateRequiredKeys, AbstractPayKitProvider, HTTPClient, OAuth2TokenManager, OperationFailedError, createCheckoutSchema, ValidationError, isEmailCustomer, InvalidTypeError, PAYKIT_METADATA_KEY, ProviderNotSupportedError, createSubscriptionSchema, isIdCustomer, createPaymentSchema, ConfigurationError, createRefundSchema, WebhookError, tryCatchAsync, paykitEvent$InboundSchema, omitInternalMetadata } from '@paykit-sdk/core';
2
2
  import * as crypto from 'crypto';
3
3
 
4
4
  var __defProp = Object.defineProperty;
@@ -4156,7 +4156,7 @@ var Invoice$inboundSchema = (data, isSubscription) => {
4156
4156
  };
4157
4157
  };
4158
4158
  var Subscription$inboundSchema = (data) => {
4159
- const { item } = JSON.parse(
4159
+ const { item, billing_interval: storedBillingInterval } = JSON.parse(
4160
4160
  decodeHtmlEntities(
4161
4161
  data.additional_params?.find(
4162
4162
  (param) => param.name === PAYKIT_METADATA_KEY
@@ -4169,7 +4169,18 @@ var Subscription$inboundSchema = (data) => {
4169
4169
  MONTH: "month",
4170
4170
  ON_DEMAND: "month"
4171
4171
  };
4172
- const billingInterval = billingIntervalMap[data.recurrence?.recurrence_cycle ?? "ON_DEMAND"];
4172
+ const parseStoredBillingInterval = (stored) => {
4173
+ if (typeof stored !== "string") return null;
4174
+ if (stored === "day" || stored === "week" || stored === "month" || stored === "year") {
4175
+ return stored;
4176
+ }
4177
+ const customMatch = /^custom:(\d+)ms$/.exec(stored);
4178
+ if (customMatch) {
4179
+ return { type: "custom", durationMs: Number(customMatch[1]) };
4180
+ }
4181
+ return null;
4182
+ };
4183
+ const billingInterval = parseStoredBillingInterval(storedBillingInterval) ?? billingIntervalMap[data.recurrence?.recurrence_cycle ?? "ON_DEMAND"];
4173
4184
  const recurrencePeriod = data.recurrence?.recurrence_period ?? 1;
4174
4185
  const recurrenceCycle = data.recurrence?.recurrence_cycle;
4175
4186
  const currentPeriodEnd = new Date(
@@ -4291,7 +4302,7 @@ var GoPayProvider = class extends AbstractPayKitProvider {
4291
4302
  });
4292
4303
  }
4293
4304
  providerName = providerName;
4294
- providerVersion = "1.3.0";
4305
+ providerVersion = "1.3.2";
4295
4306
  _client;
4296
4307
  baseUrl;
4297
4308
  isSandbox;
@@ -4299,6 +4310,23 @@ var GoPayProvider = class extends AbstractPayKitProvider {
4299
4310
  get _native() {
4300
4311
  return null;
4301
4312
  }
4313
+ async submitPayment(request, method) {
4314
+ const response = await this._client.post(
4315
+ "/payments/payment",
4316
+ {
4317
+ body: JSON.stringify(request),
4318
+ headers: await this.tokenManager.getAuthHeaders()
4319
+ }
4320
+ );
4321
+ if (!response.ok) {
4322
+ throw new OperationFailedError(method, this.providerName, {
4323
+ cause: new Error(
4324
+ `Failed to submit payment: ${JSON.stringify(response.error ?? response)}`
4325
+ )
4326
+ });
4327
+ }
4328
+ return response.value;
4329
+ }
4302
4330
  createCheckout = async (params) => {
4303
4331
  const { error, data } = createCheckoutSchema.safeParse(params);
4304
4332
  if (error) {
@@ -4321,7 +4349,7 @@ var GoPayProvider = class extends AbstractPayKitProvider {
4321
4349
  }
4322
4350
  const { amount, currency = "CZK" } = validateRequiredKeys(
4323
4351
  ["amount", "currency"],
4324
- data.provider_metadata,
4352
+ data.provider_metadata ?? {},
4325
4353
  "The following fields must be present in the provider_metadata of createCheckout: {keys}"
4326
4354
  );
4327
4355
  if (this.opts.debug) {
@@ -4374,23 +4402,11 @@ var GoPayProvider = class extends AbstractPayKitProvider {
4374
4402
  value: String(value)
4375
4403
  }))
4376
4404
  };
4377
- const response = await this._client.post(
4378
- "/payments/payment",
4379
- {
4380
- body: JSON.stringify(goPayRequest),
4381
- headers: await this.tokenManager.getAuthHeaders()
4382
- }
4405
+ const responseValue = await this.submitPayment(
4406
+ goPayRequest,
4407
+ "createCheckout"
4383
4408
  );
4384
- if (!response.ok) {
4385
- throw new OperationFailedError(
4386
- "createCheckout",
4387
- this.providerName,
4388
- {
4389
- cause: new Error("Failed to create checkout")
4390
- }
4391
- );
4392
- }
4393
- return Checkout$inboundSchema(response.value);
4409
+ return Checkout$inboundSchema(responseValue);
4394
4410
  };
4395
4411
  retrieveCheckout = async (id) => {
4396
4412
  const response = await this._client.get(
@@ -4469,7 +4485,7 @@ var GoPayProvider = class extends AbstractPayKitProvider {
4469
4485
  }
4470
4486
  const { success_url } = validateRequiredKeys(
4471
4487
  ["success_url"],
4472
- data.provider_metadata,
4488
+ data.provider_metadata ?? {},
4473
4489
  "The following fields must be present in the provider_metadata of createSubscription: {keys}"
4474
4490
  );
4475
4491
  const billingInterval = data.billing_interval;
@@ -4486,22 +4502,22 @@ var GoPayProvider = class extends AbstractPayKitProvider {
4486
4502
  if (this.opts.debug) {
4487
4503
  if (isYear) {
4488
4504
  console.info(
4489
- "[PayKit/GoPay] GoPay does not support yearly recurrence. Falling back to ON_DEMAND \u2014 you must trigger each charge manually via createRecurrence(parentPaymentId, ...). See: https://doc.gopay.com/#recurring-on-demand"
4505
+ "[PayKit/GoPay] GoPay does not support yearly recurrence. Falling back to ON_DEMAND \u2014 trigger each charge via paykit.subscriptions.update(id, { provider_metadata: { amount } }). See: https://doc.gopay.com/#recurring-on-demand"
4490
4506
  );
4491
4507
  }
4492
4508
  if (isCustom) {
4493
4509
  const durationMs = billingInterval.durationMs;
4494
4510
  console.info(
4495
- `[PayKit/GoPay] Custom interval (${durationMs}ms) is not supported by GoPay. Falling back to ON_DEMAND \u2014 trigger charges manually via createRecurrence(parentPaymentId, ...). See: https://doc.gopay.com/#recurring-on-demand`
4511
+ `[PayKit/GoPay] Custom interval (${durationMs}ms) is not supported by GoPay. Falling back to ON_DEMAND \u2014 trigger each charge via paykit.subscriptions.update(id, { provider_metadata: { amount } }). See: https://doc.gopay.com/#recurring-on-demand`
4496
4512
  );
4497
4513
  }
4498
4514
  if (!isOnDemand) {
4499
4515
  console.info(
4500
- `[PayKit/GoPay] AUTO recurrence (${recurrenceCycle}) \u2014 GoPay charges automatically each cycle. No createRecurrence() call is needed. Set provider_metadata.end_date (ISO string, e.g. "2027-01-01") to control when the subscription ends. Defaults to 1 year from today.`
4516
+ `[PayKit/GoPay] AUTO recurrence (${recurrenceCycle}) \u2014 GoPay charges automatically each cycle. No manual update() call is needed to collect charges. Set provider_metadata.end_date (ISO string, e.g. "2027-01-01") to control when the subscription ends. Defaults to 1 year from today.`
4501
4517
  );
4502
4518
  } else {
4503
4519
  console.info(
4504
- "[PayKit/GoPay] ON_DEMAND recurrence \u2014 you MUST call createRecurrence(parentPaymentId, ...) for each subsequent charge. GoPay will NOT charge automatically. Set provider_metadata.end_date (ISO string) to control the authorization window. Defaults to 1 year (custom interval) or 5 years (yearly interval)."
4520
+ "[PayKit/GoPay] ON_DEMAND recurrence \u2014 you MUST call paykit.subscriptions.update(id, { provider_metadata: { amount } }) for each subsequent charge. GoPay will NOT charge automatically. Set provider_metadata.end_date (ISO string) to control the authorization window. Defaults to 1 year (custom interval) or 5 years (yearly interval)."
4505
4521
  );
4506
4522
  }
4507
4523
  if (!data.provider_metadata?.description) {
@@ -4554,7 +4570,8 @@ var GoPayProvider = class extends AbstractPayKitProvider {
4554
4570
  {
4555
4571
  name: data.item_id,
4556
4572
  amount: Number(data.amount),
4557
- count: data.quantity ?? 1
4573
+ count: data.quantity ?? 1,
4574
+ type: "ITEM"
4558
4575
  }
4559
4576
  ],
4560
4577
  recurrence,
@@ -4567,7 +4584,9 @@ var GoPayProvider = class extends AbstractPayKitProvider {
4567
4584
  [PAYKIT_METADATA_KEY]: JSON.stringify({
4568
4585
  item: data.item_id,
4569
4586
  qty: data.quantity,
4570
- // Store the original interval so createRecurrence callers know the intended cadence
4587
+ // Store the original interval so Subscription$inboundSchema can
4588
+ // recover it later - GoPay's recurrence_cycle collapses year and
4589
+ // custom intervals down to ON_DEMAND and can't tell them apart.
4571
4590
  billing_interval: isCustom ? `custom:${billingInterval.durationMs}ms` : billingInterval
4572
4591
  })
4573
4592
  }).map(([name, value]) => ({ name, value: String(value) }))
@@ -4592,15 +4611,80 @@ var GoPayProvider = class extends AbstractPayKitProvider {
4592
4611
  }
4593
4612
  return Subscription$inboundSchema(response.value);
4594
4613
  };
4614
+ /**
4615
+ * `updateSubscription` is GoPay's real mechanism for collecting each
4616
+ * charge on an ON_DEMAND recurring mandate - GoPay never charges these
4617
+ * automatically, so every subsequent payment has to be triggered
4618
+ * explicitly via `POST /payments/payment/{id}/create-recurrence`.
4619
+ * Supply the charge via `provider_metadata.amount` (and optionally
4620
+ * `currency`/`order_number`/`order_description`/`items`); `params.metadata`
4621
+ * is stored on that charge the same way createCheckout/createPayment do.
4622
+ *
4623
+ * For AUTO cycles (DAY/WEEK/MONTH), GoPay already charges on its own
4624
+ * schedule and there's nothing to trigger - calling this without
4625
+ * `provider_metadata.amount` just re-fetches the current subscription.
4626
+ *
4627
+ * @see https://doc.gopay.com/#recurring-payments
4628
+ */
4595
4629
  updateSubscription = async (id, params) => {
4630
+ const chargeParams = params.provider_metadata;
4631
+ if (chargeParams?.amount) {
4632
+ const body = {
4633
+ amount: chargeParams.amount,
4634
+ currency: (chargeParams.currency ?? "CZK").toUpperCase(),
4635
+ order_number: chargeParams.order_number ?? crypto.randomBytes(8).toString("hex").slice(0, 15),
4636
+ order_description: chargeParams.order_description ?? `Recurring charge for payment ${id}`,
4637
+ items: chargeParams.items ?? [
4638
+ {
4639
+ name: "recurring_charge",
4640
+ amount: chargeParams.amount,
4641
+ count: 1
4642
+ }
4643
+ ],
4644
+ additional_params: Object.entries(params.metadata ?? {}).map(
4645
+ ([name, value]) => ({ name, value: String(value) })
4646
+ )
4647
+ };
4648
+ const response = await this._client.post(
4649
+ `/payments/payment/${id}/create-recurrence`,
4650
+ {
4651
+ body: JSON.stringify(body),
4652
+ headers: await this.tokenManager.getAuthHeaders()
4653
+ }
4654
+ );
4655
+ if (!response.ok) {
4656
+ throw new OperationFailedError(
4657
+ "updateSubscription",
4658
+ this.providerName,
4659
+ {
4660
+ cause: new Error(
4661
+ `[PayKit/GoPay] Failed to create on-demand recurrence charge: ${JSON.stringify(response.error ?? response)}`
4662
+ )
4663
+ }
4664
+ );
4665
+ }
4666
+ const parent = await this.retrieveSubscription(id);
4667
+ if (!parent) {
4668
+ throw new OperationFailedError(
4669
+ "updateSubscription",
4670
+ this.providerName,
4671
+ {
4672
+ cause: new Error(
4673
+ "Failed to retrieve subscription after recurrence charge"
4674
+ )
4675
+ }
4676
+ );
4677
+ }
4678
+ return parent;
4679
+ }
4596
4680
  const subscription = await this.retrieveSubscription(id);
4597
4681
  if (!subscription) {
4598
4682
  throw new ProviderNotSupportedError(
4599
4683
  "updateSubscription",
4600
4684
  this.providerName,
4601
4685
  {
4602
- reason: "Gopay doesn't support updating subscriptions",
4603
- alternative: "Use the payment API instead and update the subscription manually"
4686
+ reason: "GoPay doesn't support updating subscription fields directly",
4687
+ alternative: "Pass provider_metadata.amount to trigger an on-demand recurrence charge instead"
4604
4688
  }
4605
4689
  );
4606
4690
  }
@@ -4727,8 +4811,11 @@ var GoPayProvider = class extends AbstractPayKitProvider {
4727
4811
  // automatically captures the payment
4728
4812
  additional_params: Object.entries({
4729
4813
  ...data.metadata,
4814
+ // Must use the same key ("item", not "itemId") that
4815
+ // Payment$inboundSchema reads back out of additional_params -
4816
+ // createCheckout uses this same key for the same reason.
4730
4817
  [PAYKIT_METADATA_KEY]: JSON.stringify({
4731
- itemId: data.item_id,
4818
+ item: data.item_id,
4732
4819
  qty: 1
4733
4820
  })
4734
4821
  }).map(([name, value]) => ({
@@ -4736,25 +4823,11 @@ var GoPayProvider = class extends AbstractPayKitProvider {
4736
4823
  value: String(value)
4737
4824
  }))
4738
4825
  };
4739
- const response = await this._client.post(
4740
- "/payments/payment",
4741
- {
4742
- body: JSON.stringify(goPayRequest),
4743
- headers: await this.tokenManager.getAuthHeaders()
4744
- }
4826
+ const responseValue = await this.submitPayment(
4827
+ goPayRequest,
4828
+ "createPayment"
4745
4829
  );
4746
- if (!response.ok) {
4747
- throw new OperationFailedError(
4748
- "createPayment",
4749
- this.providerName,
4750
- {
4751
- cause: new Error(
4752
- `Failed to create payment: ${JSON.stringify(response.error ?? response)}`
4753
- )
4754
- }
4755
- );
4756
- }
4757
- return Payment$inboundSchema(response.value);
4830
+ return Payment$inboundSchema(responseValue);
4758
4831
  };
4759
4832
  retrievePayment = async (id) => {
4760
4833
  const response = await this._client.get(
@@ -113,7 +113,7 @@ var Invoice$inboundSchema = (data, isSubscription) => {
113
113
  };
114
114
  };
115
115
  var Subscription$inboundSchema = (data) => {
116
- const { item } = JSON.parse(
116
+ const { item, billing_interval: storedBillingInterval } = JSON.parse(
117
117
  decodeHtmlEntities(
118
118
  data.additional_params?.find(
119
119
  (param) => param.name === core.PAYKIT_METADATA_KEY
@@ -126,7 +126,18 @@ var Subscription$inboundSchema = (data) => {
126
126
  MONTH: "month",
127
127
  ON_DEMAND: "month"
128
128
  };
129
- const billingInterval = billingIntervalMap[data.recurrence?.recurrence_cycle ?? "ON_DEMAND"];
129
+ const parseStoredBillingInterval = (stored) => {
130
+ if (typeof stored !== "string") return null;
131
+ if (stored === "day" || stored === "week" || stored === "month" || stored === "year") {
132
+ return stored;
133
+ }
134
+ const customMatch = /^custom:(\d+)ms$/.exec(stored);
135
+ if (customMatch) {
136
+ return { type: "custom", durationMs: Number(customMatch[1]) };
137
+ }
138
+ return null;
139
+ };
140
+ const billingInterval = parseStoredBillingInterval(storedBillingInterval) ?? billingIntervalMap[data.recurrence?.recurrence_cycle ?? "ON_DEMAND"];
130
141
  const recurrencePeriod = data.recurrence?.recurrence_period ?? 1;
131
142
  const recurrenceCycle = data.recurrence?.recurrence_cycle;
132
143
  const currentPeriodEnd = new Date(
@@ -111,7 +111,7 @@ var Invoice$inboundSchema = (data, isSubscription) => {
111
111
  };
112
112
  };
113
113
  var Subscription$inboundSchema = (data) => {
114
- const { item } = JSON.parse(
114
+ const { item, billing_interval: storedBillingInterval } = JSON.parse(
115
115
  decodeHtmlEntities(
116
116
  data.additional_params?.find(
117
117
  (param) => param.name === PAYKIT_METADATA_KEY
@@ -124,7 +124,18 @@ var Subscription$inboundSchema = (data) => {
124
124
  MONTH: "month",
125
125
  ON_DEMAND: "month"
126
126
  };
127
- const billingInterval = billingIntervalMap[data.recurrence?.recurrence_cycle ?? "ON_DEMAND"];
127
+ const parseStoredBillingInterval = (stored) => {
128
+ if (typeof stored !== "string") return null;
129
+ if (stored === "day" || stored === "week" || stored === "month" || stored === "year") {
130
+ return stored;
131
+ }
132
+ const customMatch = /^custom:(\d+)ms$/.exec(stored);
133
+ if (customMatch) {
134
+ return { type: "custom", durationMs: Number(customMatch[1]) };
135
+ }
136
+ return null;
137
+ };
138
+ const billingInterval = parseStoredBillingInterval(storedBillingInterval) ?? billingIntervalMap[data.recurrence?.recurrence_cycle ?? "ON_DEMAND"];
128
139
  const recurrencePeriod = data.recurrence?.recurrence_period ?? 1;
129
140
  const recurrenceCycle = data.recurrence?.recurrence_cycle;
130
141
  const currentPeriodEnd = new Date(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@paykit-sdk/gopay",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
4
4
  "description": "GoPay provider for PayKit",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -31,7 +31,7 @@
31
31
  "tsup": "^8.5.0",
32
32
  "typescript": "^5.9.2",
33
33
  "zod": "^3.24.2",
34
- "@paykit-sdk/core": "1.3.0"
34
+ "@paykit-sdk/core": "1.3.2"
35
35
  },
36
36
  "publishConfig": {
37
37
  "access": "public"