@dvmkit/sdk 0.21.2 → 0.22.0

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.
Files changed (55) hide show
  1. package/dist/card-charge-journal-SKMBEPRM.js +44 -0
  2. package/dist/{chunk-MA55LUNR.js → chunk-2WWVI32F.js} +55 -1
  3. package/dist/{chunk-3V6CSTWB.js → chunk-3DGIRHP2.js} +2 -2
  4. package/dist/{chunk-WWKFG6TM.js → chunk-5NMHZIIU.js} +1 -1
  5. package/dist/{chunk-FAT57DAP.js → chunk-5YJWAJ4N.js} +1 -1
  6. package/dist/{chunk-D2MGOQIS.js → chunk-CUWSJQGT.js} +1 -1
  7. package/dist/{chunk-6GZF7VDO.js → chunk-DLRPKEAM.js} +1 -1
  8. package/dist/{chunk-62JG4AF7.js → chunk-DY5QIHJA.js} +1 -1
  9. package/dist/{chunk-LWF4ABYF.js → chunk-EHBUAJ5M.js} +1 -1
  10. package/dist/{chunk-IUFVIIPF.js → chunk-EQIP6TFD.js} +1 -1
  11. package/dist/{chunk-ZBOCNF56.js → chunk-GDZXGB43.js} +3 -3
  12. package/dist/{chunk-2HSWWHXK.js → chunk-HEH7YUGE.js} +1 -1
  13. package/dist/{chunk-B27E7JIR.js → chunk-HVZIITWW.js} +27 -4
  14. package/dist/{chunk-VIAMJ6GS.js → chunk-JINZMDNK.js} +1224 -80
  15. package/dist/{chunk-NRE4NOCY.js → chunk-JMTLK2SZ.js} +1 -1
  16. package/dist/{chunk-LSANCGLP.js → chunk-L35WYOH2.js} +1 -1
  17. package/dist/{chunk-27QB2D3Z.js → chunk-LCNRFKP7.js} +2 -2
  18. package/dist/{chunk-2AGADOX3.js → chunk-QMKGYIBM.js} +1 -1
  19. package/dist/{chunk-JOSNSNS7.js → chunk-SEIMPHIP.js} +225 -11
  20. package/dist/{chunk-Y746QPX7.js → chunk-SQOVLAJ2.js} +1 -1
  21. package/dist/{chunk-XWG54M6T.js → chunk-UVRBD5GR.js} +1 -1
  22. package/dist/{chunk-5RGZN6WQ.js → chunk-XB644VN3.js} +1 -1
  23. package/dist/{chunk-2RZRYMCR.js → chunk-YNHFJE6U.js} +1 -1
  24. package/dist/{chunk-7KNFEDFW.js → chunk-ZH5CLGO6.js} +1 -1
  25. package/dist/{content-retention-efjireLM.d.ts → content-retention-DEvY5vjM.d.ts} +1 -1
  26. package/dist/{credit-ledger-M3EBVMOP.js → credit-ledger-S76KU53A.js} +11 -3
  27. package/dist/{credit-menu-UOOxuxVL.d.ts → credit-menu-BA2my6hE.d.ts} +229 -8
  28. package/dist/{fx-BtxHVj6w.d.ts → fx-J5oXbv9A.d.ts} +1 -1
  29. package/dist/index.d.ts +5 -5
  30. package/dist/index.js +4 -4
  31. package/dist/internal/caller.d.ts +10 -9
  32. package/dist/internal/caller.js +11 -11
  33. package/dist/internal/server.d.ts +7 -7
  34. package/dist/internal/server.js +21 -21
  35. package/dist/{job-store-DWVkleH3.d.ts → job-store-CjPnf0dd.d.ts} +1 -1
  36. package/dist/{lightning-backend-CTijN35u.d.ts → lightning-backend-D9Q2KDS-.d.ts} +1 -1
  37. package/dist/{memory-credit-ledger-WL2VKLE3.js → memory-credit-ledger-5KXHOWB2.js} +4 -4
  38. package/dist/{mpp-setup-DBYDXISN.js → mpp-setup-3YZMURZL.js} +3 -3
  39. package/dist/{payout-reporter-D5TCIBQA.js → payout-reporter-KTLBNJJ7.js} +2 -2
  40. package/dist/{postgres-consumed-credential-store-IPWPJ6V7.js → postgres-consumed-credential-store-Y336KNIB.js} +1 -1
  41. package/dist/{postgres-job-store-POZJNFJU.js → postgres-job-store-ESEAN7JL.js} +2 -2
  42. package/dist/{postgres-replay-store-JQYAKG47.js → postgres-replay-store-6SVCZ475.js} +2 -2
  43. package/dist/{pricing-FGPVRNTL.js → pricing-RP5CGW6I.js} +3 -3
  44. package/dist/{revenue-reporter-QKCBYFWT.js → revenue-reporter-FD3GDQZS.js} +2 -2
  45. package/dist/server/index.d.ts +20 -11
  46. package/dist/server/index.js +292 -28
  47. package/dist/{step-cache-Bx_ZybWF.d.ts → step-cache-DXFftnAc.d.ts} +154 -12
  48. package/dist/{tempo-charge-store-P7ZBR3GG.js → tempo-charge-store-SOIRMEFJ.js} +2 -2
  49. package/dist/{tempo-session-store-ZVRBAMLG.js → tempo-session-store-UBDFFHDG.js} +3 -3
  50. package/dist/testing/index.d.ts +3 -3
  51. package/dist/testing/index.js +6 -6
  52. package/dist/{usd-eOAMEO12.d.ts → usd-Bc_rva_K.d.ts} +1 -1
  53. package/dist/{x402-44JZJQ5F.js → x402-MFBRORHG.js} +5 -5
  54. package/package.json +1 -1
  55. package/dist/{chunk-NUDHO4HV.js → chunk-LABJSMSJ.js} +48 -48
