@yiaany/ghostapi 0.1.8 → 0.2.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 (293) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/CONTRIBUTING.md +132 -119
  3. package/LICENSE +21 -21
  4. package/PROVENANCE.md +13 -0
  5. package/README.md +78 -80
  6. package/SECURITY.md +56 -53
  7. package/dist/actions/gateway.js +320 -72
  8. package/dist/actions/gateway.js.map +1 -1
  9. package/dist/actions/index.d.ts +2 -2
  10. package/dist/actions/index.js +1 -1
  11. package/dist/actions/index.js.map +1 -1
  12. package/dist/agents/agentPrompt.js +7 -5
  13. package/dist/agents/agentPrompt.js.map +1 -1
  14. package/dist/ai/aiClient.js +5 -5
  15. package/dist/ai/aiClient.js.map +1 -1
  16. package/dist/ai/aiGenerator.js +66 -26
  17. package/dist/ai/aiGenerator.js.map +1 -1
  18. package/dist/ai/genericInference.js +151 -36
  19. package/dist/ai/genericInference.js.map +1 -1
  20. package/dist/ai/prompts.js +55 -49
  21. package/dist/ai/prompts.js.map +1 -1
  22. package/dist/approvals/approvalInbox.js +649 -133
  23. package/dist/approvals/approvalInbox.js.map +1 -1
  24. package/dist/approvals/index.d.ts +2 -2
  25. package/dist/approvals/index.js +1 -1
  26. package/dist/approvals/index.js.map +1 -1
  27. package/dist/behavior/behaviorStore.js +34 -14
  28. package/dist/behavior/behaviorStore.js.map +1 -1
  29. package/dist/cache/index.js +14 -5
  30. package/dist/cache/index.js.map +1 -1
  31. package/dist/cli/index.js +226 -93
  32. package/dist/cli/index.js.map +1 -1
  33. package/dist/cli/openUrl.js +17 -3
  34. package/dist/cli/openUrl.js.map +1 -1
  35. package/dist/cli/parser.js +79 -18
  36. package/dist/cli/parser.js.map +1 -1
  37. package/dist/config/dataPaths.js +4 -2
  38. package/dist/config/dataPaths.js.map +1 -1
  39. package/dist/config/localConfig.js +5 -2
  40. package/dist/config/localConfig.js.map +1 -1
  41. package/dist/config/serverConfig.js +11 -5
  42. package/dist/config/serverConfig.js.map +1 -1
  43. package/dist/contracts/contract.js +311 -83
  44. package/dist/contracts/contract.js.map +1 -1
  45. package/dist/contracts/index.d.ts +2 -2
  46. package/dist/contracts/index.js +1 -1
  47. package/dist/contracts/index.js.map +1 -1
  48. package/dist/credentials/credentialBroker.js +391 -79
  49. package/dist/credentials/credentialBroker.js.map +1 -1
  50. package/dist/credentials/index.d.ts +2 -2
  51. package/dist/credentials/index.js +1 -1
  52. package/dist/credentials/index.js.map +1 -1
  53. package/dist/dashboard/app.js +823 -685
  54. package/dist/dashboard/dashboard.js.map +1 -1
  55. package/dist/dashboard/index.html +704 -267
  56. package/dist/dashboard/styles.css +1040 -349
  57. package/dist/egress/capabilities.js +55 -25
  58. package/dist/egress/capabilities.js.map +1 -1
  59. package/dist/egress/linuxBootstrap.js +47 -11
  60. package/dist/egress/linuxBootstrap.js.map +1 -1
  61. package/dist/egress/run.js +170 -31
  62. package/dist/egress/run.js.map +1 -1
  63. package/dist/errors/errorEngine.js +15 -3
  64. package/dist/errors/errorEngine.js.map +1 -1
  65. package/dist/errors/providerErrors.js.map +1 -1
  66. package/dist/evals/evals.js +304 -78
  67. package/dist/evals/evals.js.map +1 -1
  68. package/dist/evals/index.d.ts +2 -2
  69. package/dist/evals/index.js +1 -1
  70. package/dist/evals/index.js.map +1 -1
  71. package/dist/evidence/index.d.ts +2 -2
  72. package/dist/evidence/index.js +1 -1
  73. package/dist/evidence/index.js.map +1 -1
  74. package/dist/evidence/report.d.ts +1 -1
  75. package/dist/evidence/report.js +263 -79
  76. package/dist/evidence/report.js.map +1 -1
  77. package/dist/fault/faultLab.js +24 -9
  78. package/dist/fault/faultLab.js.map +1 -1
  79. package/dist/index.d.ts +42 -42
  80. package/dist/index.js +21 -21
  81. package/dist/index.js.map +1 -1
  82. package/dist/inventory/analysis.d.ts +1 -1
  83. package/dist/inventory/analysis.js +174 -50
  84. package/dist/inventory/analysis.js.map +1 -1
  85. package/dist/inventory/index.d.ts +4 -4
  86. package/dist/inventory/index.js +3 -3
  87. package/dist/inventory/index.js.map +1 -1
  88. package/dist/inventory/inventoryController.js +362 -115
  89. package/dist/inventory/inventoryController.js.map +1 -1
  90. package/dist/inventory/types.js +680 -140
  91. package/dist/inventory/types.js.map +1 -1
  92. package/dist/ledger/actionLedger.js +355 -78
  93. package/dist/ledger/actionLedger.js.map +1 -1
  94. package/dist/ledger/index.d.ts +2 -2
  95. package/dist/ledger/index.js +1 -1
  96. package/dist/ledger/index.js.map +1 -1
  97. package/dist/mcp/server.js +26 -11
  98. package/dist/mcp/server.js.map +1 -1
  99. package/dist/policy/index.d.ts +2 -2
  100. package/dist/policy/index.js +1 -1
  101. package/dist/policy/index.js.map +1 -1
  102. package/dist/policy/policy.js +112 -32
  103. package/dist/policy/policy.js.map +1 -1
  104. package/dist/productTelemetry/index.js +71 -20
  105. package/dist/productTelemetry/index.js.map +1 -1
  106. package/dist/providers/conformance.js +10 -5
  107. package/dist/providers/conformance.js.map +1 -1
  108. package/dist/providers/discord.js +2 -2
  109. package/dist/providers/generic.js +3 -3
  110. package/dist/providers/github.js +2 -2
  111. package/dist/providers/index.d.ts +3 -3
  112. package/dist/providers/index.js +2 -2
  113. package/dist/providers/index.js.map +1 -1
  114. package/dist/providers/openai.js +3 -3
  115. package/dist/providers/packs/resendPack.js +51 -25
  116. package/dist/providers/packs/resendPack.js.map +1 -1
  117. package/dist/providers/packs/stripePack.js +590 -121
  118. package/dist/providers/packs/stripePack.js.map +1 -1
  119. package/dist/providers/registry.js +4 -4
  120. package/dist/providers/registry.js.map +1 -1
  121. package/dist/providers/runtime.js +7 -6
  122. package/dist/providers/runtime.js.map +1 -1
  123. package/dist/providers/stripeWebhook.js +39 -10
  124. package/dist/providers/stripeWebhook.js.map +1 -1
  125. package/dist/providers/twilio.js +2 -2
  126. package/dist/proxy/cacheKey.js +9 -2
  127. package/dist/proxy/cacheKey.js.map +1 -1
  128. package/dist/proxy/providerDetector.js +22 -9
  129. package/dist/proxy/providerDetector.js.map +1 -1
  130. package/dist/proxy/proxyHandler.js +57 -22
  131. package/dist/proxy/proxyHandler.js.map +1 -1
  132. package/dist/proxy/requestNormalizer.js +4 -2
  133. package/dist/proxy/requestNormalizer.js.map +1 -1
  134. package/dist/reliability/costGovernance.js +211 -49
  135. package/dist/reliability/costGovernance.js.map +1 -1
  136. package/dist/reliability/index.d.ts +8 -8
  137. package/dist/reliability/index.js +4 -4
  138. package/dist/reliability/index.js.map +1 -1
  139. package/dist/reliability/reconciliation.js +274 -65
  140. package/dist/reliability/reconciliation.js.map +1 -1
  141. package/dist/reliability/runtimeHealth.js +127 -35
  142. package/dist/reliability/runtimeHealth.js.map +1 -1
  143. package/dist/reliability/slo.js +257 -50
  144. package/dist/reliability/slo.js.map +1 -1
  145. package/dist/report/safetyReport.d.ts +6 -0
  146. package/dist/report/safetyReport.js +56 -12
  147. package/dist/report/safetyReport.js.map +1 -1
  148. package/dist/rules/aiRules.js +5 -5
  149. package/dist/rules/aiRules.js.map +1 -1
  150. package/dist/safety/index.d.ts +2 -2
  151. package/dist/safety/index.js +1 -1
  152. package/dist/safety/index.js.map +1 -1
  153. package/dist/safety/safetyController.js +675 -165
  154. package/dist/safety/safetyController.js.map +1 -1
  155. package/dist/scenarios/scenarioBundle.js +204 -71
  156. package/dist/scenarios/scenarioBundle.js.map +1 -1
  157. package/dist/scenarios/scenarioStore.js +88 -29
  158. package/dist/scenarios/scenarioStore.js.map +1 -1
  159. package/dist/security/headerSanitizer.js +52 -10
  160. package/dist/security/headerSanitizer.js.map +1 -1
  161. package/dist/security/secrets.js +10 -5
  162. package/dist/security/secrets.js.map +1 -1
  163. package/dist/server/accessControl.js +37 -10
  164. package/dist/server/accessControl.js.map +1 -1
  165. package/dist/server/createServer.js +1 -1
  166. package/dist/server/createServer.js.map +1 -1
  167. package/dist/server/eventsStore.js +15 -5
  168. package/dist/server/eventsStore.js.map +1 -1
  169. package/dist/server/routes.js +67 -19
  170. package/dist/server/routes.js.map +1 -1
  171. package/dist/server/sse.js +1 -1
  172. package/dist/server/sse.js.map +1 -1
  173. package/dist/setup/sdkDetector.js +7 -2
  174. package/dist/setup/sdkDetector.js.map +1 -1
  175. package/dist/setup/setupGenerator.js +136 -48
  176. package/dist/setup/setupGenerator.js.map +1 -1
  177. package/dist/state/stateExtractor.js +3 -1
  178. package/dist/state/stateExtractor.js.map +1 -1
  179. package/dist/state/stateResolver.js +10 -7
  180. package/dist/state/stateResolver.js.map +1 -1
  181. package/dist/state/stateStore.js +13 -4
  182. package/dist/state/stateStore.js.map +1 -1
  183. package/dist/storage/fileStore.js +12 -4
  184. package/dist/storage/fileStore.js.map +1 -1
  185. package/dist/teamControl/controlPlane.js +846 -174
  186. package/dist/teamControl/controlPlane.js.map +1 -1
  187. package/dist/teamControl/deployment.js +19 -5
  188. package/dist/teamControl/deployment.js.map +1 -1
  189. package/dist/teamControl/index.d.ts +4 -4
  190. package/dist/teamControl/index.js +2 -2
  191. package/dist/teamControl/index.js.map +1 -1
  192. package/dist/tests/testGenerator.js +15 -7
  193. package/dist/tests/testGenerator.js.map +1 -1
  194. package/dist/trust/index.d.ts +2 -2
  195. package/dist/trust/index.js +1 -1
  196. package/dist/trust/index.js.map +1 -1
  197. package/dist/trust/trustLadder.js +309 -63
  198. package/dist/trust/trustLadder.js.map +1 -1
  199. package/dist/worlds/index.d.ts +2 -2
  200. package/dist/worlds/index.js +1 -1
  201. package/dist/worlds/index.js.map +1 -1
  202. package/dist/worlds/worlds.js +364 -65
  203. package/dist/worlds/worlds.js.map +1 -1
  204. package/docs/assets/dashboard.png +0 -0
  205. package/docs/assets/ghostapi-avatar.png +0 -0
  206. package/docs/ci.md +49 -49
  207. package/docs/github-actions.md +84 -84
  208. package/docs/mcp.md +58 -52
  209. package/docs/policy.md +63 -61
  210. package/docs/providers/authoring-packs.md +180 -175
  211. package/docs/providers/stripe-core-pack.md +84 -82
  212. package/docs/releases/README.md +13 -0
  213. package/docs/security/action-gateway-threat-model.md +34 -34
  214. package/docs/security/action-ledger-incident-replay-threat-model.md +42 -42
  215. package/docs/security/approval-inbox-threat-model.md +35 -35
  216. package/docs/security/credential-broker-threat-model.md +52 -52
  217. package/docs/security/egress-threat-model.md +126 -126
  218. package/docs/security/inventory-threat-model.md +104 -104
  219. package/docs/security/kill-switch-budgets-threat-model.md +37 -37
  220. package/docs/security/reliability-threat-model.md +111 -111
  221. package/docs/security/trust-ladder-threat-model.md +33 -33
  222. package/docs/telemetry.md +14 -0
  223. package/docs/usage.md +680 -571
  224. package/examples/README.md +10 -10
  225. package/examples/agent-instructions/README.md +39 -39
  226. package/examples/ci-smoke/README.md +21 -21
  227. package/examples/ci-smoke/ghostapi.policy.yaml +22 -22
  228. package/examples/ci-smoke/package.json +9 -9
  229. package/examples/ci-smoke/production-egress.mjs +4 -2
  230. package/examples/ci-smoke/safe.mjs +21 -17
  231. package/examples/evals/README.md +24 -24
  232. package/examples/evals/retry-after.eval.json +71 -52
  233. package/examples/generic-rest/README.md +27 -27
  234. package/examples/github-fetch/README.md +38 -35
  235. package/examples/openai-streaming/README.md +16 -16
  236. package/examples/openai-streaming/streaming-tool-call.mjs +44 -31
  237. package/examples/policy/ghostapi.policy.yaml +27 -27
  238. package/examples/record-replay/README.md +19 -19
  239. package/examples/record-replay/replay-requests.json +22 -22
  240. package/examples/record-replay/stripe-checkout.har.json +30 -30
  241. package/examples/resend-node/README.md +22 -22
  242. package/examples/stripe-node/README.md +35 -35
  243. package/examples/stripe-node/checkout-flow.mjs +87 -61
  244. package/examples/twilio-node/README.md +21 -21
  245. package/examples/worlds/README.md +19 -19
  246. package/examples/worlds/subscription-recovery.mjs +21 -12
  247. package/package.json +28 -6
  248. package/dist/landing/assets/index-BXR41fAD.css +0 -1
  249. package/dist/landing/assets/index-CxWsopzP.css +0 -1
  250. package/dist/landing/assets/index-D8_ggMRE.js +0 -55
  251. package/dist/landing/assets/index-DZlpyhqd.js +0 -55
  252. package/dist/landing/assets/index-Dk-Xezs3.js +0 -55
  253. package/dist/landing/index.html +0 -15
  254. package/dist/landing/landing.d.ts +0 -3
  255. package/dist/landing/landing.js +0 -12
  256. package/dist/landing/landing.js.map +0 -1
  257. package/docs/commercial/README.md +0 -29
  258. package/docs/commercial/data-inventory.md +0 -36
  259. package/docs/commercial/manual-invoicing.md +0 -44
  260. package/docs/commercial/metrics.md +0 -51
  261. package/docs/commercial/pricing.md +0 -103
  262. package/docs/commercial/privacy-policy-draft.md +0 -41
  263. package/docs/commercial/terms-topics-for-counsel.md +0 -51
  264. package/docs/design-partners/README.md +0 -52
  265. package/docs/design-partners/demo-narratives.md +0 -33
  266. package/docs/design-partners/design-partner-offer.md +0 -39
  267. package/docs/design-partners/discovery-questions.md +0 -28
  268. package/docs/design-partners/feedback-capture-template.md +0 -48
  269. package/docs/design-partners/icp-one-pager.md +0 -43
  270. package/docs/design-partners/onboarding-checklist.md +0 -33
  271. package/docs/design-partners/pilot-success-criteria.md +0 -27
  272. package/docs/design-partners/pricing-interview-script.md +0 -18
  273. package/docs/design-partners/telemetry-plan.md +0 -48
  274. package/docs/development/baseline.md +0 -114
  275. package/docs/development/onboarding-smoke.md +0 -37
  276. package/docs/development/verification-0.1.8.md +0 -25
  277. package/docs/enterprise-product-roadmap-ru.md +0 -1879
  278. package/docs/fundraising/README.md +0 -87
  279. package/docs/fundraising/data-room-checklist.md +0 -72
  280. package/docs/fundraising/demo-script.md +0 -52
  281. package/docs/fundraising/design-partners-50.md +0 -74
  282. package/docs/fundraising/launch-posts.md +0 -37
  283. package/docs/fundraising/metrics-and-evidence.md +0 -30
  284. package/docs/fundraising/roadmap-12-month.md +0 -25
  285. package/docs/fundraising/technical-due-diligence-index.md +0 -37
  286. package/docs/fundraising/yc-application.md +0 -25
  287. package/docs/hosted-pilot.md +0 -111
  288. package/docs/operations/disaster-recovery-runbook.md +0 -81
  289. package/docs/operations/kill-switch-runbook.md +0 -32
  290. package/docs/release-checklist.md +0 -83
  291. package/docs/release-migration-and-rollback.md +0 -63
  292. package/docs/release-readiness.md +0 -39
  293. package/docs/team-control-plane.md +0 -72
