@dineway-ai/plugin-payment 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dineway-ai/plugin-payment",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Provider-neutral payment infrastructure and Stripe integration for Dineway plugins",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -28,7 +28,7 @@
28
28
  "license": "MIT",
29
29
  "peerDependencies": {
30
30
  "react": "^18.0.0 || ^19.0.0",
31
- "dineway": "^0.2.1"
31
+ "dineway": "^0.2.19"
32
32
  },
33
33
  "dependencies": {
34
34
  "@stripe/stripe-js": "9.9.0",
package/src/admin/form.ts CHANGED
@@ -39,11 +39,7 @@ export function buildPaymentSettingsUpdate(
39
39
  if (webhookSecret && !webhookSecret.startsWith("whsec_")) {
40
40
  throw new Error("Webhook signing secret must start with whsec_");
41
41
  }
42
- if (
43
- configuration.secretKey &&
44
- configuration.settings.mode !== form.mode &&
45
- !secretKey
46
- ) {
42
+ if (configuration.secretKey && configuration.settings.mode !== form.mode && !secretKey) {
47
43
  throw new Error("Enter the matching secret key when changing Stripe mode");
48
44
  }
49
45
  return {
package/src/admin.tsx CHANGED
@@ -1,18 +1,17 @@
1
1
  import * as React from "react";
2
2
 
3
- import type { PaymentStripeConfiguration } from "./configuration/service.js";
4
3
  import { postPaymentRoute } from "./admin/api.js";
5
4
  import {
6
5
  buildPaymentSettingsUpdate,
7
6
  paymentSettingsForm,
8
7
  type PaymentSettingsForm,
9
8
  } from "./admin/form.js";
9
+ import type { PaymentStripeConfiguration } from "./configuration/service.js";
10
10
 
11
11
  type CredentialName = "secretKey" | "webhookSecret";
12
12
 
13
13
  export function PaymentSettingsPage() {
14
- const [configuration, setConfiguration] =
15
- React.useState<PaymentStripeConfiguration | null>(null);
14
+ const [configuration, setConfiguration] = React.useState<PaymentStripeConfiguration | null>(null);
16
15
  const [form, setForm] = React.useState<PaymentSettingsForm | null>(null);
17
16
  const [loading, setLoading] = React.useState(true);
18
17
  const [pending, setPending] = React.useState(false);
@@ -26,9 +25,7 @@ export function PaymentSettingsPage() {
26
25
 
27
26
  React.useEffect(() => {
28
27
  let cancelled = false;
29
- void postPaymentRoute<{ configuration: PaymentStripeConfiguration }>(
30
- "stripe/configuration/get",
31
- )
28
+ void postPaymentRoute<{ configuration: PaymentStripeConfiguration }>("stripe/configuration/get")
32
29
  .then((response) => {
33
30
  if (!cancelled) applyConfiguration(response.configuration);
34
31
  return undefined;
@@ -94,54 +91,213 @@ export function PaymentSettingsPage() {
94
91
 
95
92
  if (loading) return <p className="text-sm text-kumo-subtle">Loading payment settings…</p>;
96
93
  if (!configuration || !form) {
97
- return <p className="rounded-lg bg-kumo-danger/10 p-4 text-sm text-kumo-danger">{error ?? "Payment configuration is unavailable."}</p>;
94
+ return (
95
+ <p className="rounded-lg bg-kumo-danger/10 p-4 text-sm text-kumo-danger">
96
+ {error ?? "Payment configuration is unavailable."}
97
+ </p>
98
+ );
98
99
  }
99
100
  const canTest = Boolean(
100
101
  configuration.settings.publishableKey &&
101
- configuration.secretKey &&
102
- form.mode === configuration.settings.mode &&
103
- form.publishableKey.trim() === configuration.settings.publishableKey &&
104
- !form.secretKey.trim(),
102
+ configuration.secretKey &&
103
+ form.mode === configuration.settings.mode &&
104
+ form.publishableKey.trim() === configuration.settings.publishableKey &&
105
+ !form.secretKey.trim(),
105
106
  );
106
107
 
107
108
  return (
108
109
  <div className="space-y-6">
109
110
  <header className="flex flex-wrap items-start justify-between gap-3">
110
- <div><h1 className="text-2xl font-semibold">Payments</h1><p className="mt-1 text-sm text-kumo-subtle">Shared Stripe account and encrypted credentials for every transactional plugin.</p></div>
111
- <span className="rounded-full bg-kumo-tint px-3 py-1 text-xs font-semibold uppercase">{configuration.settings.configurationStatus}</span>
111
+ <div>
112
+ <h1 className="text-2xl font-semibold">Payments</h1>
113
+ <p className="mt-1 text-sm text-kumo-subtle">
114
+ Shared Stripe account and encrypted credentials for every transactional plugin.
115
+ </p>
116
+ </div>
117
+ <span className="rounded-full bg-kumo-tint px-3 py-1 text-xs font-semibold uppercase">
118
+ {configuration.settings.configurationStatus}
119
+ </span>
112
120
  </header>
113
121
  <Readiness configuration={configuration} />
114
- {error ? <p className="rounded-lg bg-kumo-danger/10 p-3 text-sm text-kumo-danger" role="alert">{error}</p> : null}
115
- {notice ? <p className="rounded-lg bg-kumo-success/10 p-3 text-sm text-kumo-success" role="status">{notice}</p> : null}
116
- <form className="grid max-w-3xl gap-5 rounded-xl border border-kumo-line p-5" onSubmit={(event) => void save(event)}>
117
- <fieldset className="grid gap-2" disabled={pending}><legend className="mb-2 text-sm font-medium">Stripe mode</legend><div className="flex gap-2">{(["test", "live"] as const).map((mode) => <button type="button" key={mode} className={`rounded-lg border px-4 py-2 text-sm ${form.mode === mode ? "border-kumo-contrast bg-kumo-contrast text-kumo-base" : "border-kumo-line"}`} onClick={() => setForm({ ...form, mode })}>{mode === "test" ? "Test" : "Live"}</button>)}</div></fieldset>
118
- <Field label="Publishable key" value={form.publishableKey} onChange={(value) => setForm({ ...form, publishableKey: value })} placeholder={form.mode === "test" ? "pk_test_…" : "pk_live_…"} />
119
- <Field label="Secret key" value={form.secretKey} onChange={(value) => setForm({ ...form, secretKey: value })} placeholder={configuration.secretKey ? "Leave blank to keep the current key" : form.mode === "test" ? "sk_test_…" : "sk_live_…"} type="password" />
120
- <Credential label="Secret key" present={Boolean(configuration.secretKey)} fingerprint={configuration.secretKey?.fingerprint ?? null} onRemove={() => void removeCredential("secretKey")} disabled={pending} />
121
- <Field label="Webhook signing secret" value={form.webhookSecret} onChange={(value) => setForm({ ...form, webhookSecret: value })} placeholder={configuration.webhookSecret ? "Leave blank to keep the current secret" : "whsec_…"} type="password" />
122
- <Credential label="Webhook secret" present={Boolean(configuration.webhookSecret)} fingerprint={configuration.webhookSecret?.fingerprint ?? null} onRemove={() => void removeCredential("webhookSecret")} disabled={pending} />
123
- <div className="flex flex-wrap gap-2"><button className="rounded-lg bg-kumo-contrast px-4 py-2 text-sm font-medium text-kumo-base" disabled={pending}>{pending ? "Saving…" : "Save account"}</button><button type="button" className="rounded-lg border border-kumo-line px-4 py-2 text-sm font-medium" disabled={!canTest || pending} onClick={() => void testConnection()}>{pending ? "Working…" : "Test connection"}</button></div>
122
+ {error ? (
123
+ <p className="rounded-lg bg-kumo-danger/10 p-3 text-sm text-kumo-danger" role="alert">
124
+ {error}
125
+ </p>
126
+ ) : null}
127
+ {notice ? (
128
+ <p className="rounded-lg bg-kumo-success/10 p-3 text-sm text-kumo-success" role="status">
129
+ {notice}
130
+ </p>
131
+ ) : null}
132
+ <form
133
+ className="grid max-w-3xl gap-5 rounded-xl border border-kumo-line p-5"
134
+ onSubmit={(event) => void save(event)}
135
+ >
136
+ <fieldset className="grid gap-2" disabled={pending}>
137
+ <legend className="mb-2 text-sm font-medium">Stripe mode</legend>
138
+ <div className="flex gap-2">
139
+ {(["test", "live"] as const).map((mode) => (
140
+ <button
141
+ type="button"
142
+ key={mode}
143
+ className={`rounded-lg border px-4 py-2 text-sm ${form.mode === mode ? "border-kumo-contrast bg-kumo-contrast text-kumo-base" : "border-kumo-line"}`}
144
+ onClick={() => setForm({ ...form, mode })}
145
+ >
146
+ {mode === "test" ? "Test" : "Live"}
147
+ </button>
148
+ ))}
149
+ </div>
150
+ </fieldset>
151
+ <Field
152
+ label="Publishable key"
153
+ value={form.publishableKey}
154
+ onChange={(value) => setForm({ ...form, publishableKey: value })}
155
+ placeholder={form.mode === "test" ? "pk_test_…" : "pk_live_…"}
156
+ />
157
+ <Field
158
+ label="Secret key"
159
+ value={form.secretKey}
160
+ onChange={(value) => setForm({ ...form, secretKey: value })}
161
+ placeholder={
162
+ configuration.secretKey
163
+ ? "Leave blank to keep the current key"
164
+ : form.mode === "test"
165
+ ? "sk_test_…"
166
+ : "sk_live_…"
167
+ }
168
+ type="password"
169
+ />
170
+ <Credential
171
+ label="Secret key"
172
+ present={Boolean(configuration.secretKey)}
173
+ fingerprint={configuration.secretKey?.fingerprint ?? null}
174
+ onRemove={() => void removeCredential("secretKey")}
175
+ disabled={pending}
176
+ />
177
+ <Field
178
+ label="Webhook signing secret"
179
+ value={form.webhookSecret}
180
+ onChange={(value) => setForm({ ...form, webhookSecret: value })}
181
+ placeholder={
182
+ configuration.webhookSecret ? "Leave blank to keep the current secret" : "whsec_…"
183
+ }
184
+ type="password"
185
+ />
186
+ <Credential
187
+ label="Webhook secret"
188
+ present={Boolean(configuration.webhookSecret)}
189
+ fingerprint={configuration.webhookSecret?.fingerprint ?? null}
190
+ onRemove={() => void removeCredential("webhookSecret")}
191
+ disabled={pending}
192
+ />
193
+ <div className="flex flex-wrap gap-2">
194
+ <button
195
+ className="rounded-lg bg-kumo-contrast px-4 py-2 text-sm font-medium text-kumo-base"
196
+ disabled={pending}
197
+ >
198
+ {pending ? "Saving…" : "Save account"}
199
+ </button>
200
+ <button
201
+ type="button"
202
+ className="rounded-lg border border-kumo-line px-4 py-2 text-sm font-medium"
203
+ disabled={!canTest || pending}
204
+ onClick={() => void testConnection()}
205
+ >
206
+ {pending ? "Working…" : "Test connection"}
207
+ </button>
208
+ </div>
124
209
  </form>
125
- <section className="max-w-3xl rounded-xl bg-kumo-tint/40 p-5 text-sm text-kumo-subtle"><h2 className="font-semibold text-kumo-default">Webhook endpoint</h2><p className="mt-2">Configure Stripe to send domain events to the webhook shown by each installed transactional plugin. Venue and Restaurant validate exact request bytes with this shared signing secret.</p></section>
210
+ <section className="max-w-3xl rounded-xl bg-kumo-tint/40 p-5 text-sm text-kumo-subtle">
211
+ <h2 className="font-semibold text-kumo-default">Webhook endpoint</h2>
212
+ <p className="mt-2">
213
+ Configure Stripe to send domain events to the webhook shown by each installed
214
+ transactional plugin. Venue and Restaurant validate exact request bytes with this shared
215
+ signing secret.
216
+ </p>
217
+ </section>
126
218
  </div>
127
219
  );
128
220
  }
129
221
 
130
222
  function Readiness({ configuration }: { configuration: PaymentStripeConfiguration }) {
131
223
  const settings = configuration.settings;
132
- return <div className="grid gap-3 md:grid-cols-4"><Metric label="Account" value={settings.stripeAccountId ?? "Not verified"} /><Metric label="Charges" value={settings.chargesEnabled === null ? "Unknown" : settings.chargesEnabled ? "Enabled" : "Restricted"} /><Metric label="Tax" value={settings.taxStatus} /><Metric label="Connection" value={settings.connectStatus} /></div>;
224
+ return (
225
+ <div className="grid gap-3 md:grid-cols-4">
226
+ <Metric label="Account" value={settings.stripeAccountId ?? "Not verified"} />
227
+ <Metric
228
+ label="Charges"
229
+ value={
230
+ settings.chargesEnabled === null
231
+ ? "Unknown"
232
+ : settings.chargesEnabled
233
+ ? "Enabled"
234
+ : "Restricted"
235
+ }
236
+ />
237
+ <Metric label="Tax" value={settings.taxStatus} />
238
+ <Metric label="Connection" value={settings.connectStatus} />
239
+ </div>
240
+ );
133
241
  }
134
242
 
135
243
  function Metric({ label, value }: { label: string; value: string }) {
136
- return <div className="rounded-xl border border-kumo-line p-4"><div className="text-xs font-medium uppercase text-kumo-subtle">{label}</div><div className="mt-1 truncate font-semibold">{value}</div></div>;
244
+ return (
245
+ <div className="rounded-xl border border-kumo-line p-4">
246
+ <div className="text-xs font-medium uppercase text-kumo-subtle">{label}</div>
247
+ <div className="mt-1 truncate font-semibold">{value}</div>
248
+ </div>
249
+ );
137
250
  }
138
251
 
139
- function Field(props: { label: string; value: string; onChange: (value: string) => void; placeholder: string; type?: string }) {
140
- return <label className="grid gap-1 text-sm font-medium">{props.label}<input className="rounded-lg border border-kumo-line bg-transparent px-3 py-2 font-normal" type={props.type ?? "text"} value={props.value} onChange={(event) => props.onChange(event.target.value)} placeholder={props.placeholder} autoComplete="off" disabled={false} /></label>;
252
+ function Field(props: {
253
+ label: string;
254
+ value: string;
255
+ onChange: (value: string) => void;
256
+ placeholder: string;
257
+ type?: string;
258
+ }) {
259
+ return (
260
+ <label className="grid gap-1 text-sm font-medium">
261
+ {props.label}
262
+ <input
263
+ className="rounded-lg border border-kumo-line bg-transparent px-3 py-2 font-normal"
264
+ type={props.type ?? "text"}
265
+ value={props.value}
266
+ onChange={(event) => props.onChange(event.target.value)}
267
+ placeholder={props.placeholder}
268
+ autoComplete="off"
269
+ disabled={false}
270
+ />
271
+ </label>
272
+ );
141
273
  }
142
274
 
143
- function Credential(props: { label: string; present: boolean; fingerprint: string | null; onRemove: () => void; disabled: boolean }) {
144
- return <div className="flex flex-wrap items-center justify-between gap-3 rounded-lg bg-kumo-tint/50 px-3 py-2 text-sm"><span>{props.present ? `${props.label} stored · ${props.fingerprint ?? "encrypted"}` : `${props.label} not stored`}</span>{props.present ? <button type="button" className="text-kumo-danger underline" onClick={props.onRemove} disabled={props.disabled}>Remove</button> : null}</div>;
275
+ function Credential(props: {
276
+ label: string;
277
+ present: boolean;
278
+ fingerprint: string | null;
279
+ onRemove: () => void;
280
+ disabled: boolean;
281
+ }) {
282
+ return (
283
+ <div className="flex flex-wrap items-center justify-between gap-3 rounded-lg bg-kumo-tint/50 px-3 py-2 text-sm">
284
+ <span>
285
+ {props.present
286
+ ? `${props.label} stored · ${props.fingerprint ?? "encrypted"}`
287
+ : `${props.label} not stored`}
288
+ </span>
289
+ {props.present ? (
290
+ <button
291
+ type="button"
292
+ className="text-kumo-danger underline"
293
+ onClick={props.onRemove}
294
+ disabled={props.disabled}
295
+ >
296
+ Remove
297
+ </button>
298
+ ) : null}
299
+ </div>
300
+ );
145
301
  }
146
302
 
147
303
  function message(value: unknown): string {
@@ -34,15 +34,11 @@ export class PaymentStripeConfigurationRepository {
34
34
  ): Promise<PaymentStripeSettings> {
35
35
  const update: Updateable<Database["payment_stripe_settings"]> = {
36
36
  ...(input.mode === undefined ? {} : { mode: input.mode }),
37
- ...(input.publishableKey === undefined
38
- ? {}
39
- : { publishable_key: input.publishableKey }),
37
+ ...(input.publishableKey === undefined ? {} : { publishable_key: input.publishableKey }),
40
38
  ...(input.configurationStatus === undefined
41
39
  ? {}
42
40
  : { configuration_status: input.configurationStatus }),
43
- ...(input.stripeAccountId === undefined
44
- ? {}
45
- : { stripe_account_id: input.stripeAccountId }),
41
+ ...(input.stripeAccountId === undefined ? {} : { stripe_account_id: input.stripeAccountId }),
46
42
  ...(input.chargesEnabled === undefined
47
43
  ? {}
48
44
  : { charges_enabled: optionalBoolean(input.chargesEnabled) }),
@@ -53,12 +49,8 @@ export class PaymentStripeConfigurationRepository {
53
49
  ...(input.validationErrorCode === undefined
54
50
  ? {}
55
51
  : { validation_error_code: input.validationErrorCode }),
56
- ...(input.lastValidatedAt === undefined
57
- ? {}
58
- : { last_validated_at: input.lastValidatedAt }),
59
- ...(input.connectStatus === undefined
60
- ? {}
61
- : { connect_status: input.connectStatus }),
52
+ ...(input.lastValidatedAt === undefined ? {} : { last_validated_at: input.lastValidatedAt }),
53
+ ...(input.connectStatus === undefined ? {} : { connect_status: input.connectStatus }),
62
54
  version: expectedVersion + 1,
63
55
  updated_at: new Date().toISOString(),
64
56
  };
@@ -1,8 +1,4 @@
1
- import type {
2
- Database,
3
- PluginSecretStatus,
4
- PluginSecretStoreOptions,
5
- } from "dineway";
1
+ import type { Database, PluginSecretStatus, PluginSecretStoreOptions } from "dineway";
6
2
  import type { Kysely, Transaction } from "kysely";
7
3
 
8
4
  import type { StripeAccountProbe, StripeMode } from "../types.js";
@@ -103,7 +99,9 @@ export async function updatePaymentStripeConfiguration(
103
99
  taxStatus: "unknown" as const,
104
100
  validationErrorCode: null,
105
101
  lastValidatedAt: null,
106
- connectStatus: credentialsComplete ? ("restricted" as const) : ("not_configured" as const),
102
+ connectStatus: credentialsComplete
103
+ ? ("restricted" as const)
104
+ : ("not_configured" as const),
107
105
  }
108
106
  : {}),
109
107
  });
@@ -143,28 +141,31 @@ export async function testPaymentStripeConnection(
143
141
 
144
142
  return db.transaction().execute(async (trx) => {
145
143
  const repository = new PaymentStripeConfigurationRepository(trx);
146
- await repository.updateSettings(before.version, result.ok
147
- ? {
148
- configurationStatus: "valid",
149
- stripeAccountId: result.accountId,
150
- chargesEnabled: result.chargesEnabled,
151
- detailsSubmitted: result.detailsSubmitted,
152
- taxStatus: result.taxStatus,
153
- validationErrorCode: null,
154
- lastValidatedAt: new Date().toISOString(),
155
- connectStatus:
156
- result.chargesEnabled && result.detailsSubmitted ? "ready" : "restricted",
157
- }
158
- : {
159
- configurationStatus: "invalid",
160
- stripeAccountId: null,
161
- chargesEnabled: null,
162
- detailsSubmitted: null,
163
- taxStatus: "unknown",
164
- validationErrorCode: result.errorCode,
165
- lastValidatedAt: new Date().toISOString(),
166
- connectStatus: "restricted",
167
- });
144
+ await repository.updateSettings(
145
+ before.version,
146
+ result.ok
147
+ ? {
148
+ configurationStatus: "valid",
149
+ stripeAccountId: result.accountId,
150
+ chargesEnabled: result.chargesEnabled,
151
+ detailsSubmitted: result.detailsSubmitted,
152
+ taxStatus: result.taxStatus,
153
+ validationErrorCode: null,
154
+ lastValidatedAt: new Date().toISOString(),
155
+ connectStatus:
156
+ result.chargesEnabled && result.detailsSubmitted ? "ready" : "restricted",
157
+ }
158
+ : {
159
+ configurationStatus: "invalid",
160
+ stripeAccountId: null,
161
+ chargesEnabled: null,
162
+ detailsSubmitted: null,
163
+ taxStatus: "unknown",
164
+ validationErrorCode: result.errorCode,
165
+ lastValidatedAt: new Date().toISOString(),
166
+ connectStatus: "restricted",
167
+ },
168
+ );
168
169
  return loadConfiguration(trx, secretOptions);
169
170
  });
170
171
  }
@@ -196,7 +197,10 @@ async function updateSecret(
196
197
  await store.set(name, value.trim());
197
198
  }
198
199
 
199
- function normalizeOptionalKey(value: string | null | undefined, current: string | null): string | null {
200
+ function normalizeOptionalKey(
201
+ value: string | null | undefined,
202
+ current: string | null,
203
+ ): string | null {
200
204
  if (value === undefined) return current;
201
205
  if (value === null) return null;
202
206
  return value.trim() || null;
@@ -211,7 +215,11 @@ function validateUpdate(input: UpdatePaymentStripeConfigurationInput): void {
211
215
  if (!Number.isSafeInteger(input.expectedVersion) || input.expectedVersion < 1) {
212
216
  throw new PaymentConfigurationError("VALIDATION_ERROR", "Expected version is invalid");
213
217
  }
214
- if (input.publishableKey !== undefined && input.publishableKey !== null && input.publishableKey.length > 512) {
218
+ if (
219
+ input.publishableKey !== undefined &&
220
+ input.publishableKey !== null &&
221
+ input.publishableKey.length > 512
222
+ ) {
215
223
  throw new PaymentConfigurationError("VALIDATION_ERROR", "Publishable key is too long");
216
224
  }
217
225
  if (input.secretKey !== undefined && input.secretKey !== null && input.secretKey.length > 512) {
@@ -233,9 +241,6 @@ function validateModeKeys(
233
241
  );
234
242
  }
235
243
  if (validateSecret && secretKey && !secretKey.startsWith(`sk_${keyMode}_`)) {
236
- throw new PaymentConfigurationError(
237
- "VALIDATION_ERROR",
238
- `Secret key must match ${mode} mode`,
239
- );
244
+ throw new PaymentConfigurationError("VALIDATION_ERROR", `Secret key must match ${mode} mode`);
240
245
  }
241
246
  }
@@ -1,10 +1,6 @@
1
1
  import type { StripeMode, StripeTaxStatus } from "../types.js";
2
2
 
3
- export type PaymentStripeConfigurationStatus =
4
- | "unconfigured"
5
- | "unverified"
6
- | "valid"
7
- | "invalid";
3
+ export type PaymentStripeConfigurationStatus = "unconfigured" | "unverified" | "valid" | "invalid";
8
4
  export type PaymentStripeConnectStatus = "not_configured" | "restricted" | "ready";
9
5
 
10
6
  export interface PaymentStripeSettings {
package/src/index.ts CHANGED
@@ -7,7 +7,9 @@ import { PAYMENT_PLUGIN_ID } from "./constants.js";
7
7
  import { createPaymentRoutes } from "./routes/index.js";
8
8
 
9
9
  const PAYMENT_ADMIN_ENTRY = "@dineway-ai/plugin-payment/admin";
10
- const PAYMENT_ADMIN_PAGES = [{ path: "/payments", label: "Payments", icon: "credit-card" }] as const;
10
+ const PAYMENT_ADMIN_PAGES = [
11
+ { path: "/payments", label: "Payments", icon: "credit-card" },
12
+ ] as const;
11
13
 
12
14
  export interface PaymentPluginOptions {
13
15
  appName?: string;
@@ -31,7 +31,9 @@ export interface PaymentRouteOptions {
31
31
  secretOptions?: PluginSecretStoreOptions;
32
32
  }
33
33
 
34
- export function createPaymentRoutes(options: PaymentRouteOptions = {}): Record<string, PluginRoute> {
34
+ export function createPaymentRoutes(
35
+ options: PaymentRouteOptions = {},
36
+ ): Record<string, PluginRoute> {
35
37
  const probe = options.accountProbe ?? new StripeNodeAccountProbe();
36
38
  return {
37
39
  "stripe/configuration/get": route(getPaymentStripeConfigurationSchema, async () => ({
@@ -48,11 +50,7 @@ export function createPaymentRoutes(options: PaymentRouteOptions = {}): Record<s
48
50
  }),
49
51
  ),
50
52
  "stripe/configuration/test": route(testPaymentStripeConnectionSchema, async () => ({
51
- configuration: await testPaymentStripeConnection(
52
- await getDb(),
53
- probe,
54
- options.secretOptions,
55
- ),
53
+ configuration: await testPaymentStripeConnection(await getDb(), probe, options.secretOptions),
56
54
  })),
57
55
  };
58
56
  }
@@ -1,11 +1,11 @@
1
1
  import Stripe from "stripe";
2
2
 
3
- import { stripeProviderFailure } from "./errors.js";
4
3
  import type {
5
4
  StripeAccountProbe,
6
5
  StripeAccountProbeInput,
7
6
  StripeAccountProbeResult,
8
7
  } from "../types.js";
8
+ import { stripeProviderFailure } from "./errors.js";
9
9
 
10
10
  export interface StripeAccountProbeClient {
11
11
  retrieveCurrentAccount(): Promise<{
@@ -360,7 +360,11 @@ function stripeAddress(address: StripeAddress) {
360
360
  }
361
361
 
362
362
  function assertTaxCalculationInput(input: StripeTaxCalculationInput): void {
363
- if (!input.secretKey.trim() || !input.idempotencyKey.trim() || !CURRENCY_PATTERN.test(input.currency)) {
363
+ if (
364
+ !input.secretKey.trim() ||
365
+ !input.idempotencyKey.trim() ||
366
+ !CURRENCY_PATTERN.test(input.currency)
367
+ ) {
364
368
  throw new Error("Stripe Tax credentials or currency are invalid");
365
369
  }
366
370
  if (input.lines.length === 0 || input.lines.length > 100) {
@@ -399,7 +403,10 @@ function assertTaxCalculationResponse(
399
403
  }
400
404
  }
401
405
  const responseLines = new Map(response.lineItems.map((line) => [line.reference, line] as const));
402
- if (response.lineItems.length !== input.lines.length || responseLines.size !== input.lines.length) {
406
+ if (
407
+ response.lineItems.length !== input.lines.length ||
408
+ responseLines.size !== input.lines.length
409
+ ) {
403
410
  throw new Error("Stripe Tax response line set does not match the request");
404
411
  }
405
412
  let calculatedTaxMinor = 0;
@@ -459,7 +466,10 @@ function assertPaymentIntentResponse(
459
466
  }
460
467
 
461
468
  function assertOperationInput(
462
- input: StripeCancelPaymentIntentInput | StripeCapturePaymentIntentInput | StripeIncrementAuthorizationInput,
469
+ input:
470
+ | StripeCancelPaymentIntentInput
471
+ | StripeCapturePaymentIntentInput
472
+ | StripeIncrementAuthorizationInput,
463
473
  requireAmount: boolean,
464
474
  ): void {
465
475
  if (
@@ -517,7 +527,10 @@ function assertCancelResponse(
517
527
  }
518
528
 
519
529
  function assertMatchingOperationResponse(
520
- input: StripeCancelPaymentIntentInput | StripeCapturePaymentIntentInput | StripeIncrementAuthorizationInput,
530
+ input:
531
+ | StripeCancelPaymentIntentInput
532
+ | StripeCapturePaymentIntentInput
533
+ | StripeIncrementAuthorizationInput,
521
534
  response: StripeCheckoutPaymentIntentResponse,
522
535
  ): void {
523
536
  if (
@@ -553,7 +566,8 @@ function validMetadata(metadata: Record<string, string> | undefined): boolean {
553
566
  return (
554
567
  entries.length <= 50 &&
555
568
  entries.every(
556
- ([key, value]) => key.length > 0 && key.length <= 40 && value.length > 0 && value.length <= 500,
569
+ ([key, value]) =>
570
+ key.length > 0 && key.length <= 40 && value.length > 0 && value.length <= 500,
557
571
  )
558
572
  );
559
573
  }
@@ -125,7 +125,8 @@ function validMetadata(metadata: Record<string, string> | undefined): boolean {
125
125
  return (
126
126
  entries.length <= 50 &&
127
127
  entries.every(
128
- ([key, value]) => key.length > 0 && key.length <= 40 && value.length > 0 && value.length <= 500,
128
+ ([key, value]) =>
129
+ key.length > 0 && key.length <= 40 && value.length > 0 && value.length <= 500,
129
130
  )
130
131
  );
131
132
  }
@@ -96,8 +96,7 @@ function projectEvent(event: Stripe.Event, rawBody: Uint8Array): StripeWebhookEv
96
96
  payloadSha256: `sha256:${createHash("sha256").update(rawBody).digest("hex")}`,
97
97
  objectType,
98
98
  objectId,
99
- paymentIntent:
100
- objectType === "payment_intent" ? projectPaymentIntent(objectRecord) : null,
99
+ paymentIntent: objectType === "payment_intent" ? projectPaymentIntent(objectRecord) : null,
101
100
  refund: objectType === "refund" ? projectRefund(objectRecord) : null,
102
101
  };
103
102
  }