@@ -0,0 +1,44 @@
1
+ // src/sdk/server/card-charge-journal.ts
2
+ async function createPostgresCardChargeJournal(pool, opts = {}) {
3
+ const now = opts.now ?? Date.now;
4
+ await pool.query(`
5
+ CREATE TABLE IF NOT EXISTS card_charge_attempts (
6
+ challenge_id TEXT PRIMARY KEY,
7
+ created_at BIGINT NOT NULL,
8
+ resolved_at BIGINT,
9
+ outcome TEXT
10
+ )
11
+ `);
12
+ await pool.query(
13
+ `CREATE INDEX IF NOT EXISTS idx_card_charge_attempts_open
14
+ ON card_charge_attempts(created_at) WHERE resolved_at IS NULL`
15
+ );
16
+ return {
17
+ async record(challengeId) {
18
+ await pool.query(
19
+ `INSERT INTO card_charge_attempts (challenge_id, created_at) VALUES ($1, $2)
20
+ ON CONFLICT (challenge_id) DO NOTHING`,
21
+ [challengeId, now()]
22
+ );
23
+ },
24
+ async listUnresolved({ olderThanMs, limit }) {
25
+ const { rows } = await pool.query(
26
+ `SELECT challenge_id, created_at FROM card_charge_attempts
27
+ WHERE resolved_at IS NULL AND created_at <= $1
28
+ ORDER BY created_at LIMIT $2`,
29
+ [olderThanMs, limit]
30
+ );
31
+ return rows.map((row) => ({ challengeId: row.challenge_id, createdAt: Number(row.created_at) }));
32
+ },
33
+ async resolve(challengeId, outcome) {
34
+ await pool.query(
35
+ `UPDATE card_charge_attempts SET resolved_at = $2, outcome = $3
36
+ WHERE challenge_id = $1 AND resolved_at IS NULL`,
37
+ [challengeId, now(), outcome]
38
+ );
39
+ }
40
+ };
41
+ }
42
+ export {
43
+ createPostgresCardChargeJournal
44
+ };
@@ -496,7 +496,15 @@ var diagnostic_catalogue_v1_default = {
496
496
  "x402_settlement_reconciliation_chain_unreachable",
497
497
  "x402_settlement_reconciliation_facilitator_unavailable",
498
498
  "x402_settlement_submission_failed",
499
- "x402_settlement_written_off"
499
+ "x402_settlement_written_off",
500
+ "card_charge_journal_failed",
501
+ "card_charge_stranded_refunded",
502
+ "card_charge_unsettled",
503
+ "card_refund_pending",
504
+ "card_refund_sweep",
505
+ "card_refund_expiry_failed",
506
+ "card_refund_sweep_failed",
507
+ "card_refund_sweep_step_failed"
500
508
  ],