@@ -8,27 +8,47 @@ const stripeScenarios = [
8
8
  title: "Stripe card declined",
9
9
  provider: "stripe",
10
10
  description: "A confirmed Payment Intent using pm_card_chargeDeclined returns a Stripe card_error.",
11
- steps: [{
11
+ steps: [
12
+ {
12
13
  name: "Confirm declined payment intent",
13
14
  method: "POST",
14
15
  path: "/v1/payment_intents",
15
16
  status: 402,
16
- body: { error: { type: "card_error", code: "card_declined", decline_code: "generic_decline", message: "Your card was declined.", param: "payment_method" } }
17
- }]
17
+ body: {
18
+ error: {
19
+ type: "card_error",
20
+ code: "card_declined",
21
+ decline_code: "generic_decline",
22
+ message: "Your card was declined.",
23
+ param: "payment_method",
24
+ },
25
+ },
26
+ },
27
+ ],
18
28
  },
19
29
  {
20
30
  id: "stripe-subscription-payment-failed",
21
31
  title: "Stripe subscription renewal payment failed",
22
32
  provider: "stripe",
23
33
  description: "A renewal with pm_card_chargeDeclined leaves the invoice open and subscription past_due.",
24
- steps: [{
34
+ steps: [
35
+ {
25
36
  name: "Renew subscription with declined card",
26
37
  method: "POST",
27
38
  path: "/v1/subscriptions/sub_ghostapi/renew",
28
39
  status: 402,
29
- body: { error: { type: "card_error", code: "card_declined", decline_code: "generic_decline", message: "Your card was declined.", param: "payment_method" } }
30
- }]
31
- }
40
+ body: {
41
+ error: {
42
+ type: "card_error",
43
+ code: "card_declined",
44
+ decline_code: "generic_decline",
45
+ message: "Your card was declined.",
46
+ param: "payment_method",
47
+ },
48
+ },
49
+ },
50
+ ],
51
+ },
32
52
  ];
