warpmetal 0.7.5 → 0.7.7

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
@@ -34,7 +34,7 @@ published wallet CLI with the exact version WarpMetal reports:
34
34
  npm install --global warpmetal
35
35
  warpmetal --help
36
36
 
37
- npm install --global @x402api/agent-wallet-cli@0.2.6
37
+ npm install --global @x402api/agent-wallet-cli@0.2.7
38
38
  x402api help --json
39
39
  ```
40
40
 
@@ -58,7 +58,7 @@ integration.
58
58
 
59
59
  The repository also contains a skills-only WarpMetal plugin for the public
60
60
  Plugins Directory shared by Codex and ChatGPT. The plugin remains a separate
61
- artifact from the npm CLI and requires `warpmetal` CLI version 0.7.5 or newer.
61
+ artifact from the npm CLI and requires `warpmetal` CLI version 0.7.7 or newer.
62
62
 
63
63
  To test the repository marketplace after the plugin lands on `main`:
64
64
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "warpmetal",
3
- "version": "0.7.5",
3
+ "version": "0.7.7",
4
4
  "description": "Agent-safe CLI and skill for purchasing, renewing, and managing WarpMetal VPS servers",
5
5
  "type": "module",
6
6
  "bin": {
@@ -11,7 +11,7 @@ with ad hoc HTTP commands.
11
11
 
12
12
  ## Start safely
13
13
 
14
- 1. Run `warpmetal --version` and require version `0.7.5` or newer for this
14
+ 1. Run `warpmetal --version` and require version `0.7.7` or newer for this
15
15
  plugin. If it is missing or older, explain the compatibility requirement,
16
16
  ask before installing or upgrading software, and use only the official npm
17
17
  package from `https://www.npmjs.com/package/warpmetal`.
@@ -136,12 +136,22 @@ WarpMetal verifies that the artifact matches the exact saved request and an
136
136
  advertised sponsored requirement, gas reservation, resource, extensions, and
137
137
  buyer payment identifier before sending its signature. The legacy
138
138
  `--payment-signature-file` input remains available for another compatible
139
- external signer. If a signed request returns a replacement challenge, use the
140
- new CLI-produced workflow and re-evaluate payment authority. Ask again when a
141
- human is chatting and the terms changed; in an unattended run, proceed only if
142
- the replacement remains within standing authority. On `manual_review` or an
139
+ external signer. If a signed request returns `payment_rejected`, preserve its
140
+ safe `errorCode` and `requestId` diagnostics and inspect `replacementAllowed`.
141
+ When it is true, run the unsigned challenge flow again and re-evaluate payment
142
+ authority before creating the one permitted replacement authorization. When it
143
+ is false or absent, stop without signing again. On `manual_review` or an
143
144
  ambiguous attempt, stop and never create another payment.
144
145
 
146
+ WarpMetal continues read-only backend reconciliation for
147
+ `manual_review/stale_payment_outcome`; this is not a client bypass. Re-read the
148
+ task on a later autonomous cycle. Only when it becomes `expired` with
149
+ `failure.code` equal to `payment_expired_unsettled`, `paidAt` null, and
150
+ `retrySafe` true has WarpMetal proved that the prior charge expired unpaid. An
151
+ autonomous workflow may then prepare a new order if the original intent and
152
+ standing authority still apply. Never reuse the old signature or infer safety
153
+ from wallet balance, timeout, `paidAt`, `expired`, or `manual_review` alone.
154
+
145
155
  ## Renew autonomously within policy
146
156
 
147
157
  Configure a bounded server policy before unattended renewal. Require a
@@ -178,6 +188,11 @@ Poll a prepared or paid order with:
178
188
  warpmetal order status --task <taskId> --wait --json