501
509
  routeTemplates: [
502
510
  "/",
@@ -769,13 +777,23 @@ var diagnostic_catalogue_v1_default = {
769
777
  "/v1/request-resume/:requestId"
770
778
  ],
771
779
  errorCodes: [
780
+ "amount_too_large",
781
+ "api_error",
772
782
  "app_shell_detected",
773
783
  "audio_source_required",
774
784
  "auth_error",
775
785
  "auth_required",
786
+ "authentication_error",
787
+ "balance_insufficient",
776
788
  "bytes_cap",
777
789
  "capability_required",
778
790
  "captcha_unsolved",
791
+ "card_error",
792
+ "charge_already_refunded",
793
+ "charge_disputed",
794
+ "charge_expired_for_refund",
795
+ "charge_not_refundable",
796
+ "charges_exhausted",
779
797
  "cloudflare_unsolved",
780
798
  "content_rejected",
781
799
  "cover_invalid",
@@ -793,6 +811,7 @@ var diagnostic_catalogue_v1_default = {
793
811
  "extract_llm_failed",
794
812
  "extract_schema_invalid",
795
813
  "extract_validation_failed",
814
+ "fee_not_settled",
796
815
  "feed_full",
797
816
  "feed_not_found",
798
817
  "feed_path_invalid",
@@ -807,6 +826,8 @@ var diagnostic_catalogue_v1_default = {
807
826
  "head_status",
808
827
  "hint",
809
828
  "hosting_identity_revoke_failed",
829
+ "idempotency_error",
830
+ "idempotency_key_in_use",
810
831
  "ingest_concurrency",
811
832
  "ingest_global_concurrency",
812
833
  "ingest_ip_concurrency",
@@ -817,6 +838,7 @@ var diagnostic_catalogue_v1_default = {
817
838
  "invalid_menu",
818
839
  "invalid_payment",
819
840
  "invalid_quote_data",
841
+ "invalid_request_error",
820
842
  "invalid_url",
821
843
  "job_failed",
822
844
  "job_timeout",
@@ -837,6 +859,7 @@ var diagnostic_catalogue_v1_default = {
837
859
  "payment_invalid",
838
860
  "payment_required",
839
861
  "paywall_detected",
862
+ "permission_error",
840
863
  "polish_input_too_large",
841
864
  "polish_llm_failed",
842
865
  "presign_failed",
@@ -854,11 +877,14 @@ var diagnostic_catalogue_v1_default = {
854
877
  "provider_timeout",
855
878
  "provider_unavailable",
856
879
  "quota_exceeded",
880
+ "rate_limit_error",
857
881
  "rate_limited",
858
882
  "rate_limited_by_target",
883
+ "refund_disputed_payment",
859
884
  "region_unavailable",
860
885
  "replay_detected",
861
886
  "request_integrity_failed",
887
+ "resource_missing",
862
888
  "retry_hardened",
863
889
  "retry_with_bounded_wait",
864
890
  "retry_with_content_selector",
@@ -872,6 +898,7 @@ var diagnostic_catalogue_v1_default = {
872
898
  "source_unreachable",
873
899
  "ssrf_blocked",
874
900
  "string",
901
+ "stripe_unreachable",
875
902
  "substrate_unavailable",
876
903
  "timeout",
877
904
  "timestamp_drift",
@@ -1184,6 +1211,17 @@ var CAPABILITY_RE = /^[a-z][a-z0-9_-]{0,47}$/;
1184
1211
  var CLIENT_VERSION_RE = /^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-rc\.(?:0|[1-9]\d*))?$/;
1185
1212
  var HASH_RE = /^(?:[0-9a-f]{16}|[0-9a-f]{64})$/i;
1186
1213
  var DECIMAL_RE = /^(?:0|[1-9]\d{0,77})$/;
1214
+ var CARD_REFUND_EVENTS = /* @__PURE__ */ new Set([
1215
+ "card_charge_stranded_refunded",
1216
+ "card_charge_unsettled",
1217
+ "card_refund_expiry_failed",
1218
+ "card_refund_pending",
1219
+ "card_refund_sweep",
1220
+ "card_refund_sweep_step_failed"
1221
+ ]);
1222
+ var CARD_REFUND_COUNTS = /* @__PURE__ */ new Set(["frozen", "unfrozen", "expiry_refunds", "completed", "pending", "stranded_refunds"]);
1223
+ var CARD_REFUND_STAGES = /* @__PURE__ */ new Set(["plan", "refund", "disputes", "expiry", "retry", "stranded"]);
1224
+ var CARD_REFUND_STATUSES = /* @__PURE__ */ new Set(["planned", "pending", "requires_action", "succeeded", "failed", "canceled", "rejected"]);
1187
1225
  var STRIPE_EVENT_RE = /^evt_[A-Za-z0-9]{1,64}$/;
1188
1226
  var REDACTED_SEGMENT_RE = /^:(?:uuid|hex|n|b58|token)$/;
1189
1227
  var SERVICE_NAMES = /* @__PURE__ */ new Set([
@@ -1355,6 +1393,10 @@ function projectField(key, value, dataset, authority, identityAuthority, eventNa
1355
1393
  const closedEnum = Object.hasOwn(PAYMENT_ENUMS, key) ? PAYMENT_ENUMS[key] : Object.hasOwn(ROUTER_ENUMS, key) ? ROUTER_ENUMS[key] : void 0;
1356
1394
  if (closedEnum)
1357
1395
  return typeof value === "string" && closedEnum.has(value) ? value : void 0;
1396
+ if (eventName !== void 0 && CARD_REFUND_EVENTS.has(eventName)) {
1397
+ const projected = projectCardRefundField(key, value);
1398
+ if (projected !== void 0) return projected;
1399
+ }
1358
1400
  if (dataset === "tempo") {
1359
1401
  if (key === "prior_status")
1360
1402
  return typeof value === "string" && TEMPO_CLOSE_REPORT_PRIOR_STATUSES.has(value) ? value : void 0;
@@ -1436,6 +1478,18 @@ function projectField(key, value, dataset, authority, identityAuthority, eventNa
1436
1478
  return typeof value === "string" && SANCTIONS_REGIONS.has(value) ? value : void 0;
1437
1479
  return void 0;
1438
1480
  }
1481
+ function projectCardRefundField(key, value) {
1482
+ if (key === "credit_id" || key === "drain_id" || key === "challenge_id")
1483
+ return typeof value === "string" && /^[a-zA-Z0-9_.:-]{1,512}$/.test(value) ? value : void 0;
1484
+ if (key === "payment_intent")
1485
+ return typeof value === "string" && /^pi_[A-Za-z0-9]{1,64}$/.test(value) ? value : void 0;
1486
+ if (key === "stage")
1487
+ return typeof value === "string" && CARD_REFUND_STAGES.has(value) ? value : void 0;
1488
+ if (key === "refund_status")
1489
+ return typeof value === "string" && CARD_REFUND_STATUSES.has(value) ? value : void 0;
1490
+ if (CARD_REFUND_COUNTS.has(key)) return isNonnegativeSafeInteger(value) ? value : void 0;
1491
+ return void 0;
1492
+ }
1439
1493
  function projectService(value) {
1440
1494
  if (typeof value !== "string") return "unknown";
1441
1495
  if (SERVICE_NAMES.has(value)) return value;
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  X402_DEFAULT_FACILITATOR
3
- } from "./chunk-NUDHO4HV.js";
3
+ } from "./chunk-LABJSMSJ.js";
4
4
  import {
5
5
  callerLoggers
6
6
  } from "./chunk-66HGCPBU.js";
7
7
  import {
8
8
  diagnosticConsole
9
- } from "./chunk-MA55LUNR.js";
9
+ } from "./chunk-2WWVI32F.js";
10
10
 
11
11
  // src/observability/payment-diagnostics.ts
12
12
  import { AsyncLocalStorage } from "async_hooks";
@@ -10,7 +10,7 @@ import {
10
10
  } from "./chunk-FUJ36YDV.js";
11
11
  import {
12
12
  loggers
13
- } from "./chunk-7KNFEDFW.js";
13
+ } from "./chunk-ZH5CLGO6.js";
14
14
 
15
15
  // src/sdk/server/job-store.ts
16
16
  function accumulateAskedTopUp(prior, ask) {
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-COYKJRMM.js";
8
8
  import {
9
9
  withPostgresTransactionRecovery
10
- } from "./chunk-LSANCGLP.js";
10
+ } from "./chunk-L35WYOH2.js";
11
11
 
12
12
  // src/sdk/server/tempo-session-store.ts
13
13
  import { AsyncLocalStorage } from "async_hooks";
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  diagnosticConsole,
6
6
  projectSpanRow
7
- } from "./chunk-MA55LUNR.js";
7
+ } from "./chunk-2WWVI32F.js";
8
8
 
9
9
  // src/lib/x402/env.ts
10
10
  import { isAddress } from "viem";
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-SXL44PL5.js";
8
8
  import {
9
9
  diagnosticConsole
10
- } from "./chunk-MA55LUNR.js";
10
+ } from "./chunk-2WWVI32F.js";
11
11
 
12
12
  // src/sdk/server/zod-error.ts
13
13
  function humanizeZodError(err, opts) {
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-COYKJRMM.js";
4
4
  import {
5
5
  diagnosticConsole
6
- } from "./chunk-MA55LUNR.js";
6
+ } from "./chunk-2WWVI32F.js";
7
7
 
8
8
  // src/sdk/server/payout-reporter.ts
9
9
  import { createHash, randomUUID } from "crypto";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  validateCurrency
3
- } from "./chunk-XWG54M6T.js";
3
+ } from "./chunk-UVRBD5GR.js";
4
4
 
5
5
  // src/sdk/pricing/upfront.ts
6
6
  function buildUpfront(input) {
@@ -19,7 +19,7 @@ import {
19
19
  } from "./chunk-66HGCPBU.js";
20
20
  import {
21
21
  diagnosticConsole
22
- } from "./chunk-MA55LUNR.js";
22
+ } from "./chunk-2WWVI32F.js";
23
23
 
24
24
  // src/lib/cashu/lock-pubkey.ts
25
25
  function toLockPubkey(raw) {
@@ -2,20 +2,20 @@ import {
2
2
  createX402FacilitatorAuthHeaders,
3
3
  settleWithFacilitator,
4
4
  verifyWithFacilitator
5
- } from "./chunk-3V6CSTWB.js";
5
+ } from "./chunk-3DGIRHP2.js";
6
6
  import {
7
7
  buildPaymentRequirements,
8
8
  decodePayment,
9
9
  encodeSettleResponseHeader,
10
10
  exactEvmAuthorization,
11
11
  usdcToMsats
12
- } from "./chunk-NUDHO4HV.js";
12
+ } from "./chunk-LABJSMSJ.js";
13
13
  import {
14
14
  callerLoggers
15
15
  } from "./chunk-66HGCPBU.js";
16
16
  import {
17
17
  diagnosticConsole
18
- } from "./chunk-MA55LUNR.js";
18
+ } from "./chunk-2WWVI32F.js";
19
19
 
20
20
  // src/lib/x402/verify.ts
21
21
  async function verifyX402Payment(paymentHeader, config, requiredUsdcMicro, resource, btcUsdRate, exactVersions = { v1: true, v2: true }) {
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-COYKJRMM.js";
4
4
  import {
5
5
  diagnosticConsole
6
- } from "./chunk-MA55LUNR.js";
6
+ } from "./chunk-2WWVI32F.js";
7
7
 
8
8
  // src/sdk/server/postgres-replay-store.ts
9
9
  var DEFAULT_TABLE_NAME = "signed_request_replays";
@@ -4,12 +4,15 @@ import {
4
4
  X402_SPEND_BLOCKING_SETTLEMENT_STATUSES,
5
5
  X402_WEDGED_SETTLEMENT_STATUSES,
6
6
  allocateDrawValue,
7
+ assertCardDrainMatchesRail,
7
8
  assertFundingBasis,
9
+ cardRefundPendingConflict,
8
10
  clipEarmark,
9
11
  depleteLots,
10
12
  fifoOrder,
11
13
  growthRailValue,
12
14
  inKindDrawMsats,
15
+ isCardFundedRail,
13
16
  isFundingRail,
14
17
  isInKindDepletion,
15
18
  isNonChannelBitcoinRail,
@@ -18,7 +21,7 @@ import {
18
21
  refundAssetDelta,
19
22
  refundRequest,
20
23
  x402SettlementPending
21
- } from "./chunk-JOSNSNS7.js";
24
+ } from "./chunk-SEIMPHIP.js";
22
25
 
23
26
  // src/sdk/server/memory-credit-ledger.ts
24
27
  import { randomUUID } from "crypto";
@@ -889,6 +892,7 @@ var MemoryCreditLedger = class _MemoryCreditLedger {
889
892
  `credit ${args.creditId} belongs to a different caller pubkey`
890
893
  );
891
894
  }