33
53
  const stripeConformanceFixtures = [
34
54
  {
@@ -37,12 +57,18 @@ const stripeConformanceFixtures = [
37
57
  method: "POST",
38
58
  path: "/v1/customers",
39
59
  query: {},
40
- headers: { "content-type": "application/x-www-form-urlencoded", "stripe-version": STRIPE_API_VERSION },
60
+ headers: {
61
+ "content-type": "application/x-www-form-urlencoded",
62
+ "stripe-version": STRIPE_API_VERSION,
63
+ },
41
64
  body: "email=ada%40example.com&name=Ada%20Lovelace&metadata%5Bsource%5D=fixture",
42
- receivedAt: "2026-08-06T12:00:00.000Z"
65
+ receivedAt: "2026-08-06T12:00:00.000Z",
43
66
  },
44
67
  assertResponse(response) {
45
- if (!isJsonObject(response.body) || response.body.object !== "customer" || response.body.id !== "cus_fixture" || response.body.email !== "ada@example.com") {
68
+ if (!isJsonObject(response.body) ||
69
+ response.body.object !== "customer" ||
70
+ response.body.id !== "cus_fixture" ||
71
+ response.body.email !== "ada@example.com") {
46
72
  return "response must contain a deterministic Stripe customer";
47
73
  }
48
74
  if (response.headers["request-id"] !== "req_fixture")
@@ -50,11 +76,13 @@ const stripeConformanceFixtures = [
50
76
  return null;
51
77
  },
52
78
  assertStateTransition(transition, response) {
53
- if (transition === null || transition.key !== "stripe:cus_fixture" || transition.value !== response.body) {
79
+ if (transition === null ||
80
+ transition.key !== "stripe:cus_fixture" ||
81
+ transition.value !== response.body) {
54
82
  return "state transition must persist the exact customer under its Stripe id";
55
83
  }
56
84
  return null;
57
- }
85
+ },
58
86
  },
59
87
  {
60
88
  name: "create a deterministic billing product",
@@ -62,23 +90,31 @@ const stripeConformanceFixtures = [
62
90
  method: "POST",
63
91
  path: "/v1/products",
64
92
  query: {},
65
- headers: { "content-type": "application/json", "stripe-version": STRIPE_API_VERSION },
93
+ headers: {
94
+ "content-type": "application/json",
95
+ "stripe-version": STRIPE_API_VERSION,
96
+ },
66
97
  body: { name: "GhostAPI Pro", metadata: { tier: "pro" } },
67
- receivedAt: "2026-08-06T12:00:00.000Z"
98
+ receivedAt: "2026-08-06T12:00:00.000Z",
68
99
  },
69
100
  assertResponse(response) {
70
- if (!isJsonObject(response.body) || response.body.object !== "product" || response.body.id !== "prod_fixture" || response.body.name !== "GhostAPI Pro") {
101
+ if (!isJsonObject(response.body) ||
102
+ response.body.object !== "product" ||
103
+ response.body.id !== "prod_fixture" ||
104
+ response.body.name !== "GhostAPI Pro") {
71
105
  return "response must contain a deterministic Stripe billing product";
72
106
  }
73
107
  return null;
74
108
  },
75
109
  assertStateTransition(transition, response) {
76
- if (transition === null || transition.key !== "stripe:prod_fixture" || transition.value !== response.body) {
110
+ if (transition === null ||
111
+ transition.key !== "stripe:prod_fixture" ||
112
+ transition.value !== response.body) {
77
113
  return "state transition must persist the exact billing product under its Stripe id";
78
114
  }
79
115
  return null;
80
- }
81
- }
116
+ },
117
+ },
82
118
  ];
83
119
  export const stripePack = {
84
120
  name: "stripe",
@@ -89,7 +125,10 @@ export const stripePack = {
89
125
  displayName: "Stripe",
90
126
  implementation: "pack",
91
127
  packVersion: STRIPE_PACK_VERSION,
92
- apiVersions: { default: STRIPE_API_VERSION, supported: [STRIPE_API_VERSION] },
128
+ apiVersions: {
129
+ default: STRIPE_API_VERSION,
130
+ supported: [STRIPE_API_VERSION],
131
+ },
93
132
  capabilities: {
94
133
  detection: true,
95
134
  requestParsing: true,
@@ -99,12 +138,12 @@ export const stripePack = {
99
138
  providerErrors: true,
100
139
  scenarios: true,
101
140
  webhooks: true,
102
- conformanceFixtures: true
103
- }
141
+ conformanceFixtures: true,
142
+ },
104
143
  },
105
144
  detection: {
106
145
  priority: 400,
107
- matches: ({ path }) => /^\/v1\/(?:customers|products|prices|subscriptions|invoices|events|payment_intents|payment_methods|checkout\/sessions|refunds)(?:\/|$)/.test(path.toLowerCase())
146
+ matches: ({ path }) => /^\/v1\/(?:customers|products|prices|subscriptions|invoices|events|payment_intents|payment_methods|checkout\/sessions|refunds)(?:\/|$)/.test(path.toLowerCase()),
108
147
  },
109
148
  parseRequest(request) {
110
149
  return parseStripeRequest(request);
@@ -119,13 +158,15 @@ export const stripePack = {
119
158
  type: "invalid_request_error",
120
159
  code: "invalid_api_version",
121
160
  param: "Stripe-Version",
122
- message: `Unsupported Stripe API version: ${requested}. Supported version: ${STRIPE_API_VERSION}.`
123
- }
161
+ message: `Unsupported Stripe API version: ${requested}. Supported version: ${STRIPE_API_VERSION}.`,
162
+ },
124
163
  };
125
164
  },
126
165
  validate(parsedRequest, request) {
127
166
  const parsed = readStripeRequest(parsedRequest);
128
- if (request.method === "POST" && request.path === "/v1/customers" && Object.keys(parsed.params).length === 0)
167
+ if (request.method === "POST" &&
168
+ request.path === "/v1/customers" &&
169
+ Object.keys(parsed.params).length === 0)
129
170
  return missingParam("email");
130
171
  if (request.method === "POST" && request.path === "/v1/payment_intents") {
131
172
  if (parsed.params.amount === undefined)
@@ -135,7 +176,9 @@ export const stripePack = {
135
176
  if (!hasCurrency(parsed.params.currency))
136
177
  return missingParam("currency");
137
178
  }
138
- if (request.method === "POST" && request.path === "/v1/payment_methods" && typeof parsed.params.type !== "string")
179
+ if (request.method === "POST" &&
180
+ request.path === "/v1/payment_methods" &&
181
+ typeof parsed.params.type !== "string")
139
182
  return missingParam("type");
140
183
  if (request.method === "POST" && request.path === "/v1/checkout/sessions") {
141
184
  if (!hasMode(parsed.params.mode))
@@ -145,7 +188,9 @@ export const stripePack = {
145
188
  if (typeof parsed.params.cancel_url !== "string")
146
189
  return missingParam("cancel_url");
147
190
  }
148
- if (request.method === "POST" && request.path === "/v1/products" && typeof parsed.params.name !== "string")
191
+ if (request.method === "POST" &&
192
+ request.path === "/v1/products" &&
193
+ typeof parsed.params.name !== "string")
149
194
  return missingParam("name");
150
195
  if (request.method === "POST" && request.path === "/v1/prices") {
151
196
  if (!hasPositiveInteger(parsed.params.unit_amount))
@@ -159,10 +204,14 @@ export const stripePack = {
159
204
  if (subscriptionPriceId(parsed.params.items) === undefined)
160
205
  return missingParam("items[0][price]");
161
206
  const trialDays = parsed.params.trial_period_days;
162
- if (trialDays !== undefined && readNonNegativeInteger(trialDays) === undefined)
207
+ if (trialDays !== undefined &&
208
+ readNonNegativeInteger(trialDays) === undefined)
163
209
  return invalidParamError("trial_period_days", "Invalid non-negative integer: trial_period_days.");
164
210
  }
165
- if (request.method === "POST" && request.path === "/v1/refunds" && typeof parsed.params.payment_intent !== "string" && typeof parsed.params.charge !== "string") {
211
+ if (request.method === "POST" &&
212
+ request.path === "/v1/refunds" &&
213
+ typeof parsed.params.payment_intent !== "string" &&
214
+ typeof parsed.params.charge !== "string") {
166
215
  return missingParam("payment_intent");
167
216
  }
168
217
  return null;
@@ -181,14 +230,14 @@ export const stripePack = {
181
230
  "content-type": "application/json",
182
231
  ...createStripeResponseHeaders(runtime),
183
232
  "stripe-version": STRIPE_API_VERSION,
184
- ...response.headers
185
- }
233
+ ...response.headers,
234
+ },
186
235
  };
187
236
  },
188
237
  createResponseHeaders({ runtime }) {
189
238
  return createStripeResponseHeaders(runtime);
190
239
  },
191
- transitionState({ request, response, runtime }) {
240
+ transitionState({ request, response, runtime, }) {
192
241
  if (response.headers["x-ghostapi-idempotency"] === "REPLAY")
193
242
  return null;
194
243
  const resource = resourceFromResponse(response.body);
@@ -196,19 +245,36 @@ export const stripePack = {
196
245
  return null;
197
246
  const parsed = parseStripeRequest(request);
198
247
  const now = Math.floor(runtime.requireCapability("clock").now().getTime() / 1000);
199
- const additionalWrites = [...(response.stateWrites ?? []), ...(parsed.idempotencyKey === undefined || request.method !== "POST" ? [] : [{
200
- key: `stripe:idempotency:${parsed.idempotencyKey}`,
201
- value: {
202
- method: request.method,
203
- path: request.path,
204
- params: stableStringify(parsed.params),
205
- created: now,
206
- response: { status: response.status, headers: response.headers, body: response.body }
207
- }
208
- }])];
248
+ const additionalWrites = [
249
+ ...(response.stateWrites ?? []),
250
+ ...(parsed.idempotencyKey === undefined || request.method !== "POST"
251
+ ? []
252
+ : [
253
+ {
254
+ key: `stripe:idempotency:${parsed.idempotencyKey}`,
255
+ value: {
256
+ method: request.method,
257
+ path: request.path,
258
+ params: stableStringify(parsed.params),
259
+ created: now,
260
+ response: {
261
+ status: response.status,
262
+ headers: response.headers,
263
+ body: response.body,
264
+ },
265
+ },
266
+ },
267
+ ]),
268
+ ];
209
269
  if (resource === null) {
210
270
  const [firstWrite, ...remainingWrites] = additionalWrites;
211
- return firstWrite === undefined ? null : { key: firstWrite.key, value: firstWrite.value, additionalWrites: remainingWrites };
271
+ return firstWrite === undefined
272
+ ? null
273
+ : {
274
+ key: firstWrite.key,
275
+ value: firstWrite.value,
276
+ additionalWrites: remainingWrites,
277
+ };
212
278
  }
213
279
  return { key: `stripe:${resource.id}`, value: resource, additionalWrites };
214
280
  },
@@ -218,10 +284,10 @@ export const stripePack = {
218
284
  },
219
285
  promptHints: [
220
286
  "This is the Stripe API billing pack with deterministic Customers, Products, Prices, Subscriptions, Invoices, Payment Intents, Payment Methods, Checkout Sessions, Refunds, and pull-based signed webhook events.",
221
- "Unsupported Stripe endpoints return a diagnostic invalid_request_error rather than a generated success."
287
+ "Unsupported Stripe endpoints return a diagnostic invalid_request_error rather than a generated success.",
222
288
  ],
223
289
  scenarios: stripeScenarios,
224
- conformanceFixtures: stripeConformanceFixtures
290
+ conformanceFixtures: stripeConformanceFixtures,
225
291
  };
226
292
  function dispatchStripeRequest(request, params, state, runtime, now) {
227
293
  const segments = request.path.split("/").filter(Boolean);
@@ -256,7 +322,17 @@ function handleProducts(method, id, action, params, query, state, runtime, now)
256
322
  if (method === "GET" && id === undefined)
257
323
  return stripeList("/v1/products", listResources(state, "product"), query);
258
324
  if (method === "POST" && id === undefined) {
259
- return stripeObject({ id: runtime.requireCapability("idGenerator").create("prod"), object: "product", active: params.active !== false, name: stringOrNull(params.name) ?? "GhostAPI product", description: stringOrNull(params.description), metadata: recordOrEmpty(params.metadata), livemode: false, created: now, updated: now });
325
+ return stripeObject({
326
+ id: runtime.requireCapability("idGenerator").create("prod"),
327
+ object: "product",
328
+ active: params.active !== false,
329
+ name: stringOrNull(params.name) ?? "GhostAPI product",
330
+ description: stringOrNull(params.description),
331
+ metadata: recordOrEmpty(params.metadata),
332
+ livemode: false,
333
+ created: now,
334
+ updated: now,
335
+ });
260
336
  }
261
337
  const product = id === undefined ? null : getResource(state, id, "product");
262
338
  if (product === null)
@@ -264,7 +340,14 @@ function handleProducts(method, id, action, params, query, state, runtime, now)
264
340
  if (method === "GET")
265
341
  return stripeObject(product);
266
342
  if (method === "POST")
267
- return stripeObject({ ...product, ...pick(params, ["name", "description", "active", "metadata"]), updated: now, metadata: isJsonObject(params.metadata) ? params.metadata : product.metadata });
343
+ return stripeObject({
344
+ ...product,
345
+ ...pick(params, ["name", "description", "active", "metadata"]),
346
+ updated: now,
347
+ metadata: isJsonObject(params.metadata)
348
+ ? params.metadata
349
+ : product.metadata,
350
+ });
268
351
  return unsupportedEndpointFor("products", method);
269
352
  }
270
353
  function handlePrices(method, id, action, params, query, state, runtime, now) {
@@ -274,7 +357,8 @@ function handlePrices(method, id, action, params, query, state, runtime, now) {
274
357
  return stripeList("/v1/prices", listResources(state, "price"), query);
275
358
  if (method === "POST" && id === undefined) {
276
359
  const productId = typeof params.product === "string" ? params.product : undefined;
277
- if (productId !== undefined && getResource(state, productId, "product") === null)
360
+ if (productId !== undefined &&
361
+ getResource(state, productId, "product") === null)
278
362
  return notFound("product", productId);
279
363
  if (!hasPositiveInteger(params.unit_amount))
280
364
  return invalidParam("unit_amount", "Invalid positive integer: unit_amount.");
@@ -283,7 +367,19 @@ function handlePrices(method, id, action, params, query, state, runtime, now) {
283
367
  const recurring = parseRecurring(params.recurring);
284
368
  if (recurring.error !== null)
285
369
  return stripeFailure(400, "invalid_request_error", recurring.error, "recurring[interval]", "parameter_invalid_enum");
286
- return stripeObject({ id: runtime.requireCapability("idGenerator").create("price"), object: "price", active: params.active !== false, currency: String(params.currency).toLowerCase(), unit_amount: params.unit_amount, product: productId ?? null, nickname: stringOrNull(params.nickname), recurring: recurring.value, metadata: recordOrEmpty(params.metadata), livemode: false, created: now });
370
+ return stripeObject({
371
+ id: runtime.requireCapability("idGenerator").create("price"),
372
+ object: "price",
373
+ active: params.active !== false,
374
+ currency: String(params.currency).toLowerCase(),
375
+ unit_amount: params.unit_amount,
376
+ product: productId ?? null,
377
+ nickname: stringOrNull(params.nickname),
378
+ recurring: recurring.value,
379
+ metadata: recordOrEmpty(params.metadata),
380
+ livemode: false,
381
+ created: now,
382
+ });
287
383
  }
288
384
  const price = id === undefined ? null : getResource(state, id, "price");
289
385
  if (price === null)
@@ -291,7 +387,13 @@ function handlePrices(method, id, action, params, query, state, runtime, now) {
291
387
  if (method === "GET")
292
388
  return stripeObject(price);
293
389
  if (method === "POST")
294
- return stripeObject({ ...price, ...pick(params, ["active", "nickname", "metadata"]), metadata: isJsonObject(params.metadata) ? params.metadata : price.metadata });
390
+ return stripeObject({
391
+ ...price,
392
+ ...pick(params, ["active", "nickname", "metadata"]),
393
+ metadata: isJsonObject(params.metadata)
394
+ ? params.metadata
395
+ : price.metadata,
396
+ });
295
397
  return unsupportedEndpointFor("prices", method);
296
398
  }
297
399
  function handleSubscriptions(method, id, action, params, query, state, runtime, now) {
@@ -311,7 +413,14 @@ function handleSubscriptions(method, id, action, params, query, state, runtime,
311
413
  if (method === "POST" && action === undefined) {
312
414
  if (params.cancel_at_period_end === true)
313
415
  return responseWithWebhookWrites({ ...subscription, cancel_at_period_end: true, updated: now }, ["customer.subscription.updated"], runtime, now);
314
- return responseWithWebhookWrites({ ...subscription, ...pick(params, ["metadata", "default_payment_method"]), metadata: isJsonObject(params.metadata) ? params.metadata : subscription.metadata, updated: now }, ["customer.subscription.updated"], runtime, now);
416
+ return responseWithWebhookWrites({
417
+ ...subscription,
418
+ ...pick(params, ["metadata", "default_payment_method"]),
419
+ metadata: isJsonObject(params.metadata)
420
+ ? params.metadata
421
+ : subscription.metadata,
422
+ updated: now,
423
+ }, ["customer.subscription.updated"], runtime, now);
315
424
  }
316
425
  if (method === "DELETE" && action === undefined)
317
426
  return updateSubscriptionStatus(subscription, "canceled", now, ["customer.subscription.deleted"], runtime);
@@ -334,7 +443,9 @@ function handleInvoices(method, id, action, params, query, state, runtime, now)
334
443
  function handleEvents(method, id, action, _params, query, state) {
335
444
  if (method === "GET" && id === undefined)
336
445
  return stripeList("/v1/events", orderedWebhookEvents(state, query), query);
337
- if (method === "GET" && id !== undefined && (action === undefined || action === "deliver")) {
446
+ if (method === "GET" &&
447
+ id !== undefined &&
448
+ (action === undefined || action === "deliver")) {
338
449
  const event = getResource(state, id, "event");
339
450
  if (event === null)
340
451
  return notFound("event", id);
@@ -358,7 +469,8 @@ function createSubscription(params, state, runtime, now) {
358
469
  return stripeFailure(400, "invalid_request_error", "Subscription prices require recurring configuration.", "items[0][price]", "parameter_invalid_empty");
359
470
  const trialDays = readNonNegativeInteger(params.trial_period_days) ?? 0;
360
471
  const id = runtime.requireCapability("idGenerator").create("sub");
361
- const periodEnd = now + (trialDays > 0 ? trialDays * 86_400 : recurringSeconds(price.recurring));
472
+ const periodEnd = now +
473
+ (trialDays > 0 ? trialDays * 86_400 : recurringSeconds(price.recurring));
362
474
  const subscription = {
363
475
  id,
364
476
  object: "subscription",
@@ -370,19 +482,45 @@ function createSubscription(params, state, runtime, now) {
370
482
  trial_start: trialDays > 0 ? now : null,
371
483
  trial_end: trialDays > 0 ? periodEnd : null,
372
484
  default_payment_method: stringOrNull(params.default_payment_method),
373
- items: { object: "list", data: [{ id: runtime.requireCapability("idGenerator").create("si"), object: "subscription_item", price: priceId, quantity: subscriptionQuantity(params.items) }] },
485
+ items: {
486
+ object: "list",
487
+ data: [
488
+ {
489
+ id: runtime.requireCapability("idGenerator").create("si"),
490
+ object: "subscription_item",
491
+ price: priceId,
492
+ quantity: subscriptionQuantity(params.items),
493
+ },
494
+ ],
495
+ },
374
496
  latest_invoice: null,
375
497
  metadata: recordOrEmpty(params.metadata),
376
498
  livemode: false,
377
499
  created: now,
378
- updated: now
500
+ updated: now,
379
501
  };
380
502
  if (trialDays === 0) {
381
503
  const invoice = createInvoice(subscription, price, runtime, now, "paid", "subscription_create");
382
- const paymentIntent = createPaymentIntent({ amount: price.unit_amount, currency: price.currency, customer: customerId, payment_method: params.default_payment_method }, runtime, now, true);
504
+ const paymentIntent = createPaymentIntent({
505
+ amount: price.unit_amount,
506
+ currency: price.currency,
507
+ customer: customerId,
508
+ payment_method: params.default_payment_method,
509
+ }, runtime, now, true);
383
510
  const createdSubscription = { ...subscription, latest_invoice: invoice.id };
384
- const response = responseWithWebhookWrites(createdSubscription, ["customer.subscription.created", "invoice.paid", "invoice.payment_succeeded"], runtime, now, { "invoice.paid": invoice, "invoice.payment_succeeded": invoice });
385
- response.stateWrites = [...(response.stateWrites ?? []), { key: `stripe:${invoice.id}`, value: { ...invoice, payment_intent: paymentIntent.id } }, { key: `stripe:${paymentIntent.id}`, value: paymentIntent }];
511
+ const response = responseWithWebhookWrites(createdSubscription, [
512
+ "customer.subscription.created",
513
+ "invoice.paid",
514
+ "invoice.payment_succeeded",
515
+ ], runtime, now, { "invoice.paid": invoice, "invoice.payment_succeeded": invoice });
516
+ response.stateWrites = [
517
+ ...(response.stateWrites ?? []),
518
+ {
519
+ key: `stripe:${invoice.id}`,
520
+ value: { ...invoice, payment_intent: paymentIntent.id },
521
+ },
522
+ { key: `stripe:${paymentIntent.id}`, value: paymentIntent },
523
+ ];
386
524
  return response;
387
525
  }
388
526
  return responseWithWebhookWrites(subscription, ["customer.subscription.created"], runtime, now);
@@ -394,7 +532,10 @@ function renewSubscription(subscription, params, state, runtime, now) {
394
532
  const price = priceId === undefined ? null : getResource(state, priceId, "price");
395
533
  if (price === null)
396
534
  return stripeFailure(500, "api_error", "Subscription has no valid recurring price.", undefined, "subscription_invalid");
397
- const declined = isDeclined(params) || isDeclined({ payment_method: params.payment_method ?? subscription.default_payment_method });
535
+ const declined = isDeclined(params) ||
536
+ isDeclined({
537
+ payment_method: params.payment_method ?? subscription.default_payment_method,
538
+ });
398
539
  const status = declined ? "open" : "paid";
399
540
  const invoice = createInvoice(subscription, price, runtime, now, status, "subscription_cycle");
400
541
  const nextStatus = declined ? "past_due" : "active";
@@ -404,7 +545,7 @@ function renewSubscription(subscription, params, state, runtime, now) {
404
545
  current_period_start: now,
405
546
  current_period_end: now + recurringSeconds(price.recurring),
406
547
  latest_invoice: invoice.id,
407
- updated: now
548
+ updated: now,
408
549
  };
409
550
  if (declined) {
410
551
  const failure = cardDeclined();
@@ -413,13 +554,29 @@ function renewSubscription(subscription, params, state, runtime, now) {
413
554
  stateWrites: [
414
555
  { key: `stripe:${invoice.id}`, value: invoice },
415
556
  { key: `stripe:${subscription.id}`, value: nextSubscription },
416
- ...createWebhookWrites(["invoice.payment_failed", "customer.subscription.updated"], nextSubscription, runtime, now)
417
- ]
557
+ ...createWebhookWrites(["invoice.payment_failed", "customer.subscription.updated"], nextSubscription, runtime, now),
558
+ ],
418
559
  };
419
560
  }
420
- const paymentIntent = createPaymentIntent({ amount: price.unit_amount, currency: price.currency, customer: subscription.customer, payment_method: params.payment_method ?? subscription.default_payment_method }, runtime, now, true);
421
- const response = responseWithWebhookWrites(nextSubscription, ["invoice.paid", "invoice.payment_succeeded", "customer.subscription.updated"], runtime, now, { "invoice.paid": invoice, "invoice.payment_succeeded": invoice });
422
- response.stateWrites = [...(response.stateWrites ?? []), { key: `stripe:${invoice.id}`, value: { ...invoice, payment_intent: paymentIntent.id } }, { key: `stripe:${paymentIntent.id}`, value: paymentIntent }];
561
+ const paymentIntent = createPaymentIntent({
562
+ amount: price.unit_amount,
563
+ currency: price.currency,
564
+ customer: subscription.customer,
565
+ payment_method: params.payment_method ?? subscription.default_payment_method,
566
+ }, runtime, now, true);
567
+ const response = responseWithWebhookWrites(nextSubscription, [
568
+ "invoice.paid",
569
+ "invoice.payment_succeeded",
570
+ "customer.subscription.updated",
571
+ ], runtime, now, { "invoice.paid": invoice, "invoice.payment_succeeded": invoice });
572
+ response.stateWrites = [
573
+ ...(response.stateWrites ?? []),
574
+ {
575
+ key: `stripe:${invoice.id}`,
576
+ value: { ...invoice, payment_intent: paymentIntent.id },
577
+ },
578
+ { key: `stripe:${paymentIntent.id}`, value: paymentIntent },
579
+ ];
423
580
  return response;
424
581
  }
425
582
  function payInvoice(invoice, params, state, runtime, now) {
@@ -427,37 +584,125 @@ function payInvoice(invoice, params, state, runtime, now) {
427
584
  return stripeObject(invoice);
428
585
  if (invoice.status === "void")
429
586
  return stripeFailure(400, "invalid_request_error", "Void invoices cannot be paid.", "id", "invoice_invalid");
430
- const subscription = typeof invoice.subscription === "string" ? getResource(state, invoice.subscription, "subscription") : null;
431
- if (isDeclined(params) || isDeclined({ payment_method: params.payment_method ?? subscription?.default_payment_method })) {
432
- const failedInvoice = { ...invoice, status: "open", paid: false, updated: now, attempt_count: numberOr(invoice.attempt_count, 0) + 1 };
433
- const writes = [{ key: `stripe:${String(invoice.id)}`, value: failedInvoice }, ...(subscription === null ? [] : [{ key: `stripe:${subscription.id}`, value: { ...subscription, status: "past_due", updated: now } }]), ...createWebhookWrites(["invoice.payment_failed", ...(subscription === null ? [] : ["customer.subscription.updated"])], failedInvoice, runtime, now)];
587
+ const subscription = typeof invoice.subscription === "string"
588
+ ? getResource(state, invoice.subscription, "subscription")
589
+ : null;
590
+ if (isDeclined(params) ||
591
+ isDeclined({
592
+ payment_method: params.payment_method ?? subscription?.default_payment_method,
593
+ })) {
594
+ const failedInvoice = {
595
+ ...invoice,
596
+ status: "open",
597
+ paid: false,
598
+ updated: now,
599
+ attempt_count: numberOr(invoice.attempt_count, 0) + 1,
600
+ };
601
+ const writes = [
602
+ { key: `stripe:${String(invoice.id)}`, value: failedInvoice },
603
+ ...(subscription === null
604
+ ? []
605
+ : [
606
+ {
607
+ key: `stripe:${subscription.id}`,
608
+ value: { ...subscription, status: "past_due", updated: now },
609
+ },
610
+ ]),
611
+ ...createWebhookWrites([
612
+ "invoice.payment_failed",
613
+ ...(subscription === null ? [] : ["customer.subscription.updated"]),
614
+ ], failedInvoice, runtime, now),
615
+ ];
434
616
  return { ...cardDeclined(), stateWrites: writes };
435
617
  }
436
- const paidInvoice = { ...invoice, status: "paid", paid: true, amount_paid: invoice.amount_due, amount_remaining: 0, updated: now, attempt_count: numberOr(invoice.attempt_count, 0) + 1 };
618
+ const paidInvoice = {
619
+ ...invoice,
620
+ status: "paid",
621
+ paid: true,
622
+ amount_paid: invoice.amount_due,
623
+ amount_remaining: 0,
624
+ updated: now,
625
+ attempt_count: numberOr(invoice.attempt_count, 0) + 1,
626
+ };
437
627
  const response = responseWithWebhookWrites(paidInvoice, ["invoice.paid", "invoice.payment_succeeded"], runtime, now);
438
628
  if (subscription !== null)
439
- response.stateWrites = [...(response.stateWrites ?? []), { key: `stripe:${subscription.id}`, value: { ...subscription, status: "active", latest_invoice: invoice.id, updated: now } }];
629
+ response.stateWrites = [
630
+ ...(response.stateWrites ?? []),
631
+ {
632
+ key: `stripe:${subscription.id}`,
633
+ value: {
634
+ ...subscription,
635
+ status: "active",
636
+ latest_invoice: invoice.id,
637
+ updated: now,
638
+ },
639
+ },
640
+ ];
440
641
  return response;
441
642
  }
442
643
  function updateSubscriptionStatus(subscription, status, now, eventTypes, runtime) {
443
- return responseWithWebhookWrites({ ...subscription, status, canceled_at: status === "canceled" ? now : null, cancel_at_period_end: false, updated: now }, eventTypes, runtime, now);
644
+ return responseWithWebhookWrites({
645
+ ...subscription,
646
+ status,
647
+ canceled_at: status === "canceled" ? now : null,
648
+ cancel_at_period_end: false,
649
+ updated: now,
650
+ }, eventTypes, runtime, now);
444
651
  }
445
652
  function createInvoice(subscription, price, runtime, now, status, billingReason) {
446
653
  const id = runtime.requireCapability("idGenerator").create("in");
447
654
  const amount = numberOr(price.unit_amount, 0);
448
- return { id, object: "invoice", customer: subscription.customer, subscription: subscription.id, status, paid: status === "paid", amount_due: amount, amount_paid: status === "paid" ? amount : 0, amount_remaining: status === "paid" ? 0 : amount, currency: price.currency, billing_reason: billingReason, payment_intent: null, attempt_count: 1, metadata: {}, livemode: false, created: now, updated: now };
655
+ return {
656
+ id,
657
+ object: "invoice",
658
+ customer: subscription.customer,
659
+ subscription: subscription.id,
660
+ status,
661
+ paid: status === "paid",
662
+ amount_due: amount,
663
+ amount_paid: status === "paid" ? amount : 0,
664
+ amount_remaining: status === "paid" ? 0 : amount,
665
+ currency: price.currency,
666
+ billing_reason: billingReason,
667
+ payment_intent: null,
668
+ attempt_count: 1,
669
+ metadata: {},
670
+ livemode: false,
671
+ created: now,
672
+ updated: now,
673
+ };
449
674
  }
450
675
  function responseWithWebhookWrites(resource, eventTypes, runtime, now, eventObjects = {}) {
451
- return { status: 200, headers: {}, body: resource, stateWrites: createWebhookWrites(eventTypes, resource, runtime, now, eventObjects) };
676
+ return {
677
+ status: 200,
678
+ headers: {},
679
+ body: resource,
680
+ stateWrites: createWebhookWrites(eventTypes, resource, runtime, now, eventObjects),
681
+ };
452
682
  }
453
683
  function createWebhookWrites(eventTypes, dataObject, runtime, now, eventObjects = {}) {
454
684
  return eventTypes.map((type) => {
455
685
  const id = runtime.requireCapability("idGenerator").create("evt");
456
- return { key: `stripe:${id}`, value: { id, object: "event", api_version: STRIPE_API_VERSION, created: now, livemode: false, pending_webhooks: 1, request: { id: null, idempotency_key: null }, type, data: { object: structuredClone(eventObjects[type] ?? dataObject) } } };
686
+ return {
687
+ key: `stripe:${id}`,
688
+ value: {
689
+ id,
690
+ object: "event",
691
+ api_version: STRIPE_API_VERSION,
692
+ created: now,
693
+ livemode: false,
694
+ pending_webhooks: 1,
695
+ request: { id: null, idempotency_key: null },
696
+ type,
697
+ data: { object: structuredClone(eventObjects[type] ?? dataObject) },
698
+ },
699
+ };
457
700
  });
458
701
  }
459
702
  function createStripeResponseHeaders(runtime) {
460
- return { "request-id": runtime.requireCapability("idGenerator").create("req") };
703
+ return {
704
+ "request-id": runtime.requireCapability("idGenerator").create("req"),
705
+ };
461
706
  }
462
707
  function handleCustomers(method, id, action, params, query, state, runtime, now) {
463
708
  if (action !== undefined)
@@ -465,14 +710,29 @@ function handleCustomers(method, id, action, params, query, state, runtime, now)
465
710
  if (method === "GET" && id === undefined)
466
711
  return stripeList("/v1/customers", listResources(state, "customer"), query);
467
712
  if (method === "POST" && id === undefined)
468
- return stripeObject({ id: runtime.requireCapability("idGenerator").create("cus"), object: "customer", email: stringOrNull(params.email), name: stringOrNull(params.name), description: stringOrNull(params.description), metadata: recordOrEmpty(params.metadata), livemode: false, created: now });
713
+ return stripeObject({
714
+ id: runtime.requireCapability("idGenerator").create("cus"),
715
+ object: "customer",
716
+ email: stringOrNull(params.email),
717
+ name: stringOrNull(params.name),
718
+ description: stringOrNull(params.description),
719
+ metadata: recordOrEmpty(params.metadata),
720
+ livemode: false,
721
+ created: now,
722
+ });
469
723
  const customer = id === undefined ? null : getResource(state, id, "customer");
470
724
  if (customer === null)
471
725
  return notFound("customer", id);
472
726
  if (method === "GET")
473
727
  return stripeObject(customer);
474
728
  if (method === "POST")
475
- return stripeObject({ ...customer, ...pick(params, ["email", "name", "description", "metadata"]), metadata: isJsonObject(params.metadata) ? params.metadata : customer.metadata });
729
+ return stripeObject({
730
+ ...customer,
731
+ ...pick(params, ["email", "name", "description", "metadata"]),
732
+ metadata: isJsonObject(params.metadata)
733
+ ? params.metadata
734
+ : customer.metadata,
735
+ });
476
736
  if (method === "DELETE")
477
737
  return stripeObject({ id, object: "customer", deleted: true });
478
738
  return unsupportedEndpointFor("customers", method);
@@ -486,7 +746,23 @@ function handlePaymentMethods(method, id, action, params, query, state, runtime,
486
746
  const type = String(params.type);
487
747
  if (type !== "card")
488
748
  return stripeFailure(400, "invalid_request_error", "Only payment method type 'card' is supported by this GhostAPI Stripe pack.", "type", "parameter_invalid_enum");
489
- return stripeObject({ id: runtime.requireCapability("idGenerator").create("pm"), object: "payment_method", type, card: { brand: "visa", country: "US", exp_month: 12, exp_year: 2030, funding: "credit", last4: "4242" }, billing_details: recordOrEmpty(params.billing_details), customer: null, livemode: false, created: now });
749
+ return stripeObject({
750
+ id: runtime.requireCapability("idGenerator").create("pm"),
751
+ object: "payment_method",
752
+ type,
753
+ card: {
754
+ brand: "visa",
755
+ country: "US",
756
+ exp_month: 12,
757
+ exp_year: 2030,
758
+ funding: "credit",
759
+ last4: "4242",
760
+ },
761
+ billing_details: recordOrEmpty(params.billing_details),
762
+ customer: null,
763
+ livemode: false,
764
+ created: now,
765
+ });
490
766
  }
491
767
  const paymentMethod = id === undefined ? null : getResource(state, id, "payment_method");
492
768
  if (paymentMethod === null)
@@ -502,7 +778,9 @@ function handlePaymentIntents(method, id, action, params, query, state, runtime,
502
778
  if (isDeclined(params))
503
779
  return cardDeclined();
504
780
  const paymentIntent = createPaymentIntent(params, runtime, now, Boolean(params.confirm));
505
- return params.confirm === true ? responseWithWebhookWrites(paymentIntent, ["payment_intent.succeeded"], runtime, now) : stripeObject(paymentIntent);
781
+ return params.confirm === true
782
+ ? responseWithWebhookWrites(paymentIntent, ["payment_intent.succeeded"], runtime, now)
783
+ : stripeObject(paymentIntent);
506
784
  }
507
785
  const paymentIntent = id === undefined ? null : getResource(state, id, "payment_intent");
508
786
  if (paymentIntent === null)
@@ -512,10 +790,28 @@ function handlePaymentIntents(method, id, action, params, query, state, runtime,
512
790
  if (method === "POST" && action === "confirm") {
513
791
  if (isDeclined(params))
514
792
  return cardDeclined();
515
- return responseWithWebhookWrites({ ...paymentIntent, ...pick(params, ["payment_method", "metadata"]), status: "succeeded", amount_received: paymentIntent.amount, latest_charge: `ch_${id}`, next_action: null }, ["payment_intent.succeeded"], runtime, now);
793
+ return responseWithWebhookWrites({
794
+ ...paymentIntent,
795
+ ...pick(params, ["payment_method", "metadata"]),
796
+ status: "succeeded",
797
+ amount_received: paymentIntent.amount,
798
+ latest_charge: `ch_${id}`,
799
+ next_action: null,
800
+ }, ["payment_intent.succeeded"], runtime, now);
516
801
  }
517
802
  if (method === "POST" && action === undefined)
518
- return stripeObject({ ...paymentIntent, ...pick(params, ["amount", "currency", "description", "metadata", "payment_method"]), amount: numberOr(paymentIntent.amount, params.amount), status: paymentIntent.status });
803
+ return stripeObject({
804
+ ...paymentIntent,
805
+ ...pick(params, [
806
+ "amount",
807
+ "currency",
808
+ "description",
809
+ "metadata",
810
+ "payment_method",
811
+ ]),
812
+ amount: numberOr(paymentIntent.amount, params.amount),
813
+ status: paymentIntent.status,
814
+ });
519
815
  return unsupportedEndpointFor("payment_intents", method);
520
816
  }
521
817
  function handleCheckoutSessions(method, id, action, params, query, state, runtime, now) {
@@ -524,8 +820,25 @@ function handleCheckoutSessions(method, id, action, params, query, state, runtim
524
820
  if (method === "GET" && id === undefined)
525
821
  return stripeList("/v1/checkout/sessions", listResources(state, "checkout.session"), query);
526
822
  if (method === "POST" && id === undefined) {
527
- const sessionId = runtime.requireCapability("idGenerator").create("cs_test");
528
- return stripeObject({ id: sessionId, object: "checkout.session", mode: params.mode, status: "open", payment_status: "unpaid", customer: stringOrNull(params.customer), success_url: params.success_url, cancel_url: params.cancel_url, url: `http://127.0.0.1:8080/v1/checkout/sessions/${sessionId}/mock-hosted-page`, amount_total: checkoutAmount(params), currency: checkoutCurrency(params), livemode: false, created: now, metadata: recordOrEmpty(params.metadata) });
823
+ const sessionId = runtime
824
+ .requireCapability("idGenerator")
825
+ .create("cs_test");
826
+ return stripeObject({
827
+ id: sessionId,
828
+ object: "checkout.session",
829
+ mode: params.mode,
830
+ status: "open",
831
+ payment_status: "unpaid",
832
+ customer: stringOrNull(params.customer),
833
+ success_url: params.success_url,
834
+ cancel_url: params.cancel_url,
835
+ url: `http://127.0.0.1:8080/v1/checkout/sessions/${sessionId}/mock-hosted-page`,
836
+ amount_total: checkoutAmount(params),
837
+ currency: checkoutCurrency(params),
838
+ livemode: false,
839
+ created: now,
840
+ metadata: recordOrEmpty(params.metadata),
841
+ });
529
842
  }
530
843
  const session = id === undefined ? null : getResource(state, id, "checkout.session");
531
844
  if (session === null)
@@ -540,17 +853,49 @@ function handleRefunds(method, id, action, params, query, state, runtime, now) {
540
853
  if (method === "GET" && id === undefined)
541
854
  return stripeList("/v1/refunds", listResources(state, "refund"), query);
542
855
  if (method === "POST" && id === undefined) {
543
- const paymentIntentId = typeof params.payment_intent === "string" ? params.payment_intent : undefined;
544
- const paymentIntent = paymentIntentId === undefined ? null : getResource(state, paymentIntentId, "payment_intent");
856
+ const paymentIntentId = typeof params.payment_intent === "string"
857
+ ? params.payment_intent
858
+ : undefined;
859
+ const paymentIntent = paymentIntentId === undefined
860
+ ? null
861
+ : getResource(state, paymentIntentId, "payment_intent");
545
862
  if (paymentIntentId !== undefined && paymentIntent === null)
546
863
  return notFound("payment_intent", paymentIntentId);
547
- const amount = typeof params.amount === "number" ? params.amount : typeof paymentIntent?.amount === "number" ? paymentIntent.amount : null;
864
+ const amount = typeof params.amount === "number"
865
+ ? params.amount
866
+ : typeof paymentIntent?.amount === "number"
867
+ ? paymentIntent.amount
868
+ : null;
548
869
  if (amount === null || !Number.isInteger(amount) || amount <= 0)
549
870
  return invalidParam("amount", "Invalid positive integer: amount.");
550
- const refund = { id: runtime.requireCapability("idGenerator").create("re"), object: "refund", amount, currency: typeof paymentIntent?.currency === "string" ? paymentIntent.currency : "usd", payment_intent: paymentIntentId ?? null, charge: typeof params.charge === "string" ? params.charge : null, status: "succeeded", reason: stringOrNull(params.reason), metadata: recordOrEmpty(params.metadata), livemode: false, created: now };
871
+ const refund = {
872
+ id: runtime.requireCapability("idGenerator").create("re"),
873
+ object: "refund",
874
+ amount,
875
+ currency: typeof paymentIntent?.currency === "string"
876
+ ? paymentIntent.currency
877
+ : "usd",
878
+ payment_intent: paymentIntentId ?? null,
879
+ charge: typeof params.charge === "string" ? params.charge : null,
880
+ status: "succeeded",
881
+ reason: stringOrNull(params.reason),
882
+ metadata: recordOrEmpty(params.metadata),
883
+ livemode: false,
884
+ created: now,
885
+ };
551
886
  const response = responseWithWebhookWrites(refund, ["charge.refunded"], runtime, now);
552
887
  if (paymentIntent !== null)
553
- response.stateWrites = [...(response.stateWrites ?? []), { key: `stripe:${paymentIntent.id}`, value: { ...paymentIntent, amount_refunded: numberOr(paymentIntent.amount_refunded, 0) + amount, status: "succeeded" } }];
888
+ response.stateWrites = [
889
+ ...(response.stateWrites ?? []),
890
+ {
891
+ key: `stripe:${paymentIntent.id}`,
892
+ value: {
893
+ ...paymentIntent,
894
+ amount_refunded: numberOr(paymentIntent.amount_refunded, 0) + amount,
895
+ status: "succeeded",
896
+ },
897
+ },
898
+ ];
554
899
  return response;
555
900
  }
556
901
  const refund = id === undefined ? null : getResource(state, id, "refund");
@@ -562,7 +907,24 @@ function handleRefunds(method, id, action, params, query, state, runtime, now) {
562
907
  }
563
908
  function createPaymentIntent(params, runtime, now, confirmed) {
564
909
  const id = runtime.requireCapability("idGenerator").create("pi");
565
- return { id, object: "payment_intent", amount: params.amount, amount_received: confirmed ? params.amount : 0, currency: params.currency, customer: stringOrNull(params.customer), description: stringOrNull(params.description), metadata: recordOrEmpty(params.metadata), payment_method: stringOrNull(params.payment_method), client_secret: `${id}_secret_ghostapi`, status: confirmed ? "succeeded" : "requires_payment_method", confirmation_method: "automatic", capture_method: "automatic", latest_charge: confirmed ? `ch_${id}` : null, livemode: false, created: now };
910
+ return {
911
+ id,
912
+ object: "payment_intent",
913
+ amount: params.amount,
914
+ amount_received: confirmed ? params.amount : 0,
915
+ currency: params.currency,
916
+ customer: stringOrNull(params.customer),
917
+ description: stringOrNull(params.description),
918
+ metadata: recordOrEmpty(params.metadata),
919
+ payment_method: stringOrNull(params.payment_method),
920
+ client_secret: `${id}_secret_ghostapi`,
921
+ status: confirmed ? "succeeded" : "requires_payment_method",
922
+ confirmation_method: "automatic",
923
+ capture_method: "automatic",
924
+ latest_charge: confirmed ? `ch_${id}` : null,
925
+ livemode: false,
926
+ created: now,
927
+ };
566
928
  }
567
929
  function resolveIdempotencyReplay(state, request, parsed, now) {
568
930
  if (parsed.idempotencyKey === undefined || !isMutation(request.method))
@@ -572,20 +934,22 @@ function resolveIdempotencyReplay(state, request, parsed, now) {
572
934
  return null;
573
935
  if (stored.created + IDEMPOTENCY_TTL_SECONDS <= now)
574
936
  return null;
575
- if (stored.method !== request.method || stored.path !== request.path || stored.params !== stableStringify(parsed.params)) {
937
+ if (stored.method !== request.method ||
938
+ stored.path !== request.path ||
939
+ stored.params !== stableStringify(parsed.params)) {
576
940
  return stripeFailure(400, "idempotency_error", "Keys for idempotent requests can only be used with the same parameters they were first used with.", undefined, "idempotency_key_in_use");
577
941
  }
578
942
  return {
579
943
  ...stored.response,
580
944
  headers: { ...stored.response.headers, "x-ghostapi-idempotency": "REPLAY" },
581
- body: restoreSyntheticClientSecret(stored.response.body)
945
+ body: restoreSyntheticClientSecret(stored.response.body),
582
946
  };
583
947
  }
584
948
  function parseStripeRequest(request) {
585
949
  const contentType = firstHeaderValue(request.headers["content-type"]) ?? "";
586
950
  return {
587
951
  params: parseStripeParams(request.body, contentType.includes("application/x-www-form-urlencoded")),
588
- idempotencyKey: firstHeaderValue(request.headers["idempotency-key"])
952
+ idempotencyKey: firstHeaderValue(request.headers["idempotency-key"]),
589
953
  };
590
954
  }
591
955
  function parseStripeParams(body, formEncoded) {
@@ -599,7 +963,10 @@ function parseStripeParams(body, formEncoded) {
599
963
  return params;
600
964
  }
601
965
  function coerceFormParams(value) {
602
- return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, coerceFormParameter(entry)]));
966
+ return Object.fromEntries(Object.entries(value).map(([key, entry]) => [
967
+ key,
968
+ coerceFormParameter(entry),
969
+ ]));
603
970
  }
604
971
  function coerceFormParameter(value) {
605
972
  if (Array.isArray(value))
@@ -664,19 +1031,47 @@ function stripeList(url, values, query) {
664
1031
  start = Math.max(0, index - limit);
665
1032
  }
666
1033
  const data = values.slice(start, start + limit);
667
- return stripeObject({ object: "list", data, has_more: start + limit < values.length, url });
1034
+ return stripeObject({
1035
+ object: "list",
1036
+ data,
1037
+ has_more: start + limit < values.length,
1038
+ url,
1039
+ });
668
1040
  }
669
1041
  function stripeObject(body) {
670
1042
  return { status: 200, headers: {}, body };
671
1043
  }
672
1044
  function stripeFailure(status, type, message, param, code) {
673
- return { status, headers: {}, body: stripeError({ status, type, message, param, code }) };
1045
+ return {
1046
+ status,
1047
+ headers: {},
1048
+ body: stripeError({ status, type, message, param, code }),
1049
+ };
674
1050
  }
675
1051
  function stripeError(details) {
676
- return { error: { type: details.type ?? "invalid_request_error", message: details.message, ...(details.code === undefined ? {} : { code: details.code }), param: details.param ?? "unknown" } };
1052
+ return {
1053
+ error: {
1054
+ type: details.type ?? "invalid_request_error",
1055
+ message: details.message,
1056
+ ...(details.code === undefined ? {} : { code: details.code }),
1057
+ param: details.param ?? "unknown",
1058
+ },
1059
+ };
677
1060
  }
678
1061
  function cardDeclined() {
679
- return { status: 402, headers: {}, body: { error: { type: "card_error", code: "card_declined", decline_code: "generic_decline", message: "Your card was declined.", param: "payment_method" } } };
1062
+ return {
1063
+ status: 402,
1064
+ headers: {},
1065
+ body: {
1066
+ error: {
1067
+ type: "card_error",
1068
+ code: "card_declined",
1069
+ decline_code: "generic_decline",
1070
+ message: "Your card was declined.",
1071
+ param: "payment_method",
1072
+ },
1073
+ },
1074
+ };
680
1075
  }
681
1076
  function unsupportedEndpoint(request) {
682
1077
  return stripeFailure(404, "invalid_request_error", `Unsupported Stripe endpoint: ${request.method} ${request.path}. This GhostAPI Stripe pack supports Customers, Payment Intents, Payment Methods, Checkout Sessions, and Refunds.`, undefined, "resource_missing");
@@ -688,13 +1083,25 @@ function notFound(resource, id) {
688
1083
  return stripeFailure(404, "invalid_request_error", `No such ${resource}: '${id ?? "unknown"}'`, "id", "resource_missing");
689
1084
  }
690
1085
  function missingParam(param) {
691
- return { status: 400, type: "invalid_request_error", code: "parameter_missing", param, message: `Missing required param: ${param}.` };
1086
+ return {
1087
+ status: 400,
1088
+ type: "invalid_request_error",
1089
+ code: "parameter_missing",
1090
+ param,
1091
+ message: `Missing required param: ${param}.`,
1092
+ };
692
1093
  }
693
1094
  function invalidParam(param, message) {
694
1095
  return stripeFailure(400, "invalid_request_error", message, param, "parameter_invalid_integer");
695
1096
  }
696
1097
  function invalidParamError(param, message) {
697
- return { status: 400, type: "invalid_request_error", code: "parameter_invalid_integer", param, message };
1098
+ return {
1099
+ status: 400,
1100
+ type: "invalid_request_error",
1101
+ code: "parameter_invalid_integer",
1102
+ param,
1103
+ message,
1104
+ };
698
1105
  }
699
1106
  function getResource(state, id, object) {
700
1107
  const value = state[`stripe:${id}`];
@@ -707,25 +1114,48 @@ function getResource(state, id, object) {
707
1114
  }
708
1115
  function listResources(state, object) {
709
1116
  return Object.values(state)
710
- .filter((value) => isJsonObject(value) && value.object === object && value.deleted !== true)
711
- .sort((left, right) => Number(right.created ?? 0) - Number(left.created ?? 0) || String(right.id).localeCompare(String(left.id)))
1117
+ .filter((value) => isJsonObject(value) &&
1118
+ value.object === object &&
1119
+ value.deleted !== true)
1120
+ .sort((left, right) => Number(right.created ?? 0) - Number(left.created ?? 0) ||
1121
+ String(right.id).localeCompare(String(left.id)))
712
1122
  .map((value) => structuredClone(value));
713
1123
  }
714
1124
  function resourceFromResponse(body) {
715
- return isJsonObject(body) && typeof body.id === "string" && typeof body.object === "string" ? body : null;
1125
+ return isJsonObject(body) &&
1126
+ typeof body.id === "string" &&
1127
+ typeof body.object === "string"
1128
+ ? body
1129
+ : null;
716
1130
  }
717
1131
  function restoreSyntheticClientSecret(body) {
718
- if (!isJsonObject(body) || body.object !== "payment_intent" || typeof body.id !== "string" || body.client_secret !== "***")
1132
+ if (!isJsonObject(body) ||
1133
+ body.object !== "payment_intent" ||
1134
+ typeof body.id !== "string" ||
1135
+ body.client_secret !== "***")
719
1136
  return body;
720
1137
  return { ...body, client_secret: `${body.id}_secret_ghostapi` };
721
1138
  }
722
1139
  function readStripeRequest(value) {
723
1140
  if (!isJsonObject(value) || !isJsonObject(value.params))
724
1141
  return { params: {}, idempotencyKey: undefined };
725
- return { params: value.params, idempotencyKey: typeof value.idempotencyKey === "string" ? value.idempotencyKey : undefined };
1142
+ return {
1143
+ params: value.params,
1144
+ idempotencyKey: typeof value.idempotencyKey === "string"
1145
+ ? value.idempotencyKey
1146
+ : undefined,
1147
+ };
726
1148
  }
727
1149
  function isStoredIdempotency(value) {
728
- return isJsonObject(value) && typeof value.method === "string" && typeof value.path === "string" && typeof value.params === "string" && typeof value.created === "number" && isJsonObject(value.response) && typeof value.response.status === "number" && isJsonObject(value.response.headers) && "body" in value.response;
1150
+ return (isJsonObject(value) &&
1151
+ typeof value.method === "string" &&
1152
+ typeof value.path === "string" &&
1153
+ typeof value.params === "string" &&
1154
+ typeof value.created === "number" &&
1155
+ isJsonObject(value.response) &&
1156
+ typeof value.response.status === "number" &&
1157
+ isJsonObject(value.response.headers) &&
1158
+ "body" in value.response);
729
1159
  }
730
1160
  function hasPositiveInteger(value) {
731
1161
  return typeof value === "number" && Number.isInteger(value) && value > 0;
@@ -740,26 +1170,43 @@ function parseRecurring(value) {
740
1170
  if (value === undefined)
741
1171
  return { value: null, error: null };
742
1172
  if (!isJsonObject(value) || typeof value.interval !== "string")
743
- return { value: null, error: "Recurring prices require recurring[interval] to be day, week, month, or year." };
1173
+ return {
1174
+ value: null,
1175
+ error: "Recurring prices require recurring[interval] to be day, week, month, or year.",
1176
+ };
744
1177
  if (!isRecurringInterval(value.interval))
745
- return { value: null, error: "Recurring prices require recurring[interval] to be day, week, month, or year." };
1178
+ return {
1179
+ value: null,
1180
+ error: "Recurring prices require recurring[interval] to be day, week, month, or year.",
1181
+ };
746
1182
  const intervalCount = readPositiveInteger(value.interval_count) ?? 1;
747
- return { value: { interval: value.interval, interval_count: intervalCount }, error: null };
1183
+ return {
1184
+ value: { interval: value.interval, interval_count: intervalCount },
1185
+ error: null,
1186
+ };
748
1187
  }
749
1188
  function recurringSeconds(value) {
750
1189
  if (!isJsonObject(value) || typeof value.interval !== "string")
751
1190
  return 30 * 86_400;
752
1191
  const count = readPositiveInteger(value.interval_count) ?? 1;
753
- const unit = value.interval === "day" ? 86_400 : value.interval === "week" ? 7 * 86_400 : value.interval === "year" ? 365 * 86_400 : 30 * 86_400;
1192
+ const unit = value.interval === "day"
1193
+ ? 86_400
1194
+ : value.interval === "week"
1195
+ ? 7 * 86_400
1196
+ : value.interval === "year"
1197
+ ? 365 * 86_400
1198
+ : 30 * 86_400;
754
1199
  return unit * count;
755
1200
  }
756
1201
  function subscriptionPriceId(items) {
757
1202
  const first = firstSubscriptionItem(items);
758
- return isJsonObject(first) && typeof first.price === "string" ? first.price : undefined;
1203
+ return isJsonObject(first) && typeof first.price === "string"
1204
+ ? first.price
1205
+ : undefined;
759
1206
  }
760
1207
  function subscriptionQuantity(items) {
761
1208
  const first = firstSubscriptionItem(items);
762
- return isJsonObject(first) ? readPositiveInteger(first.quantity) ?? 1 : 1;
1209
+ return isJsonObject(first) ? (readPositiveInteger(first.quantity) ?? 1) : 1;
763
1210
  }
764
1211
  function firstSubscriptionItem(items) {
765
1212
  if (Array.isArray(items))
@@ -779,13 +1226,17 @@ function missingParamResponse(param) {
779
1226
  return stripeFailure(details.status, details.type ?? "invalid_request_error", details.message, details.param, String(details.code));
780
1227
  }
781
1228
  function isRecurringInterval(value) {
782
- return value === "day" || value === "week" || value === "month" || value === "year";
1229
+ return (value === "day" || value === "week" || value === "month" || value === "year");
783
1230
  }
784
1231
  function isDeclined(params) {
785
- return params.payment_method === "pm_card_chargeDeclined" || params.payment_method === "tok_chargeDeclined";
1232
+ return (params.payment_method === "pm_card_chargeDeclined" ||
1233
+ params.payment_method === "tok_chargeDeclined");
786
1234
  }
787
1235
  function isMutation(method) {
788
- return method === "POST" || method === "PUT" || method === "PATCH" || method === "DELETE";
1236
+ return (method === "POST" ||
1237
+ method === "PUT" ||
1238
+ method === "PATCH" ||
1239
+ method === "DELETE");
789
1240
  }
790
1241
  function firstHeaderValue(value) {
791
1242
  return Array.isArray(value) ? value[0] : value;
@@ -797,22 +1248,37 @@ function recordOrEmpty(value) {
797
1248
  return isJsonObject(value) ? structuredClone(value) : {};
798
1249
  }
799
1250
  function pick(value, keys) {
800
- return Object.fromEntries(keys.filter((key) => value[key] !== undefined).map((key) => [key, structuredClone(value[key])]));
1251
+ return Object.fromEntries(keys
1252
+ .filter((key) => value[key] !== undefined)
1253
+ .map((key) => [key, structuredClone(value[key])]));
801
1254
  }
802
1255
  function numberOr(current, next) {
803
- return typeof next === "number" ? next : typeof current === "number" ? current : 0;
1256
+ return typeof next === "number"
1257
+ ? next
1258
+ : typeof current === "number"
1259
+ ? current
1260
+ : 0;
804
1261
  }
805
1262
  function checkoutAmount(params) {
806
1263
  return checkoutLineItems(params).reduce((total, item) => {
807
- if (total === null || !isJsonObject(item) || !isJsonObject(item.price_data) || typeof item.price_data.unit_amount !== "number")
1264
+ if (total === null ||
1265
+ !isJsonObject(item) ||
1266
+ !isJsonObject(item.price_data) ||
1267
+ typeof item.price_data.unit_amount !== "number")
808
1268
  return null;
809
- const quantity = typeof item.quantity === "number" && item.quantity > 0 ? item.quantity : 1;
1269
+ const quantity = typeof item.quantity === "number" && item.quantity > 0
1270
+ ? item.quantity
1271
+ : 1;
810
1272
  return total + item.price_data.unit_amount * quantity;
811
1273
  }, 0);
812
1274
  }
813
1275
  function checkoutCurrency(params) {
814
1276
  const first = checkoutLineItems(params)[0];
815
- return isJsonObject(first) && isJsonObject(first.price_data) && typeof first.price_data.currency === "string" ? first.price_data.currency : null;
1277
+ return isJsonObject(first) &&
1278
+ isJsonObject(first.price_data) &&
1279
+ typeof first.price_data.currency === "string"
1280
+ ? first.price_data.currency
1281
+ : null;
816
1282
  }
817
1283
  function checkoutLineItems(params) {
818
1284
  const lineItems = params.line_items;
@@ -846,7 +1312,10 @@ function stableStringify(value) {
846
1312
  if (Array.isArray(value))
847
1313
  return `[${value.map(stableStringify).join(",")}]`;
848
1314
  if (isJsonObject(value))
849
- return `{${Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, entry]) => `${JSON.stringify(key)}:${stableStringify(entry)}`).join(",")}}`;
1315
+ return `{${Object.entries(value)
1316
+ .sort(([left], [right]) => left.localeCompare(right))
1317
+ .map(([key, entry]) => `${JSON.stringify(key)}:${stableStringify(entry)}`)
1318
+ .join(",")}}`;
850
1319
  return JSON.stringify(value);
851
1320
  }
852
1321
  //# sourceMappingURL=stripePack.js.map