179
189
  ```
180
190
 
191
+ `manual_review` remains terminal for payment and mutation attempts. A later
192
+ read-only status check may observe the backend's authoritative reconciliation;
193
+ prepare a replacement only for the exact `retrySafe: true` result described
194
+ above.
195
+
181
196
  After initial provisioning and every OS reload, WarpMetal reapplies the same
182
197
  key-only policy: `PasswordAuthentication no`,
183
198
  `KbdInteractiveAuthentication no`, `PermitRootLogin prohibit-password`, and
@@ -69,7 +69,7 @@ retire that attempt and return a new `paymentAttemptId`. The CLI replaces the
69
69
  saved challenge and stale wallet-attempt metadata; use only the newly returned
70
70
  workflow.
71
71
 
72
- The current integration targets `@x402api/agent-wallet-cli@0.2.6`. A compatible
72
+ The current integration targets `@x402api/agent-wallet-cli@0.2.7`. A compatible
73
73
  live term is marked `agentWalletSupported: true` and must use the sponsored
74
74
  Base USDC or Solana USDC/USDT launch profile with buyer native fees disabled.
75
75
  For the current declaration, x402api pays actual gas from its platform treasury
@@ -25,7 +25,7 @@ contract.
25
25
  WarpMetal runs on Node.js 20 or 22. The x402api Agent Wallet currently requires
26
26
  Node.js 22. Use the exact published package reported by
27
27
  `paymentWorkflow.signerPackage.spec`; the current contract is
28
- `@x402api/agent-wallet-cli@0.2.6`. Do not add it as a WarpMetal dependency,
28
+ `@x402api/agent-wallet-cli@0.2.7`. Do not add it as a WarpMetal dependency,
29
29
  install executable wallet code from an unpinned repository URL, or substitute
30
30
  a similarly named package.
31
31
 
@@ -149,7 +149,7 @@ reservation or any buyer-funded, unsupported, or unbound alternative.
149
149
 
150
150
  x402api pays actual gas from its platform treasury. The merchant tenant's
151
151
  active sponsorship allowance controls admission, but actual gas is not a
152
- tenant debit. During the coordinated rollout, accept only the exact 0.2.6
152
+ tenant debit. During the coordinated rollout, accept only the exact 0.2.7
153
153
  platform-treasury declaration or the matched legacy tenant-credit declaration;
154
154
  never accept a mixed billing and final-charge policy.
155
155
 
@@ -191,14 +191,17 @@ current `termEndsAt` generation are different.
191
191
  terms and authorize only the new workflow; never reuse an expired envelope or
192
192
  artifact.
193
193
  - `payment_pending` or `payment_finalizing`: keep the same checkout bytes and
194
- artifact. `--wait` performs bounded retries with that exact authorization.
194
+ artifact. Preserve the returned durable `paymentId`; `--wait` records it in
195
+ private WarpMetal state and performs bounded retries with that exact authorization.
195
196
  - Timeout or process restart after authorization: use the saved x402api attempt
196
197
  ID and artifact. Reconcile with WarpMetal and reuse its submit argv. Never
197
198
  authorize again merely because submission is unknown.
198
- - Signed HTTP 402: the artifact was definitively rejected. Run the WarpMetal
199
- challenge flow again, inspect the replacement terms, and authorize one new
200
- artifact only when the protocol and current interactive or autonomous
201
- payment authority allow it.
199
+ - Signed HTTP 402: the artifact was definitively rejected. A `paymentId` may be
200
+ absent because settlement never began; preserve the safe `errorCode`,
201
+ `requestId`, and `replacementAllowed` result instead. Only when
202
+ `replacementAllowed` is true may you run the unsigned WarpMetal challenge
203
+ flow again, inspect the replacement terms, and authorize the one permitted
204
+ replacement artifact. When false or absent, stop without signing again.
202
205
  - `sponsorship_allowance_unavailable`, `sponsorship_payment_cap_exceeded`,
203
206
  `sponsorship_payment_allowance_exhausted`,
204
207
  `sponsorship_volume_allowance_exhausted`, or
@@ -206,7 +209,12 @@ current `termEndsAt` generation are different.
206
209
  terminal. The merchant tenant must restore or change its allowance and issue
207
210
  a fresh challenge; never retry the artifact or fall back to buyer-funded gas.
208
211
  - `manual_review`: payment or fulfillment may be final. Stop all payment and
209
- mutation retries.
212
+ mutation retries. WarpMetal may continue read-only backend reconciliation for
213
+ `stale_payment_outcome`; check status on a later autonomous cycle without
214
+ submitting a payment. Only `expired` plus
215
+ `failure.code: payment_expired_unsettled`, `paidAt: null`, and
216
+ `retrySafe: true` proves the old charge expired unpaid and permits preparing
217
+ a new order under current standing authority. Never reuse the old signature.
210
218
  - Expired or corrupt signed artifact, changed request digest, unexpected recipient,
211
219
  unsupported network, asset, profile, sponsorship error, or request-binding
212
220
  mismatch: stop instead of falling back or asking the buyer to fund ETH/SOL.
@@ -127,7 +127,9 @@ generated QR encoding only that address.
127
127
  ## Stop and recovery states
128
128
 
129
129
  - Insufficient balance: notify once, stop, and recheck later.
130
- - Signed HTTP 402: re-run prepare and re-evaluate the replacement challenge.
130
+ - Signed HTTP 402: preserve the safe rejection diagnostics. Re-run prepare and
131
+ re-evaluate one replacement challenge only when `replacementAllowed` is true;
132
+ stop when false or absent.
131
133
  - Pending or timeout after authorization: reuse the same artifact; do not sign again.
132
134
  - Price or payment rail outside policy: stop for deliberate policy revision.
133
135
  - Exhausted count, horizon, or cumulative budget: stop for deliberate policy revision.
@@ -115,10 +115,13 @@ Confirm the plan, hostname, OS, and public key before preparing it.
115
115
  replaces its saved challenge metadata. Re-evaluate the new terms before
116
116
  authorization; never sign or submit the expired challenge.
117
117
  - For `payment_pending`, retry the exact checkout body and the exact same
118
- payment artifact. Do not create a replacement payment.
119
- - A signed HTTP 402 rejects that signature. Use the new live challenge for one
120
- replacement authorization after checking the new terms against the current
121
- interactive or autonomous payment authority.
118
+ payment artifact. Preserve the returned `paymentId` as the durable x402api
119
+ reconciliation key. Do not create a replacement payment.
120
+ - A signed HTTP 402 rejects that signature. A `paymentId` can be absent;
121
+ preserve the safe `errorCode`, `requestId`, and `replacementAllowed` result.
122
+ Request a new live challenge and create the one permitted replacement only
123
+ when `replacementAllowed` is true and the new terms remain within current
124
+ interactive or autonomous payment authority. When false or absent, stop.
122
125
  - Treat `sponsorship_allowance_unavailable`,
123
126
  `sponsorship_payment_cap_exceeded`,
124
127
  `sponsorship_payment_allowance_exhausted`,
@@ -130,8 +133,11 @@ Confirm the plan, hostname, OS, and public key before preparing it.
130
133
  attempt and artifact through WarpMetal. Never authorize again merely because
131
134
  submission or fulfillment is uncertain, and never send the private owner
132
135
  token through `x402api pay`, `payment submit`, or `payment reconcile`.
133
- - Treat `manual_review` as terminal. Do not pay again and do not repeat the
134
- mutation.
136
+ - Treat `manual_review` as terminal for payments and mutations. Do not pay
137
+ again and do not repeat the mutation. A later read-only status check may
138
+ observe automatic reconciliation of `stale_payment_outcome`; only the exact
139
+ `expired/payment_expired_unsettled` result with `paidAt: null` and
140
+ `retrySafe: true` permits preparing a new order.
135
141
  - Treat HTTP 202 as accepted or pending, never as proof of success. Poll the
136
142
  returned task or operation until a documented terminal state.
137
143
  - Temporary sandbox expiry is a local hard deadline. Stop, restart, and
package/src/cli.js CHANGED
@@ -379,19 +379,26 @@ async function pollGrant(
379
379
  }
380
380
  }
381
381
 
382
- function challengeResult(taskId, checkoutBody, response) {
382
+ function challengeResult(
383
+ taskId,
384
+ checkoutBody,
385
+ response,
386
+ { requireChallenge = true } = {},
387
+ ) {
388
+ const status = response.data?.status;
389
+ const rejected = response.status === 402 && status === "payment_rejected";
383
390
  const paymentRequired = response.headers["payment-required"];
384
391
  const challengeHandle = response.headers["x-x402api-challenge-handle"];
385
- if (response.status === 402 && !paymentRequired) {
392
+ if (requireChallenge && response.status === 402 && !paymentRequired) {
386
393
  throw new CliError("WarpMetal returned HTTP 402 without PAYMENT-REQUIRED.");
387
394
  }
388
- if (response.status === 402 && !challengeHandle) {
395
+ if (requireChallenge && response.status === 402 && !challengeHandle) {
389
396
  throw new CliError(
390
397
  "WarpMetal returned HTTP 402 without X-X402API-Challenge-Handle.",
391
398
  );
392
399
  }
393
400
  return {
394
- status: response.data?.status,
401
+ status,
395
402
  taskId,
396
403
  paymentAttemptId:
397
404
  response.data?.paymentAttemptId ||
@@ -399,6 +406,13 @@ function challengeResult(taskId, checkoutBody, response) {
399
406
  paymentRequired,
400
407
  challengeHandle,
401
408
  checkoutBodySha256: createHash("sha256").update(checkoutBody).digest("hex"),
409
+ ...(rejected
410
+ ? {
411
+ errorCode: response.data?.errorCode,
412
+ requestId: response.data?.requestId,
413
+ replacementAllowed: response.data?.replacementAllowed,
414
+ }
415
+ : {}),
402
416
  };
403
417
  }
404
418
 
@@ -736,13 +750,26 @@ async function handleCheckoutSubmit(client, store, options, context) {
736
750
  token,
737
751
  paymentSignature,
738
752
  });
753
+ if (response.data?.paymentId) {
754
+ await store.saveOrderPaymentId(taskId, response.data.paymentId);
755
+ }
739
756
  const safe = await attachPaymentWorkflow(
740
757
  client,
741
758
  store,
742
759
  taskId,
743
760
  order,
744
- challengeResult(taskId, order.checkoutBody, response),
761
+ challengeResult(taskId, order.checkoutBody, response, {
762
+ requireChallenge: false,
763
+ }),
745
764
  );
765
+ if (response.status === 402 && safe.status === "payment_rejected") {
766
+ await store.saveOrderPaymentRejection(taskId, {
767
+ paymentAttemptId: safe.paymentAttemptId,
768
+ errorCode: safe.errorCode,
769
+ requestId: safe.requestId,
770
+ replacementAllowed: safe.replacementAllowed,
771
+ });
772
+ }
746
773
  const retryable =
747
774
  response.status === 202 &&
748
775
  ["payment_pending", "payment_finalizing"].includes(response.data?.status);
@@ -755,6 +782,7 @@ async function handleCheckoutSubmit(client, store, options, context) {
755
782
  const output = {
756
783
  ...safe,
757
784
  task: response.data?.task,
785
+ paymentId: response.data?.paymentId,
758
786
  message: response.data?.message,
759
787
  ...(walletPayment
760
788
  ? {
@@ -1327,6 +1355,19 @@ async function handleRenewalSubmit(client, store, options, context) {
1327
1355
  paymentSignature: walletPayment.paymentSignature,
1328
1356
  },
1329
1357
  );
1358
+ if (response.data?.paymentId) {
1359
+ await store.saveRenewalPaymentId(serverId, response.data.paymentId);
1360
+ }
1361
+ if (response.status === 402 && response.data?.status === "payment_rejected") {
1362
+ await store.saveRenewalPaymentRejection(serverId, {
1363
+ paymentAttemptId:
1364
+ response.data?.paymentAttemptId ||
1365
+ response.headers["x-warpmetal-payment-attempt"],
1366
+ errorCode: response.data?.errorCode,
1367
+ requestId: response.data?.requestId,
1368
+ replacementAllowed: response.data?.replacementAllowed,
1369
+ });
1370
+ }
1330
1371
  const retryable =
1331
1372
  response.status === 202 &&
1332
1373
  ["payment_pending", "payment_finalizing"].includes(response.data?.status);
@@ -1339,9 +1380,13 @@ async function handleRenewalSubmit(client, store, options, context) {
1339
1380
  status: response.data?.status,
1340
1381
  serverId,
1341
1382
  task: response.data?.task,
1383
+ paymentId: response.data?.paymentId,
1342
1384
  paymentAttemptId:
1343
1385
  response.data?.paymentAttemptId ||
1344
1386
  response.headers["x-warpmetal-payment-attempt"],
1387
+ errorCode: response.data?.errorCode,
1388
+ requestId: response.data?.requestId,
1389
+ replacementAllowed: response.data?.replacementAllowed,
1345
1390
  walletPayment: {
1346
1391
  attemptId: walletPayment.attemptId,
1347
1392
  wallet: walletPayment.wallet,
package/src/payment.js CHANGED
@@ -15,7 +15,7 @@ const MAX_ARTIFACT_BYTES = 1024 * 1024;
15
15
  const MAX_SIGNATURE_BYTES = 512 * 1024;
16
16
 
17
17
  export const AGENT_WALLET_PACKAGE = "@x402api/agent-wallet-cli";
18
- export const AGENT_WALLET_VERSION = "0.2.6";
18
+ export const AGENT_WALLET_VERSION = "0.2.7";
19
19
  const AGENT_WALLET_SPEC = `${AGENT_WALLET_PACKAGE}@${AGENT_WALLET_VERSION}`;
20
20
  const GAS_SPONSORSHIP_EXTENSION = "com.x402api.gas-sponsorship";
21
21
  const BASE_NETWORK = "eip155:8453";
package/src/state.js CHANGED
@@ -13,7 +13,61 @@ const WALLET_PAYMENT_STATE_FIELDS = [
13
13
  "walletPayerAddress",
14
14
  "paymentArtifactExpiresAt",
15
15
  "paymentArtifactSavedAt",
16
+ "gatewayPaymentId",
17
+ "rejectedPaymentAttemptId",
18
+ "paymentRejectionErrorCode",
19
+ "paymentRejectionRequestId",
20
+ "paymentReplacementAllowed",
21
+ "paymentRejectedAt",
16
22
  ];
23
+ const X402API_PAYMENT_ID =
24
+ /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
25
+ const SAFE_PROTOCOL_IDENTIFIER = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$/;
26
+
27
+ function validatedPaymentRejection({
28
+ paymentAttemptId,
29
+ errorCode,
30
+ requestId,
31
+ replacementAllowed,
32
+ }) {
33
+ if (
34
+ typeof paymentAttemptId !== "string" ||
35
+ !SAFE_PROTOCOL_IDENTIFIER.test(paymentAttemptId)
36
+ ) {
37
+ throw new CliError("WarpMetal returned an invalid payment-attempt ID.", {
38
+ exitCode: 3,
39
+ });
40
+ }
41
+ for (const [label, value] of [
42
+ ["payment rejection error code", errorCode],
43
+ ["payment rejection request ID", requestId],
44
+ ]) {
45
+ if (
46
+ value !== undefined &&
47
+ (typeof value !== "string" || !SAFE_PROTOCOL_IDENTIFIER.test(value))
48
+ ) {
49
+ throw new CliError(`WarpMetal returned an invalid ${label}.`, {
50
+ exitCode: 3,
51
+ });
52
+ }
53
+ }
54
+ if (
55
+ replacementAllowed !== undefined &&
56
+ typeof replacementAllowed !== "boolean"
57
+ ) {
58
+ throw new CliError(
59
+ "WarpMetal returned an invalid payment replacement decision.",
60
+ { exitCode: 3 },
61
+ );
62
+ }
63
+ return {
64
+ rejectedPaymentAttemptId: paymentAttemptId,
65
+ paymentRejectionErrorCode: errorCode,
66
+ paymentRejectionRequestId: requestId,
67
+ paymentReplacementAllowed: replacementAllowed,
68
+ paymentRejectedAt: new Date().toISOString(),
69
+ };
70
+ }
17
71
 
18
72
  function replaceChallenge(record, challenge) {
19
73
  const changed =
@@ -361,6 +415,94 @@ export class StateStore {
361
415
  });
362
416
  }
363
417
 
418
+ async saveOrderPaymentId(taskId, paymentId) {
419
+ if (!X402API_PAYMENT_ID.test(paymentId || "")) {
420
+ throw new CliError("WarpMetal returned an invalid x402api payment ID.", {
421
+ exitCode: 3,
422
+ });
423
+ }
424
+ await this.update((state) => {
425
+ const order = state.orders[taskId];
426
+ if (!order) {
427
+ throw new CliError(`No local order state exists for ${taskId}.`, {
428
+ exitCode: 2,
429
+ });
430
+ }
431
+ if (order.gatewayPaymentId && order.gatewayPaymentId !== paymentId) {
432
+ throw new CliError("WarpMetal changed the x402api payment ID for this attempt.", {
433
+ exitCode: 3,
434
+ });
435
+ }
436
+ order.gatewayPaymentId = paymentId;
437
+ });
438
+ }
439
+
440
+ async saveOrderPaymentRejection(taskId, rejection) {
441
+ const safe = validatedPaymentRejection(rejection);
442
+ await this.update((state) => {
443
+ const order = state.orders[taskId];
444
+ if (!order) {
445
+ throw new CliError(`No local order state exists for ${taskId}.`, {
446
+ exitCode: 2,
447
+ });
448
+ }
449
+ if (
450
+ order.paymentAttemptId &&
451
+ order.paymentAttemptId !== safe.rejectedPaymentAttemptId
452
+ ) {
453
+ throw new CliError(
454
+ "WarpMetal changed the payment-attempt ID for this authorization.",
455
+ { exitCode: 3 },
456
+ );
457
+ }
458
+ Object.assign(order, safe);
459
+ });
460
+ }
461
+
462
+ async saveRenewalPaymentId(serverId, paymentId) {
463
+ if (!X402API_PAYMENT_ID.test(paymentId || "")) {
464
+ throw new CliError("WarpMetal returned an invalid x402api payment ID.", {
465
+ exitCode: 3,
466
+ });
467
+ }
468
+ await this.update((state) => {
469
+ const renewal = state.renewals[serverId];
470
+ if (!renewal) {
471
+ throw new CliError(`No local renewal state exists for ${serverId}.`, {
472
+ exitCode: 2,
473
+ });
474
+ }
475
+ if (renewal.gatewayPaymentId && renewal.gatewayPaymentId !== paymentId) {
476
+ throw new CliError("WarpMetal changed the x402api payment ID for this renewal.", {
477
+ exitCode: 3,
478
+ });
479
+ }
480
+ renewal.gatewayPaymentId = paymentId;
481
+ });
482
+ }
483
+
484
+ async saveRenewalPaymentRejection(serverId, rejection) {
485
+ const safe = validatedPaymentRejection(rejection);
486
+ await this.update((state) => {
487
+ const renewal = state.renewals[serverId];
488
+ if (!renewal) {
489
+ throw new CliError(`No local renewal state exists for ${serverId}.`, {
490
+ exitCode: 2,
491
+ });
492
+ }
493
+ if (
494
+ renewal.paymentAttemptId &&
495
+ renewal.paymentAttemptId !== safe.rejectedPaymentAttemptId
496
+ ) {
497
+ throw new CliError(
498
+ "WarpMetal changed the payment-attempt ID for this renewal authorization.",
499
+ { exitCode: 3 },
500
+ );
501
+ }
502
+ Object.assign(renewal, safe);
503
+ });
504
+ }
505
+
364
506
  async saveAccessToken(serverId, accessToken, expiresAt) {
365
507
  await this.update((state) => {
366
508
  const server = state.servers[serverId] || { serverId };
@@ -488,11 +630,16 @@ export class StateStore {
488
630
  serverId: order.serverId,
489
631
  planId: order.planId,
490
632
  paymentAttemptId: order.paymentAttemptId,
633
+ paymentId: order.gatewayPaymentId,
491
634
  challengeHandle: order.challengeHandle,
492
635
  walletPaymentAttemptId: order.walletPaymentAttemptId,
493
636
  walletName: order.walletName,
494
637
  payerAddress: order.walletPayerAddress,
495
638
  paymentArtifactExpiresAt: order.paymentArtifactExpiresAt,
639
+ rejectedPaymentAttemptId: order.rejectedPaymentAttemptId,
640
+ paymentRejectionErrorCode: order.paymentRejectionErrorCode,
641
+ paymentRejectionRequestId: order.paymentRejectionRequestId,
642
+ paymentReplacementAllowed: order.paymentReplacementAllowed,
496
643
  credentialStored: Boolean(order.ownerToken),
497
644
  })),
498
645
  servers: Object.values(state.servers).map((server) => ({
@@ -523,8 +670,13 @@ export class StateStore {
523
670
  refillTargetAtomic: renewal.refillTargetAtomic,
524
671
  policy: renewal.policy,
525
672
  paymentAttemptId: renewal.paymentAttemptId,
673
+ paymentId: renewal.gatewayPaymentId,
526
674
  challengeHandle: renewal.challengeHandle,
527
675
  paymentArtifactExpiresAt: renewal.paymentArtifactExpiresAt,
676
+ rejectedPaymentAttemptId: renewal.rejectedPaymentAttemptId,
677
+ paymentRejectionErrorCode: renewal.paymentRejectionErrorCode,
678
+ paymentRejectionRequestId: renewal.paymentRejectionRequestId,
679
+ paymentReplacementAllowed: renewal.paymentReplacementAllowed,
528
680
  })),
529
681
  };
530
682
  }