895
+ assertCardDrainMatchesRail(args.creditId, credit.rail, args.method);
892
896
  const existing = credit.drains.get(args.drainId);
893
897
  if (existing) {
894
898
  if (existing.method !== args.method || JSON.stringify(existing.payout) !== JSON.stringify(args.payout)) {
@@ -904,6 +908,12 @@ var MemoryCreditLedger = class _MemoryCreditLedger {
904
908
  };
905
909
  }
906
910
  if (credit.status !== "active") throw terminalCreditError(args.creditId);
911
+ if (args.method === "stripe") {
912
+ const blocking = [...credit.drains.values()].find(
913
+ (drain2) => drain2.method === "stripe" && drain2.status === "pending"
914
+ );
915
+ if (blocking) throw cardRefundPendingConflict(args.creditId, blocking.drainId);
916
+ }
907
917
  if (x402Refund) throw x402SettlementPending(args.creditId, x402Refund);
908
918
  const pending = pendingSums(credit);
909
919
  const availableMicro = credit.balanceMicro - pending.micro;
@@ -965,7 +975,8 @@ var MemoryCreditLedger = class _MemoryCreditLedger {
965
975
  writeOffNote: null,
966
976
  writtenOffAt: null,
967
977
  provenRefundNative: null,
968
- refundPaymentProven: false
978
+ refundPaymentProven: false,
979
+ cardRefund: null
969
980
  };
970
981
  applyLotDebits(credit, drain.lotDebits, -1);
971
982
  credit.drains.set(args.drainId, drain);
@@ -1208,6 +1219,17 @@ var MemoryCreditLedger = class _MemoryCreditLedger {
1208
1219
  throw this.transitionRefusal(drain, "mark sent");
1209
1220
  });
1210
1221
  }
1222
+ saveCardRefund(args) {
1223
+ return run(() => {
1224
+ const drain = this.credits.get(args.creditId)?.drains.get(args.drainId);
1225
+ if (!drain) return null;
1226
+ const writable = drain.method === "stripe" && drain.status === "pending";
1227
+ if (writable && !(args.onlyIfUnset && drain.cardRefund)) {
1228
+ drain.cardRefund = structuredClone(args.plan);
1229
+ }
1230
+ return drain.cardRefund ? structuredClone(drain.cardRefund) : null;
1231
+ });
1232
+ }
1211
1233
  appendDrainReceipt(args) {
1212
1234
  return run(() => {
1213
1235
  const drain = this.credits.get(args.creditId)?.drains.get(args.drainId);
@@ -1326,7 +1348,7 @@ function applyBasis(credit, basis) {
1326
1348
  }
1327
1349
  }
1328
1350
  function recordLot(credit, amountMicro, basis, nowMs) {
1329
- if (!isNonChannelBitcoinRail(basis.rail)) return;
1351
+ if (!isNonChannelBitcoinRail(basis.rail) && !isCardFundedRail(basis.rail)) return;
1330
1352
  if (basis.tempoSession || basis.x402Channel) return;
1331
1353
  credit.lots.push({
1332
1354
  lotId: `lot:${randomUUID()}`,
@@ -1441,7 +1463,8 @@ function drainRecord(drain, credit) {
1441
1463
  writeOffNote: drain.writeOffNote,
1442
1464
  writtenOffAt: drain.writtenOffAt,
1443
1465
  provenRefundNative: drain.provenRefundNative,
1444
- refundPaymentProven: drain.refundPaymentProven
1466
+ refundPaymentProven: drain.refundPaymentProven,
1467
+ cardRefund: drain.cardRefund ? structuredClone(drain.cardRefund) : null
1445
1468
  };
1446
1469
  }
1447
1470
  function assertAmount(amountMicro, opts) {