better-ship 0.20.0 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/dist/{application-Dg1jIKPe.js → application-D5tNECiC.js} +2 -2
  2. package/dist/application-D5tNECiC.js.map +1 -0
  3. package/dist/application.d.ts +2 -2
  4. package/dist/application.js +1 -1
  5. package/dist/better-auth/apple-client-secret.js +1 -1
  6. package/dist/better-auth/auth-client.js +1 -1
  7. package/dist/better-auth/rate-limit.js +1 -1
  8. package/dist/better-auth.js +1 -1
  9. package/dist/billing/client.js +1 -1
  10. package/dist/cloudflare/auth-secondary-storage.js +1 -1
  11. package/dist/cloudflare/durable-object-client.js +1 -1
  12. package/dist/cloudflare/durable-object-errors.js +1 -1
  13. package/dist/cloudflare/kv-client.js +1 -1
  14. package/dist/cloudflare/kv-errors.js +1 -1
  15. package/dist/cloudflare/queue.d.ts +1 -1
  16. package/dist/cloudflare/queue.js +2 -2
  17. package/dist/cloudflare/r2-client.js +1 -1
  18. package/dist/cloudflare/r2-errors.js +1 -1
  19. package/dist/cloudflare/scheduled.js +1 -1
  20. package/dist/cloudflare.d.ts +2 -2
  21. package/dist/cloudflare.js +3 -3
  22. package/dist/{core-BT1dSEQI.js → core-BIas38R0.js} +2 -2
  23. package/dist/core-BIas38R0.js.map +1 -0
  24. package/dist/core.js +1 -1
  25. package/dist/http.js +2 -2
  26. package/dist/{index-CYHXOH08.d.ts → index-tNZHSCpJ.d.ts} +6 -2
  27. package/dist/index-tNZHSCpJ.d.ts.map +1 -0
  28. package/dist/{outbox-repository-options-B8TnAE1h.js → outbox-repository-options-D0R8z1yS.js} +2 -2
  29. package/dist/{outbox-repository-options-B8TnAE1h.js.map → outbox-repository-options-D0R8z1yS.js.map} +1 -1
  30. package/dist/{outbox-repository-options-DjBgFhMi.d.ts → outbox-repository-options-_B8l8l6l.d.ts} +2 -2
  31. package/dist/{outbox-repository-options-DjBgFhMi.d.ts.map → outbox-repository-options-_B8l8l6l.d.ts.map} +1 -1
  32. package/dist/postgres.d.ts +2 -2
  33. package/dist/postgres.js +3 -3
  34. package/dist/posthog/metrics.d.ts +1 -1
  35. package/dist/posthog/server.d.ts +1 -1
  36. package/dist/posthog/server.js +1 -1
  37. package/dist/resend.d.ts +14 -16
  38. package/dist/resend.d.ts.map +1 -1
  39. package/dist/resend.js +42 -19
  40. package/dist/resend.js.map +1 -1
  41. package/dist/sentry/client.js +1 -1
  42. package/dist/sentry/server.d.ts +1 -1
  43. package/dist/stripe/client.d.ts +13 -0
  44. package/dist/stripe/client.d.ts.map +1 -0
  45. package/dist/stripe/client.js +20 -0
  46. package/dist/stripe/client.js.map +1 -0
  47. package/dist/stripe/event-map.d.ts +14 -0
  48. package/dist/stripe/event-map.d.ts.map +1 -0
  49. package/dist/stripe/event-map.js +19 -0
  50. package/dist/stripe/event-map.js.map +1 -0
  51. package/dist/stripe/payment-service.d.ts +4 -5
  52. package/dist/stripe/payment-service.d.ts.map +1 -1
  53. package/dist/stripe/payment-service.error.js +2 -2
  54. package/dist/stripe/payment-service.error.js.map +1 -1
  55. package/dist/stripe/payment-service.js +11 -35
  56. package/dist/stripe/payment-service.js.map +1 -1
  57. package/dist/stripe/payment-webhook.js +1 -1
  58. package/dist/stripe/request.d.ts +9 -0
  59. package/dist/stripe/request.d.ts.map +1 -0
  60. package/dist/stripe/request.js +21 -0
  61. package/dist/stripe/request.js.map +1 -0
  62. package/dist/tanstack.js +1 -1
  63. package/dist/testing/outbox-repository.d.ts +1 -1
  64. package/dist/testing/payment-service.d.ts.map +1 -1
  65. package/dist/testing/payment-service.js +12 -12
  66. package/dist/testing/payment-service.js.map +1 -1
  67. package/dist/testing/stripe.d.ts +21 -0
  68. package/dist/testing/stripe.d.ts.map +1 -0
  69. package/dist/testing/stripe.js +33 -0
  70. package/dist/testing/stripe.js.map +1 -0
  71. package/dist/{webhook-verification-qSFXv5H3.js → webhook-verification-CEUARY9X.js} +2 -2
  72. package/dist/{webhook-verification-qSFXv5H3.js.map → webhook-verification-CEUARY9X.js.map} +1 -1
  73. package/package.json +5 -1
  74. package/src/application/email-service.ts +3 -0
  75. package/src/application/index.ts +1 -0
  76. package/src/core/email-service.error.ts +1 -1
  77. package/src/infrastructure/resend/email-event.ts +3 -0
  78. package/src/infrastructure/resend/index.ts +2 -2
  79. package/src/infrastructure/resend/resend-email-service.ts +39 -17
  80. package/src/infrastructure/resend/resend-webhook.ts +15 -16
  81. package/src/infrastructure/stripe/client.ts +16 -0
  82. package/src/infrastructure/stripe/event-map.ts +25 -0
  83. package/src/infrastructure/stripe/payment-service.error.ts +2 -1
  84. package/src/infrastructure/stripe/payment-service.ts +15 -39
  85. package/src/infrastructure/stripe/request.ts +17 -0
  86. package/src/infrastructure/testing/payment-service-contract.ts +11 -14
  87. package/src/infrastructure/testing/stripe-fixture.ts +42 -0
  88. package/dist/application-Dg1jIKPe.js.map +0 -1
  89. package/dist/core-BT1dSEQI.js.map +0 -1
  90. package/dist/index-CYHXOH08.d.ts.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import { Gt as AnalyticsEventIdentity, Kt as AnalyticsEventInput, qt as AnalyticsProperties } from "../index-DUFS0SJ_.js";
2
- import { X as AnalyticsService } from "../index-CYHXOH08.js";
2
+ import { Z as AnalyticsService } from "../index-tNZHSCpJ.js";
3
3
  import { PostHogMetricsService } from "./metrics.js";
4
4
  import { PostHog, PostHogOptions } from "posthog-node";
5
5
  //#region src/infrastructure/posthog/server.d.ts
@@ -1,4 +1,4 @@
1
- import { tt as MissingConfigurationError } from "../core-BT1dSEQI.js";
1
+ import { tt as MissingConfigurationError } from "../core-BIas38R0.js";
2
2
  import { PostHogMetricsService } from "./metrics.js";
3
3
  import { PostHog } from "posthog-node";
4
4
 
package/dist/resend.d.ts CHANGED
@@ -1,26 +1,25 @@
1
1
  import { jt as BounceType } from "./index-DUFS0SJ_.js";
2
- import { B as OutboundEmail, I as EmailAccepted, R as IEmailService } from "./index-CYHXOH08.js";
2
+ import { B as OutboundEmail, I as EmailAccepted, R as IEmailService, V as ReceivedEmail } from "./index-tNZHSCpJ.js";
3
3
  import { n as WebhookVerifier } from "./webhook-verification-D1c7BY4q.js";
4
- import { Resend } from "resend";
4
+ import "resend";
5
5
  //#region src/infrastructure/resend/resend-email-service.d.ts
6
- /** The SDK operations required by the adapter; a test factory can return a plain object. */
7
- type ResendEmailClient = {
8
- readonly emails: Pick<Resend['emails'], 'send'>;
9
- };
10
6
  /**
11
7
  * Send without package retries or logs. Ported from Suparelay's resend-email-service.ts.
12
8
  * The Resend SDK itself logs API errors outside production.
13
9
  */
14
10
  export declare class ResendEmailService implements IEmailService {
15
- private readonly createClient;
11
+ private readonly apiKey;
16
12
  private readonly fromAddress;
17
- /** Load the SDK on the first send. Constructing the service does not load it. */
13
+ /** Load the SDK on the first email operation. Constructing the service does not load it. */
18
14
  static create(apiKey: string, fromAddress: string): ResendEmailService;
19
15
  private client;
20
- /** Supply an async factory. The service calls it once, on the first send, and keeps its promise. */
21
- constructor(createClient: () => Promise<ResendEmailClient>, fromAddress: string);
16
+ /** Store configuration. Load the SDK and create its client on the first email operation. */
17
+ constructor(apiKey: string, fromAddress: string);
22
18
  /** Resolve after acceptance. Provider failures throw EmailServiceError; SDK defects retain their identity. */
23
19
  sendEmail(email: OutboundEmail): Promise<EmailAccepted>;
20
+ /** Retrieve headers by the received email's provider ID. Provider failures throw EmailServiceError. */
21
+ getReceivedEmail(emailId: string): Promise<ReceivedEmail>;
22
+ private getClient;
24
23
  }
25
24
  //#endregion
26
25
  //#region src/infrastructure/resend/email-event.d.ts
@@ -30,6 +29,7 @@ type EmailEventType = (typeof SIMPLE_EMAIL_TYPES)[number] | 'email.bounced' | 'e
30
29
  type EmailEventBase = {
31
30
  readonly id: string;
32
31
  readonly emailId: string;
32
+ readonly messageId: string;
33
33
  readonly recipients: readonly string[];
34
34
  readonly subject: string;
35
35
  readonly tags: Readonly<Record<string, string>>;
@@ -54,20 +54,18 @@ type EmailEvent = (EmailEventBase & {
54
54
  });
55
55
  //#endregion
56
56
  //#region src/infrastructure/resend/resend-webhook.d.ts
57
- /** The SDK's local signature verifier. It makes no API request. */
58
- type ResendWebhookClient = Pick<Resend['webhooks'], 'verify'>;
59
57
  /** Verify Svix signatures and map Resend email events. Ported from Typist's Resend route. */
60
58
  export declare class ResendWebhook implements WebhookVerifier<EmailEvent> {
61
59
  private readonly webhookSecret;
62
- private readonly verifier;
63
60
  /** Load the SDK on the first verification. Only the webhook secret is required. */
64
61
  static create(webhookSecret: string): ResendWebhook;
65
62
  private client;
66
- /** Inject the local SDK verifier when testing. */
67
- constructor(webhookSecret: string, verifier: () => Promise<ResendWebhookClient>);
63
+ /** Store the webhook secret. Load the SDK on the first verification. */
64
+ constructor(webhookSecret: string);
68
65
  /** Verify the raw body and Svix headers. Invalid requests throw typed errors; other failures propagate. */
69
66
  verify(body: string, headers: Headers): Promise<EmailEvent | null>;
67
+ private getClient;
70
68
  }
71
69
  //#endregion
72
- export type { EmailEvent, EmailEventType, ResendEmailClient, ResendWebhookClient };
70
+ export type { EmailEvent, EmailEventType };
73
71
  //# sourceMappingURL=resend.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"resend.d.ts","names":[],"sources":["../src/infrastructure/resend/resend-email-service.ts","../src/infrastructure/resend/email-event.ts","../src/infrastructure/resend/resend-webhook.ts"],"mappings":";;;;;;KAsEY;WAA+B,QAAQ,KAAK;;;;;;qBAM3C,8BAA8B;mBAatB;mBACA;;SAZZ,OAAO,gBAAgB,sBAAsB;UAO5C;;EAGR,YACmB,oBAAoB,QAAQ,oBAC5B;;EAIb,UAAU,OAAO,gBAAgB,QAAQ;;;;cCzF3C;;KAYM,yBACA;KAgDP;WACM;WACA;WACA;WACA;WACA,MAAM,SAAS;WACf;;;KAIC,cACP;WACU;WACA;aACE,MAAM;aACN;aACA;;MAGZ;WAA4B;WAA+B;MAC3D;WAA4B;WAAgC;MAC5D;WAA4B,cAAc;;;;;KC3EnC,sBAAsB,KAAK;;qBAG1B,yBAAyB,gBAAgB;mBAcjC;mBACA;;SAbZ,OAAO,wBAAwB;UAQ9B;;EAGR,YACmB,uBACA,gBAAgB,QAAQ;;EAIrC,OAAO,cAAc,SAAS,UAAU,QAAQ"}
1
+ {"version":3,"file":"resend.d.ts","names":[],"sources":["../src/infrastructure/resend/resend-email-service.ts","../src/infrastructure/resend/email-event.ts","../src/infrastructure/resend/resend-webhook.ts"],"mappings":";;;;;;;;;qBA+Ea,8BAA8B;mBAUtB;mBACA;;SATZ,OAAO,gBAAgB,sBAAsB;UAI5C;;EAGR,YACmB,gBACA;;EAIb,UAAU,OAAO,gBAAgB,QAAQ;;EA+BzC,iBAAiB,kBAAkB,QAAQ;UAgBnC;;;;cCxIV;;KAYM,yBACA;KAiDP;WACM;WACA;WACA;WACA;WACA;WACA,MAAM,SAAS;WACf;;;KAIC,cACP;WACU;WACA;aACE,MAAM;aACN;aACA;;MAGZ;WAA4B;WAA+B;MAC3D;WAA4B;WAAgC;MAC5D;WAA4B,cAAc;;;;;qBC7ElC,yBAAyB,gBAAgB;mBASvB;;SAPtB,OAAO,wBAAwB;UAI9B;;EAGR,YAA6B;;EAGvB,OAAO,cAAc,SAAS,UAAU,QAAQ;UA0BxC"}
package/dist/resend.js CHANGED
@@ -1,5 +1,5 @@
1
- import { $ as EmailAddressSchema, J as parseBounceType, X as EmailServiceError } from "./core-BT1dSEQI.js";
2
- import { t as WebhookVerificationError } from "./webhook-verification-qSFXv5H3.js";
1
+ import { $ as EmailAddressSchema, J as parseBounceType, X as EmailServiceError } from "./core-BIas38R0.js";
2
+ import { t as WebhookVerificationError } from "./webhook-verification-CEUARY9X.js";
3
3
  import { z } from "zod";
4
4
  import { Buffer } from "node:buffer";
5
5
 
@@ -61,22 +61,21 @@ function toAttachment(attachment) {
61
61
  * The Resend SDK itself logs API errors outside production.
62
62
  */
63
63
  var ResendEmailService = class ResendEmailService {
64
- createClient;
64
+ apiKey;
65
65
  fromAddress;
66
- /** Load the SDK on the first send. Constructing the service does not load it. */
66
+ /** Load the SDK on the first email operation. Constructing the service does not load it. */
67
67
  static create(apiKey, fromAddress) {
68
- return new ResendEmailService(() => import("resend").then(({ Resend }) => new Resend(apiKey)), fromAddress);
68
+ return new ResendEmailService(apiKey, fromAddress);
69
69
  }
70
70
  client;
71
- /** Supply an async factory. The service calls it once, on the first send, and keeps its promise. */
72
- constructor(createClient, fromAddress) {
73
- this.createClient = createClient;
71
+ /** Store configuration. Load the SDK and create its client on the first email operation. */
72
+ constructor(apiKey, fromAddress) {
73
+ this.apiKey = apiKey;
74
74
  this.fromAddress = fromAddress;
75
75
  }
76
76
  /** Resolve after acceptance. Provider failures throw EmailServiceError; SDK defects retain their identity. */
77
77
  async sendEmail(email) {
78
- if (this.client === void 0) this.client = Promise.resolve().then(this.createClient);
79
- const client = await this.client;
78
+ const client = await this.getClient();
80
79
  const payload = {
81
80
  from: email.from ?? this.fromAddress,
82
81
  to: [email.to].flat(),
@@ -101,6 +100,26 @@ var ResendEmailService = class ResendEmailService {
101
100
  });
102
101
  return { providerId: result.data.id };
103
102
  }
103
+ /** Retrieve headers by the received email's provider ID. Provider failures throw EmailServiceError. */
104
+ async getReceivedEmail(emailId) {
105
+ const result = await (await this.getClient()).emails.receiving.get(emailId);
106
+ if (result.error !== null) throw new EmailServiceError({
107
+ reason: reasonFromResend(result.error),
108
+ provider: "resend",
109
+ providerCode: result.error.name,
110
+ statusCode: result.error.statusCode,
111
+ retryAfter: retryAfterSeconds(result.headers?.["retry-after"]),
112
+ cause: result.error
113
+ });
114
+ return { headers: result.data.headers ?? {} };
115
+ }
116
+ async getClient() {
117
+ if (this.client === void 0) {
118
+ const { Resend } = await import("resend");
119
+ this.client ??= new Resend(this.apiKey);
120
+ }
121
+ return this.client;
122
+ }
104
123
  };
105
124
 
106
125
  //#endregion
@@ -130,6 +149,7 @@ const EMAIL_TYPES = {
130
149
  };
131
150
  const BaseDataSchema = z.object({
132
151
  email_id: z.string().min(1),
152
+ message_id: z.string().min(1),
133
153
  to: z.array(EmailAddressSchema).min(1),
134
154
  subject: z.string(),
135
155
  tags: z.record(z.string(), z.string()).default({})
@@ -168,6 +188,7 @@ function parseEmailEvent(payload, id) {
168
188
  const base = {
169
189
  id,
170
190
  emailId: event.data.email_id,
191
+ messageId: event.data.message_id,
171
192
  recipients: event.data.to,
172
193
  subject: event.data.subject,
173
194
  tags: event.data.tags,
@@ -206,19 +227,14 @@ const SignatureErrorSchema = z.instanceof(Error).refine((error) => error.name ==
206
227
  /** Verify Svix signatures and map Resend email events. Ported from Typist's Resend route. */
207
228
  var ResendWebhook = class ResendWebhook {
208
229
  webhookSecret;
209
- verifier;
210
230
  /** Load the SDK on the first verification. Only the webhook secret is required. */
211
231
  static create(webhookSecret) {
212
- return new ResendWebhook(webhookSecret, async () => {
213
- const { Resend } = await import("resend");
214
- return new Resend("re_webhook_verification_only").webhooks;
215
- });
232
+ return new ResendWebhook(webhookSecret);
216
233
  }
217
234
  client;
218
- /** Inject the local SDK verifier when testing. */
219
- constructor(webhookSecret, verifier) {
235
+ /** Store the webhook secret. Load the SDK on the first verification. */
236
+ constructor(webhookSecret) {
220
237
  this.webhookSecret = webhookSecret;
221
- this.verifier = verifier;
222
238
  }
223
239
  /** Verify the raw body and Svix headers. Invalid requests throw typed errors; other failures propagate. */
224
240
  async verify(body, headers) {
@@ -226,7 +242,7 @@ var ResendWebhook = class ResendWebhook {
226
242
  const timestamp = headers.get("svix-timestamp");
227
243
  const signature = headers.get("svix-signature");
228
244
  if (!id || !timestamp || !signature) throw new WebhookVerificationError("missing_headers");
229
- const client = await (this.client ??= this.verifier());
245
+ const client = await this.getClient();
230
246
  let payload;
231
247
  try {
232
248
  payload = client.verify({
@@ -250,6 +266,13 @@ var ResendWebhook = class ResendWebhook {
250
266
  throw cause;
251
267
  }
252
268
  }
269
+ async getClient() {
270
+ if (this.client === void 0) {
271
+ const { Resend } = await import("resend");
272
+ this.client ??= new Resend("re_webhook_verification_only").webhooks;
273
+ }
274
+ return this.client;
275
+ }
253
276
  };
254
277
 
255
278
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"resend.js","names":[],"sources":["../src/infrastructure/resend/resend-email-service.ts","../src/infrastructure/resend/email-event.ts","../src/infrastructure/resend/resend-webhook.ts"],"sourcesContent":["import { Buffer } from 'node:buffer'\n\nimport type { Attachment, CreateEmailOptions, ErrorResponse, Resend } from 'resend'\n\nimport type { EmailAccepted, EmailAttachment, IEmailService, OutboundEmail } from '@/application'\nimport { EmailServiceError, type EmailSendFailureReason } from '@/core'\n\n// SDK additions must receive an explicit classification before the package builds.\nconst SDK_REASONS = {\n invalid_idempotency_key: 'invalid_request',\n validation_error: 'invalid_request',\n missing_api_key: 'configuration',\n restricted_api_key: 'configuration',\n invalid_api_key: 'configuration',\n not_found: 'invalid_request',\n method_not_allowed: 'invalid_request',\n invalid_idempotent_request: 'invalid_request',\n concurrent_idempotent_requests: 'idempotency_conflict',\n invalid_attachment: 'invalid_request',\n invalid_from_address: 'invalid_request',\n invalid_access: 'configuration',\n invalid_parameter: 'invalid_request',\n invalid_region: 'invalid_request',\n missing_required_field: 'invalid_request',\n monthly_quota_exceeded: 'quota_exceeded',\n daily_quota_exceeded: 'quota_exceeded',\n rate_limit_exceeded: 'rate_limited',\n security_error: 'configuration',\n application_error: 'provider_error',\n internal_server_error: 'provider_error',\n} satisfies Record<ErrorResponse['name'], EmailSendFailureReason>\n\n// Resend documents these codes before its SDK includes them in ErrorResponse.\nconst DOCUMENTED_REASONS = new Map<string, EmailSendFailureReason>([\n ['email_above_quota', 'quota_exceeded'],\n ['invalid_permission', 'configuration'],\n ['suspended_api_key', 'configuration'],\n ['resource_locked', 'idempotency_conflict'],\n ['service_unavailable', 'provider_error'],\n ['missing_required_parameter', 'invalid_request'],\n])\n\nfunction reasonFromResend(error: ErrorResponse): EmailSendFailureReason {\n if (Object.hasOwn(SDK_REASONS, error.name)) return SDK_REASONS[error.name]\n const reason = DOCUMENTED_REASONS.get(error.name)\n if (reason !== undefined) return reason\n if (error.statusCode === 429) return 'rate_limited'\n if (error.statusCode !== null && error.statusCode >= 400 && error.statusCode < 500)\n return 'invalid_request'\n return 'provider_error'\n}\n\nfunction retryAfterSeconds(value: string | undefined): number | null {\n if (value === undefined || !/^\\d+$/.test(value)) return null\n const seconds = Number(value)\n return Number.isSafeInteger(seconds) ? seconds : null\n}\n\nfunction toAttachment(attachment: EmailAttachment): Attachment {\n const result: Attachment = {\n filename: attachment.filename,\n content: ArrayBuffer.isView(attachment.content)\n ? Buffer.from(attachment.content)\n : attachment.content,\n }\n if (attachment.contentType !== undefined) result.contentType = attachment.contentType\n return result\n}\n\n/** The SDK operations required by the adapter; a test factory can return a plain object. */\nexport type ResendEmailClient = { readonly emails: Pick<Resend['emails'], 'send'> }\n\n/**\n * Send without package retries or logs. Ported from Suparelay's resend-email-service.ts.\n * The Resend SDK itself logs API errors outside production.\n */\nexport class ResendEmailService implements IEmailService {\n /** Load the SDK on the first send. Constructing the service does not load it. */\n static create(apiKey: string, fromAddress: string): ResendEmailService {\n return new ResendEmailService(\n () => import('resend').then(({ Resend }) => new Resend(apiKey)),\n fromAddress,\n )\n }\n\n private client: Promise<ResendEmailClient> | undefined\n\n /** Supply an async factory. The service calls it once, on the first send, and keeps its promise. */\n constructor(\n private readonly createClient: () => Promise<ResendEmailClient>,\n private readonly fromAddress: string,\n ) {}\n\n /** Resolve after acceptance. Provider failures throw EmailServiceError; SDK defects retain their identity. */\n async sendEmail(email: OutboundEmail): Promise<EmailAccepted> {\n if (this.client === undefined) {\n // Cache synchronous factory throws as rejections too, so later sends cannot initialize again.\n this.client = Promise.resolve().then(this.createClient)\n }\n const client = await this.client\n const payload: CreateEmailOptions = {\n from: email.from ?? this.fromAddress,\n to: [email.to].flat(),\n subject: email.subject,\n html: email.html,\n }\n if (email.text !== undefined) payload.text = email.text\n if (email.replyTo !== undefined) payload.replyTo = email.replyTo\n if (email.tags !== undefined)\n payload.tags = Object.entries(email.tags).map(([name, value]) => ({ name, value }))\n if (email.attachments !== undefined) payload.attachments = email.attachments.map(toAttachment)\n const result = await client.emails.send(\n payload,\n email.idempotencyKey === undefined ? undefined : { idempotencyKey: email.idempotencyKey },\n )\n if (result.error !== null) {\n throw new EmailServiceError({\n reason: reasonFromResend(result.error),\n provider: 'resend',\n providerCode: result.error.name,\n statusCode: result.error.statusCode,\n retryAfter: retryAfterSeconds(result.headers?.['retry-after']),\n cause: result.error,\n })\n }\n return { providerId: result.data.id }\n }\n}\n","import type { WebhookEventPayload } from 'resend'\nimport { z } from 'zod'\n\nimport { EmailAddressSchema, parseBounceType, type BounceType } from '@/core'\n\nconst SIMPLE_EMAIL_TYPES = [\n 'email.sent',\n 'email.scheduled',\n 'email.delivered',\n 'email.delivery_delayed',\n 'email.complained',\n 'email.opened',\n 'email.received',\n 'email.suppressed',\n] as const\n\n/** Resend email event names supported by this module. */\nexport type EmailEventType =\n | (typeof SIMPLE_EMAIL_TYPES)[number]\n | 'email.bounced'\n | 'email.failed'\n | 'email.clicked'\n\nconst EMAIL_TYPES = {\n 'email.sent': true,\n 'email.scheduled': true,\n 'email.delivered': true,\n 'email.delivery_delayed': true,\n 'email.complained': true,\n 'email.opened': true,\n 'email.received': true,\n 'email.suppressed': true,\n 'email.bounced': true,\n 'email.failed': true,\n 'email.clicked': true,\n} satisfies Record<Extract<WebhookEventPayload['type'], `email.${string}`>, true>\n\nconst BaseDataSchema = z.object({\n email_id: z.string().min(1),\n to: z.array(EmailAddressSchema).min(1),\n subject: z.string(),\n tags: z.record(z.string(), z.string()).default({}),\n})\n\nconst EmailPayloadSchema = z.discriminatedUnion('type', [\n z.object({ type: z.enum(SIMPLE_EMAIL_TYPES), data: BaseDataSchema }),\n z.object({\n type: z.literal('email.bounced'),\n data: BaseDataSchema.extend({\n bounce: z.object({\n type: z.string().optional(),\n subType: z.string().nullable().optional(),\n message: z.string(),\n }),\n }),\n }),\n z.object({\n type: z.literal('email.failed'),\n data: BaseDataSchema.extend({ failed: z.object({ reason: z.string() }) }),\n }),\n z.object({\n type: z.literal('email.clicked'),\n data: BaseDataSchema.extend({ click: z.object({ link: z.string() }) }),\n }),\n])\n\ntype EmailEventBase = {\n readonly id: string\n readonly emailId: string\n readonly recipients: readonly string[]\n readonly subject: string\n readonly tags: Readonly<Record<string, string>>\n readonly occurredAt: string\n}\n\n/** A verified event. `id` is the Svix identity; use `resend:event:${id}` as the command id. */\nexport type EmailEvent =\n | (EmailEventBase & {\n readonly type: 'email.bounced'\n readonly bounce: {\n readonly type: BounceType\n readonly subType: string | null\n readonly message: string\n }\n })\n | (EmailEventBase & { readonly type: 'email.failed'; readonly reason: string })\n | (EmailEventBase & { readonly type: 'email.clicked'; readonly link: string })\n | (EmailEventBase & { readonly type: (typeof SIMPLE_EMAIL_TYPES)[number] })\n\nconst EventHeaderSchema = z.object({\n type: z.string(),\n created_at: z.iso.datetime({ offset: true }),\n})\n\n/** Validate signed provider data before projecting it into the public event contract. */\nexport function parseEmailEvent(payload: WebhookEventPayload, id: string): EmailEvent | null {\n const header = EventHeaderSchema.parse(payload)\n if (!Object.hasOwn(EMAIL_TYPES, header.type)) return null\n const event = EmailPayloadSchema.parse(payload)\n const base: EmailEventBase = {\n id,\n emailId: event.data.email_id,\n recipients: event.data.to,\n subject: event.data.subject,\n tags: event.data.tags,\n occurredAt: header.created_at,\n }\n switch (event.type) {\n case 'email.bounced':\n return {\n ...base,\n type: event.type,\n bounce: {\n type: parseBounceType(event.data.bounce.type),\n subType: event.data.bounce.subType ?? null,\n message: event.data.bounce.message,\n },\n }\n case 'email.failed':\n return { ...base, type: event.type, reason: event.data.failed.reason }\n case 'email.clicked':\n return { ...base, type: event.type, link: event.data.click.link }\n default:\n return { ...base, type: event.type }\n }\n}\n","import type { Resend } from 'resend'\nimport { z } from 'zod'\n\nimport { WebhookVerificationError, type WebhookVerifier } from '../http/webhook-verification.ts'\nimport { parseEmailEvent, type EmailEvent } from './email-event.ts'\n\n// Separate standardwebhooks versions have different prototypes; its error name remains the same.\nconst SignatureErrorSchema = z\n .instanceof(Error)\n .refine((error) => error.name === 'WebhookVerificationError')\n\n/** The SDK's local signature verifier. It makes no API request. */\nexport type ResendWebhookClient = Pick<Resend['webhooks'], 'verify'>\n\n/** Verify Svix signatures and map Resend email events. Ported from Typist's Resend route. */\nexport class ResendWebhook implements WebhookVerifier<EmailEvent> {\n /** Load the SDK on the first verification. Only the webhook secret is required. */\n static create(webhookSecret: string): ResendWebhook {\n return new ResendWebhook(webhookSecret, async () => {\n const { Resend } = await import('resend')\n // The SDK requires a nonempty key even though its local verifier never uses that key.\n return new Resend('re_webhook_verification_only').webhooks\n })\n }\n\n private client: Promise<ResendWebhookClient> | undefined\n\n /** Inject the local SDK verifier when testing. */\n constructor(\n private readonly webhookSecret: string,\n private readonly verifier: () => Promise<ResendWebhookClient>,\n ) {}\n\n /** Verify the raw body and Svix headers. Invalid requests throw typed errors; other failures propagate. */\n async verify(body: string, headers: Headers): Promise<EmailEvent | null> {\n const id = headers.get('svix-id')\n const timestamp = headers.get('svix-timestamp')\n const signature = headers.get('svix-signature')\n if (!id || !timestamp || !signature) throw new WebhookVerificationError('missing_headers')\n const client = await (this.client ??= this.verifier())\n let payload: ReturnType<ResendWebhookClient['verify']>\n try {\n payload = client.verify({\n payload: body,\n headers: { id, timestamp, signature },\n webhookSecret: this.webhookSecret,\n })\n } catch (cause) {\n if (cause instanceof SyntaxError) throw new WebhookVerificationError('invalid_payload', cause)\n if (!SignatureErrorSchema.safeParse(cause).success) throw cause\n throw new WebhookVerificationError('invalid_signature', cause)\n }\n try {\n return parseEmailEvent(payload, id)\n } catch (cause) {\n if (cause instanceof z.ZodError) throw new WebhookVerificationError('invalid_payload', cause)\n throw cause\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"resend.js","names":[],"sources":["../src/infrastructure/resend/resend-email-service.ts","../src/infrastructure/resend/email-event.ts","../src/infrastructure/resend/resend-webhook.ts"],"sourcesContent":["import { Buffer } from 'node:buffer'\n\nimport type { Attachment, CreateEmailOptions, ErrorResponse, Resend } from 'resend'\n\nimport type {\n EmailAccepted,\n EmailAttachment,\n IEmailService,\n OutboundEmail,\n ReceivedEmail,\n} from '@/application'\nimport { EmailServiceError, type EmailSendFailureReason } from '@/core'\n\n// SDK additions must receive an explicit classification before the package builds.\nconst SDK_REASONS = {\n invalid_idempotency_key: 'invalid_request',\n validation_error: 'invalid_request',\n missing_api_key: 'configuration',\n restricted_api_key: 'configuration',\n invalid_api_key: 'configuration',\n not_found: 'invalid_request',\n method_not_allowed: 'invalid_request',\n invalid_idempotent_request: 'invalid_request',\n concurrent_idempotent_requests: 'idempotency_conflict',\n invalid_attachment: 'invalid_request',\n invalid_from_address: 'invalid_request',\n invalid_access: 'configuration',\n invalid_parameter: 'invalid_request',\n invalid_region: 'invalid_request',\n missing_required_field: 'invalid_request',\n monthly_quota_exceeded: 'quota_exceeded',\n daily_quota_exceeded: 'quota_exceeded',\n rate_limit_exceeded: 'rate_limited',\n security_error: 'configuration',\n application_error: 'provider_error',\n internal_server_error: 'provider_error',\n} satisfies Record<ErrorResponse['name'], EmailSendFailureReason>\n\n// Resend documents these codes before its SDK includes them in ErrorResponse.\nconst DOCUMENTED_REASONS = new Map<string, EmailSendFailureReason>([\n ['email_above_quota', 'quota_exceeded'],\n ['invalid_permission', 'configuration'],\n ['suspended_api_key', 'configuration'],\n ['resource_locked', 'idempotency_conflict'],\n ['service_unavailable', 'provider_error'],\n ['missing_required_parameter', 'invalid_request'],\n])\n\nfunction reasonFromResend(error: ErrorResponse): EmailSendFailureReason {\n if (Object.hasOwn(SDK_REASONS, error.name)) return SDK_REASONS[error.name]\n const reason = DOCUMENTED_REASONS.get(error.name)\n if (reason !== undefined) return reason\n if (error.statusCode === 429) return 'rate_limited'\n if (error.statusCode !== null && error.statusCode >= 400 && error.statusCode < 500)\n return 'invalid_request'\n return 'provider_error'\n}\n\nfunction retryAfterSeconds(value: string | undefined): number | null {\n if (value === undefined || !/^\\d+$/.test(value)) return null\n const seconds = Number(value)\n return Number.isSafeInteger(seconds) ? seconds : null\n}\n\nfunction toAttachment(attachment: EmailAttachment): Attachment {\n const result: Attachment = {\n filename: attachment.filename,\n content: ArrayBuffer.isView(attachment.content)\n ? Buffer.from(attachment.content)\n : attachment.content,\n }\n if (attachment.contentType !== undefined) result.contentType = attachment.contentType\n return result\n}\n\n/**\n * Send without package retries or logs. Ported from Suparelay's resend-email-service.ts.\n * The Resend SDK itself logs API errors outside production.\n */\nexport class ResendEmailService implements IEmailService {\n /** Load the SDK on the first email operation. Constructing the service does not load it. */\n static create(apiKey: string, fromAddress: string): ResendEmailService {\n return new ResendEmailService(apiKey, fromAddress)\n }\n\n private client: Resend | undefined\n\n /** Store configuration. Load the SDK and create its client on the first email operation. */\n constructor(\n private readonly apiKey: string,\n private readonly fromAddress: string,\n ) {}\n\n /** Resolve after acceptance. Provider failures throw EmailServiceError; SDK defects retain their identity. */\n async sendEmail(email: OutboundEmail): Promise<EmailAccepted> {\n const client = await this.getClient()\n const payload: CreateEmailOptions = {\n from: email.from ?? this.fromAddress,\n to: [email.to].flat(),\n subject: email.subject,\n html: email.html,\n }\n if (email.text !== undefined) payload.text = email.text\n if (email.replyTo !== undefined) payload.replyTo = email.replyTo\n if (email.tags !== undefined)\n payload.tags = Object.entries(email.tags).map(([name, value]) => ({ name, value }))\n if (email.attachments !== undefined) payload.attachments = email.attachments.map(toAttachment)\n const result = await client.emails.send(\n payload,\n email.idempotencyKey === undefined ? undefined : { idempotencyKey: email.idempotencyKey },\n )\n if (result.error !== null) {\n throw new EmailServiceError({\n reason: reasonFromResend(result.error),\n provider: 'resend',\n providerCode: result.error.name,\n statusCode: result.error.statusCode,\n retryAfter: retryAfterSeconds(result.headers?.['retry-after']),\n cause: result.error,\n })\n }\n return { providerId: result.data.id }\n }\n\n /** Retrieve headers by the received email's provider ID. Provider failures throw EmailServiceError. */\n async getReceivedEmail(emailId: string): Promise<ReceivedEmail> {\n const client = await this.getClient()\n const result = await client.emails.receiving.get(emailId)\n if (result.error !== null) {\n throw new EmailServiceError({\n reason: reasonFromResend(result.error),\n provider: 'resend',\n providerCode: result.error.name,\n statusCode: result.error.statusCode,\n retryAfter: retryAfterSeconds(result.headers?.['retry-after']),\n cause: result.error,\n })\n }\n return { headers: result.data.headers ?? {} }\n }\n\n private async getClient(): Promise<Resend> {\n if (this.client === undefined) {\n const { Resend } = await import('resend')\n // Recheck after the import so concurrent calls share one client.\n this.client ??= new Resend(this.apiKey)\n }\n return this.client\n }\n}\n","import type { WebhookEventPayload } from 'resend'\nimport { z } from 'zod'\n\nimport { EmailAddressSchema, parseBounceType, type BounceType } from '@/core'\n\nconst SIMPLE_EMAIL_TYPES = [\n 'email.sent',\n 'email.scheduled',\n 'email.delivered',\n 'email.delivery_delayed',\n 'email.complained',\n 'email.opened',\n 'email.received',\n 'email.suppressed',\n] as const\n\n/** Resend email event names supported by this module. */\nexport type EmailEventType =\n | (typeof SIMPLE_EMAIL_TYPES)[number]\n | 'email.bounced'\n | 'email.failed'\n | 'email.clicked'\n\nconst EMAIL_TYPES = {\n 'email.sent': true,\n 'email.scheduled': true,\n 'email.delivered': true,\n 'email.delivery_delayed': true,\n 'email.complained': true,\n 'email.opened': true,\n 'email.received': true,\n 'email.suppressed': true,\n 'email.bounced': true,\n 'email.failed': true,\n 'email.clicked': true,\n} satisfies Record<Extract<WebhookEventPayload['type'], `email.${string}`>, true>\n\nconst BaseDataSchema = z.object({\n email_id: z.string().min(1),\n message_id: z.string().min(1),\n to: z.array(EmailAddressSchema).min(1),\n subject: z.string(),\n tags: z.record(z.string(), z.string()).default({}),\n})\n\nconst EmailPayloadSchema = z.discriminatedUnion('type', [\n z.object({ type: z.enum(SIMPLE_EMAIL_TYPES), data: BaseDataSchema }),\n z.object({\n type: z.literal('email.bounced'),\n data: BaseDataSchema.extend({\n bounce: z.object({\n type: z.string().optional(),\n subType: z.string().nullable().optional(),\n message: z.string(),\n }),\n }),\n }),\n z.object({\n type: z.literal('email.failed'),\n data: BaseDataSchema.extend({ failed: z.object({ reason: z.string() }) }),\n }),\n z.object({\n type: z.literal('email.clicked'),\n data: BaseDataSchema.extend({ click: z.object({ link: z.string() }) }),\n }),\n])\n\ntype EmailEventBase = {\n readonly id: string\n readonly emailId: string\n readonly messageId: string\n readonly recipients: readonly string[]\n readonly subject: string\n readonly tags: Readonly<Record<string, string>>\n readonly occurredAt: string\n}\n\n/** A verified event. `id` is the Svix identity; use `resend:event:${id}` as the command id. */\nexport type EmailEvent =\n | (EmailEventBase & {\n readonly type: 'email.bounced'\n readonly bounce: {\n readonly type: BounceType\n readonly subType: string | null\n readonly message: string\n }\n })\n | (EmailEventBase & { readonly type: 'email.failed'; readonly reason: string })\n | (EmailEventBase & { readonly type: 'email.clicked'; readonly link: string })\n | (EmailEventBase & { readonly type: (typeof SIMPLE_EMAIL_TYPES)[number] })\n\nconst EventHeaderSchema = z.object({\n type: z.string(),\n created_at: z.iso.datetime({ offset: true }),\n})\n\n/** Validate signed provider data before projecting it into the public event contract. */\nexport function parseEmailEvent(payload: WebhookEventPayload, id: string): EmailEvent | null {\n const header = EventHeaderSchema.parse(payload)\n if (!Object.hasOwn(EMAIL_TYPES, header.type)) return null\n const event = EmailPayloadSchema.parse(payload)\n const base: EmailEventBase = {\n id,\n emailId: event.data.email_id,\n messageId: event.data.message_id,\n recipients: event.data.to,\n subject: event.data.subject,\n tags: event.data.tags,\n occurredAt: header.created_at,\n }\n switch (event.type) {\n case 'email.bounced':\n return {\n ...base,\n type: event.type,\n bounce: {\n type: parseBounceType(event.data.bounce.type),\n subType: event.data.bounce.subType ?? null,\n message: event.data.bounce.message,\n },\n }\n case 'email.failed':\n return { ...base, type: event.type, reason: event.data.failed.reason }\n case 'email.clicked':\n return { ...base, type: event.type, link: event.data.click.link }\n default:\n return { ...base, type: event.type }\n }\n}\n","import type { Resend } from 'resend'\nimport { z } from 'zod'\n\nimport { WebhookVerificationError, type WebhookVerifier } from '../http/webhook-verification.ts'\nimport { parseEmailEvent, type EmailEvent } from './email-event.ts'\n\n// Separate standardwebhooks versions have different prototypes; its error name remains the same.\nconst SignatureErrorSchema = z\n .instanceof(Error)\n .refine((error) => error.name === 'WebhookVerificationError')\n\n/** Verify Svix signatures and map Resend email events. Ported from Typist's Resend route. */\nexport class ResendWebhook implements WebhookVerifier<EmailEvent> {\n /** Load the SDK on the first verification. Only the webhook secret is required. */\n static create(webhookSecret: string): ResendWebhook {\n return new ResendWebhook(webhookSecret)\n }\n\n private client: Resend['webhooks'] | undefined\n\n /** Store the webhook secret. Load the SDK on the first verification. */\n constructor(private readonly webhookSecret: string) {}\n\n /** Verify the raw body and Svix headers. Invalid requests throw typed errors; other failures propagate. */\n async verify(body: string, headers: Headers): Promise<EmailEvent | null> {\n const id = headers.get('svix-id')\n const timestamp = headers.get('svix-timestamp')\n const signature = headers.get('svix-signature')\n if (!id || !timestamp || !signature) throw new WebhookVerificationError('missing_headers')\n const client = await this.getClient()\n let payload: ReturnType<Resend['webhooks']['verify']>\n try {\n payload = client.verify({\n payload: body,\n headers: { id, timestamp, signature },\n webhookSecret: this.webhookSecret,\n })\n } catch (cause) {\n if (cause instanceof SyntaxError) throw new WebhookVerificationError('invalid_payload', cause)\n if (!SignatureErrorSchema.safeParse(cause).success) throw cause\n throw new WebhookVerificationError('invalid_signature', cause)\n }\n try {\n return parseEmailEvent(payload, id)\n } catch (cause) {\n if (cause instanceof z.ZodError) throw new WebhookVerificationError('invalid_payload', cause)\n throw cause\n }\n }\n\n private async getClient(): Promise<Resend['webhooks']> {\n if (this.client === undefined) {\n const { Resend } = await import('resend')\n // The SDK requires a nonempty key even though its local verifier never uses that key.\n this.client ??= new Resend('re_webhook_verification_only').webhooks\n }\n return this.client\n }\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { a as setLoggerErrorHook } from "../core-BT1dSEQI.js";
1
+ import { a as setLoggerErrorHook } from "../core-BIas38R0.js";
2
2
  import * as Sentry from "@sentry/tanstackstart-react";
3
3
  import { isNotFound, isRedirect } from "@tanstack/react-router";
4
4
 
@@ -1,5 +1,5 @@
1
1
  import { u as LoggerErrorHook } from "../index-DUFS0SJ_.js";
2
- import { H as ObservabilityScope, U as ObservabilityService, W as ObservabilityUser } from "../index-CYHXOH08.js";
2
+ import { G as ObservabilityUser, U as ObservabilityScope, W as ObservabilityService } from "../index-tNZHSCpJ.js";
3
3
  import * as Sentry from "@sentry/cloudflare";
4
4
  //#region src/infrastructure/sentry/server.d.ts
5
5
  /** Application-owned deployment settings with the full Cloudflare SDK option contract. */
@@ -0,0 +1,13 @@
1
+ import Stripe from "stripe";
2
+ //#region src/infrastructure/stripe/client.d.ts
3
+ /** Default SDK retries; client configuration and individual requests can override this value. */
4
+ export declare const STRIPE_REQUEST_OPTIONS: Readonly<{
5
+ maxNetworkRetries: 2;
6
+ }>;
7
+ /**
8
+ * Construct a native Stripe client with fetch transport and two SDK retries by default.
9
+ * Native configuration overrides these defaults. Inject the client into application services.
10
+ */
11
+ export declare function createStripeClient(secretKey: string, options?: Stripe.StripeConfig): Stripe;
12
+ //#endregion
13
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","names":[],"sources":["../../src/infrastructure/stripe/client.ts"],"mappings":";;;qBAGa,wBAAsB;EAAA;;;;;;wBAMnB,mBAAmB,mBAAmB,UAAS,OAAO,eAAoB"}
@@ -0,0 +1,20 @@
1
+ import Stripe from "stripe";
2
+
3
+ //#region src/infrastructure/stripe/client.ts
4
+ /** Default SDK retries; client configuration and individual requests can override this value. */
5
+ const STRIPE_REQUEST_OPTIONS = Object.freeze({ maxNetworkRetries: 2 });
6
+ /**
7
+ * Construct a native Stripe client with fetch transport and two SDK retries by default.
8
+ * Native configuration overrides these defaults. Inject the client into application services.
9
+ */
10
+ function createStripeClient(secretKey, options = {}) {
11
+ return new Stripe(secretKey, {
12
+ httpClient: Stripe.createFetchHttpClient(),
13
+ ...STRIPE_REQUEST_OPTIONS,
14
+ ...options
15
+ });
16
+ }
17
+
18
+ //#endregion
19
+ export { STRIPE_REQUEST_OPTIONS, createStripeClient };
20
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","names":[],"sources":["../../src/infrastructure/stripe/client.ts"],"sourcesContent":["import Stripe from 'stripe'\n\n/** Default SDK retries; client configuration and individual requests can override this value. */\nexport const STRIPE_REQUEST_OPTIONS = Object.freeze({ maxNetworkRetries: 2 })\n\n/**\n * Construct a native Stripe client with fetch transport and two SDK retries by default.\n * Native configuration overrides these defaults. Inject the client into application services.\n */\nexport function createStripeClient(secretKey: string, options: Stripe.StripeConfig = {}): Stripe {\n return new Stripe(secretKey, {\n httpClient: Stripe.createFetchHttpClient(),\n ...STRIPE_REQUEST_OPTIONS,\n ...options,\n })\n}\n"],"mappings":";;;;AAGA,MAAa,yBAAyB,OAAO,OAAO,EAAE,mBAAmB,EAAE,CAAC;;;;;AAM5E,SAAgB,mBAAmB,WAAmB,UAA+B,CAAC,GAAW;CAC/F,OAAO,IAAI,OAAO,WAAW;EAC3B,YAAY,OAAO,sBAAsB;EACzC,GAAG;EACH,GAAG;CACL,CAAC;AACH"}
@@ -0,0 +1,14 @@
1
+ import Stripe from "stripe";
2
+ //#region src/infrastructure/stripe/event-map.d.ts
3
+ type EventObject<K extends Stripe.Event['type']> = Extract<Stripe.Event, {
4
+ type: K;
5
+ }>['data']['object'];
6
+ /**
7
+ * Convert verified Stripe events with callbacks typed to each event's object.
8
+ * Unmapped events return undefined. Applications own validation and payment policy.
9
+ */
10
+ export declare function stripeEventMap<T>(map: { [K in Stripe.Event['type']]?: (object: EventObject<K>) => T; }): (event: Stripe.Event) => T | undefined;
11
+ /** Identify one Stripe event delivery; different events for one payment retain different IDs. */
12
+ export declare function stripeEventId(event: Stripe.Event): string;
13
+ //#endregion
14
+ //# sourceMappingURL=event-map.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-map.d.ts","names":[],"sources":["../../src/infrastructure/stripe/event-map.ts"],"mappings":";;KAEK,YAAY,UAAU,OAAO,iBAAiB,QACjD,OAAO;EACL,MAAM;;;;;;wBAOM,eAAe,GAAG,QAC/B,KAAK,OAAO,kBAAkB,QAAQ,YAAY,OAAO,QACvD,OAAO,OAAO,UAAU;;wBASb,cAAc,OAAO,OAAO"}
@@ -0,0 +1,19 @@
1
+ //#region src/infrastructure/stripe/event-map.ts
2
+ /**
3
+ * Convert verified Stripe events with callbacks typed to each event's object.
4
+ * Unmapped events return undefined. Applications own validation and payment policy.
5
+ */
6
+ function stripeEventMap(map) {
7
+ const handlers = map;
8
+ return (event) => {
9
+ return handlers[event.type]?.(event.data.object);
10
+ };
11
+ }
12
+ /** Identify one Stripe event delivery; different events for one payment retain different IDs. */
13
+ function stripeEventId(event) {
14
+ return `stripe:event:${event.id}`;
15
+ }
16
+
17
+ //#endregion
18
+ export { stripeEventId, stripeEventMap };
19
+ //# sourceMappingURL=event-map.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-map.js","names":[],"sources":["../../src/infrastructure/stripe/event-map.ts"],"sourcesContent":["import type Stripe from 'stripe'\n\ntype EventObject<K extends Stripe.Event['type']> = Extract<\n Stripe.Event,\n { type: K }\n>['data']['object']\n\n/**\n * Convert verified Stripe events with callbacks typed to each event's object.\n * Unmapped events return undefined. Applications own validation and payment policy.\n */\nexport function stripeEventMap<T>(map: {\n [K in Stripe.Event['type']]?: (object: EventObject<K>) => T\n}): (event: Stripe.Event) => T | undefined {\n const handlers: Partial<Record<Stripe.Event['type'], (object: never) => T>> = map\n return (event) => {\n // SAFETY: The map binds each event type to its object; indexed lookup loses that correlation.\n return handlers[event.type]?.(event.data.object as never)\n }\n}\n\n/** Identify one Stripe event delivery; different events for one payment retain different IDs. */\nexport function stripeEventId(event: Stripe.Event): string {\n return `stripe:event:${event.id}`\n}\n"],"mappings":";;;;;AAWA,SAAgB,eAAkB,KAES;CACzC,MAAM,WAAwE;CAC9E,QAAQ,UAAU;EAEhB,OAAO,SAAS,MAAM,KAAK,GAAG,MAAM,KAAK,MAAe;CAC1D;AACF;;AAGA,SAAgB,cAAc,OAA6B;CACzD,OAAO,gBAAgB,MAAM;AAC/B"}
@@ -14,7 +14,7 @@ export type CheckoutResult = Awaited<ReturnType<Stripe['checkout']['sessions']['
14
14
  export type PaymentResult = Awaited<ReturnType<Stripe['paymentIntents']['create']>>;
15
15
  /**
16
16
  * Stripe payment operations, with native types and stable keys for mutations.
17
- * Rejects with PaymentServiceError or native StripeCardError; unexpected defects propagate.
17
+ * Rejects with PaymentServiceError for Stripe failures; non-Stripe failures propagate unchanged.
18
18
  */
19
19
  export interface IPaymentService {
20
20
  /** Create a customer; the application owns persistence and reuse of the customer link. */
@@ -23,14 +23,14 @@ export interface IPaymentService {
23
23
  createCheckout(input: CheckoutInput, options: PaymentRequestOptions): Promise<CheckoutResult>;
24
24
  /** Create checkout in setup mode. The application records consent and verifies the saved method's owner. */
25
25
  setupPaymentMethod(input: CheckoutInput, options: PaymentRequestOptions): Promise<CheckoutResult>;
26
- /** Confirm a saved method off-session. Native card errors can require customer authentication. */
26
+ /** Confirm a saved method off-session. PaymentServiceError retains card errors in cause. */
27
27
  chargeSavedMethod(input: SavedMethodCharge, options: PaymentRequestOptions): Promise<PaymentResult>;
28
28
  /** Retrieve an existing payment without starting another payment attempt. */
29
29
  getPayment(paymentId: string, params?: Stripe.PaymentIntentRetrieveParams, options?: Stripe.RequestOptions): Promise<PaymentResult>;
30
30
  }
31
31
  /**
32
32
  * Ported from Typist's StripeClient request boundary; the native SDK now owns retries.
33
- * Each request defaults to two SDK retries. Native request options can override retries and timeout.
33
+ * The injected client owns retry and timeout defaults. Native request options can override them.
34
34
  */
35
35
  export declare class StripePaymentService implements IPaymentService {
36
36
  private readonly stripe;
@@ -42,11 +42,10 @@ export declare class StripePaymentService implements IPaymentService {
42
42
  createCheckout(input: CheckoutInput, options: PaymentRequestOptions): Promise<CheckoutResult>;
43
43
  /** Force setup mode after caller parameters; this operation does not charge the customer. */
44
44
  setupPaymentMethod(input: CheckoutInput, options: PaymentRequestOptions): Promise<CheckoutResult>;
45
- /** Force confirmation and off-session use; preserve native card failures for application handling. */
45
+ /** Force confirmation and off-session use; retain native card failures in PaymentServiceError.cause. */
46
46
  chargeSavedMethod(input: SavedMethodCharge, options: PaymentRequestOptions): Promise<PaymentResult>;
47
47
  /** Preserve every native status and response field when reconciling an existing payment. */
48
48
  getPayment(paymentId: string, params?: Stripe.PaymentIntentRetrieveParams, options?: Stripe.RequestOptions): Promise<PaymentResult>;
49
- private request;
50
49
  }
51
50
  //#endregion
52
51
  //# sourceMappingURL=payment-service.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"payment-service.d.ts","names":[],"sources":["../../src/infrastructure/stripe/payment-service.ts"],"mappings":";;;YAKY,wBAAwB,OAAO,iBACzC,SAAS,KAAK,OAAO;;YAGX,gBAAgB,KAAK,OAAO,SAAS;;YAGrC,oBAAoB,KAAK,OAAO,wDAC1C,SAAS,KAAK,OAAO;;YAGX,iBAAiB,QAAQ,WAAW;;YAGpC,iBAAiB,QAAQ,WAAW;;YAGpC,gBAAgB,QAAQ,WAAW;;;;;iBAM9B;;EAEf,eACE,OAAO,OAAO,sBACd,SAAS,wBACR,QAAQ;;EAEX,eAAe,OAAO,eAAe,SAAS,wBAAwB,QAAQ;;EAE9E,mBAAmB,OAAO,eAAe,SAAS,wBAAwB,QAAQ;;EAElF,kBACE,OAAO,mBACP,SAAS,wBACR,QAAQ;;EAEX,WACE,mBACA,SAAS,OAAO,6BAChB,UAAU,OAAO,iBAChB,QAAQ;;;;;;qBAOA,gCAAgC;mBAEd;;EAA7B,YAA6B,QAAQ;;EAGrC,eACE,OAAO,OAAO,sBACd,SAAS,wBACR,QAAQ;;EAOX,eAAe,OAAO,eAAe,SAAS,wBAAwB,QAAQ;;EAU9E,mBACE,OAAO,eACP,SAAS,wBACR,QAAQ;;EAUX,kBACE,OAAO,mBACP,SAAS,wBACR,QAAQ;;EAUX,WACE,mBACA,SAAS,OAAO,6BAChB,UAAU,OAAO,iBAChB,QAAQ;UAMG"}
1
+ {"version":3,"file":"payment-service.d.ts","names":[],"sources":["../../src/infrastructure/stripe/payment-service.ts"],"mappings":";;;YAKY,wBAAwB,OAAO,iBACzC,SAAS,KAAK,OAAO;;YAGX,gBAAgB,KAAK,OAAO,SAAS;;YAGrC,oBAAoB,KAAK,OAAO,wDAC1C,SAAS,KAAK,OAAO;;YAGX,iBAAiB,QAAQ,WAAW;;YAGpC,iBAAiB,QAAQ,WAAW;;YAGpC,gBAAgB,QAAQ,WAAW;;;;;iBAM9B;;EAEf,eACE,OAAO,OAAO,sBACd,SAAS,wBACR,QAAQ;;EAEX,eAAe,OAAO,eAAe,SAAS,wBAAwB,QAAQ;;EAE9E,mBAAmB,OAAO,eAAe,SAAS,wBAAwB,QAAQ;;EAElF,kBACE,OAAO,mBACP,SAAS,wBACR,QAAQ;;EAEX,WACE,mBACA,SAAS,OAAO,6BAChB,UAAU,OAAO,iBAChB,QAAQ;;;;;;qBAOA,gCAAgC;mBAEd;;EAA7B,YAA6B,QAAQ;;EAGrC,eACE,OAAO,OAAO,sBACd,SAAS,wBACR,QAAQ;;EAKX,eAAe,OAAO,eAAe,SAAS,wBAAwB,QAAQ;;EAO9E,mBACE,OAAO,eACP,SAAS,wBACR,QAAQ;;EAOX,kBACE,OAAO,mBACP,SAAS,wBACR,QAAQ;;EAOX,WACE,mBACA,SAAS,OAAO,6BAChB,UAAU,OAAO,iBAChB,QAAQ"}
@@ -1,4 +1,4 @@
1
- import { nt as AppError } from "../core-BT1dSEQI.js";
1
+ import { nt as AppError } from "../core-BIas38R0.js";
2
2
  import Stripe from "stripe";
3
3
 
4
4
  //#region src/infrastructure/stripe/payment-service.error.ts
@@ -18,7 +18,7 @@ function classifyStripeError(error) {
18
18
  if (error.headers?.["stripe-should-retry"] === "false") return "terminal";
19
19
  const status = error.statusCode;
20
20
  if (status !== void 0 && status >= 400 && status < 500 && status !== 409 && status !== 429) return "terminal";
21
- if (error instanceof Stripe.errors.StripeAuthenticationError || error instanceof Stripe.errors.StripePermissionError || error instanceof Stripe.errors.StripeInvalidRequestError || error instanceof Stripe.errors.StripeIdempotencyError) return "terminal";
21
+ if (error instanceof Stripe.errors.StripeAuthenticationError || error instanceof Stripe.errors.StripePermissionError || error instanceof Stripe.errors.StripeInvalidRequestError || error instanceof Stripe.errors.StripeIdempotencyError || error instanceof Stripe.errors.StripeCardError) return "terminal";
22
22
  if (error.headers?.["stripe-should-retry"] === "true") return "transient";
23
23
  if (status === 409 || status === 429 || status !== void 0 && status >= 500) return "transient";
24
24
  if (error instanceof Stripe.errors.StripeConnectionError || error instanceof Stripe.errors.StripeAPIError || error instanceof Stripe.errors.StripeRateLimitError) return "transient";
@@ -1 +1 @@
1
- {"version":3,"file":"payment-service.error.js","names":[],"sources":["../../src/infrastructure/stripe/payment-service.error.ts"],"sourcesContent":["import Stripe from 'stripe'\n\nimport { AppError, type FailureClassification } from '@/core'\n\n/** A final Stripe request failure. The caller logs once; native details remain in cause. */\nexport class PaymentServiceError extends AppError {\n readonly _tag = 'PaymentServiceError'\n readonly classification: FailureClassification\n\n /** Classify the final SDK error without scheduling another payment attempt. */\n constructor(\n readonly operation: string,\n cause: Stripe.errors.StripeError,\n ) {\n super(`Stripe request failed: ${operation}`, { cause })\n this.classification = classifyStripeError(cause)\n }\n}\n\nfunction classifyStripeError(error: Stripe.errors.StripeError): FailureClassification {\n if (error.headers?.['stripe-should-retry'] === 'false') return 'terminal'\n const status = error.statusCode\n if (status !== undefined && status >= 400 && status < 500 && status !== 409 && status !== 429)\n return 'terminal'\n if (\n error instanceof Stripe.errors.StripeAuthenticationError ||\n error instanceof Stripe.errors.StripePermissionError ||\n error instanceof Stripe.errors.StripeInvalidRequestError ||\n error instanceof Stripe.errors.StripeIdempotencyError\n )\n return 'terminal'\n if (error.headers?.['stripe-should-retry'] === 'true') return 'transient'\n if (status === 409 || status === 429 || (status !== undefined && status >= 500))\n return 'transient'\n if (\n error instanceof Stripe.errors.StripeConnectionError ||\n error instanceof Stripe.errors.StripeAPIError ||\n error instanceof Stripe.errors.StripeRateLimitError\n )\n return 'transient'\n return 'unknown'\n}\n"],"mappings":";;;;;AAKA,IAAa,sBAAb,cAAyC,SAAS;CAMrC;CALX,AAAS,OAAO;CAChB,AAAS;;CAGT,YACE,AAAS,WACT,OACA;EACA,MAAM,0BAA0B,aAAa,EAAE,MAAM,CAAC;EAH7C;EAIT,KAAK,iBAAiB,oBAAoB,KAAK;CACjD;AACF;AAEA,SAAS,oBAAoB,OAAyD;CACpF,IAAI,MAAM,UAAU,2BAA2B,SAAS,OAAO;CAC/D,MAAM,SAAS,MAAM;CACrB,IAAI,WAAW,UAAa,UAAU,OAAO,SAAS,OAAO,WAAW,OAAO,WAAW,KACxF,OAAO;CACT,IACE,iBAAiB,OAAO,OAAO,6BAC/B,iBAAiB,OAAO,OAAO,yBAC/B,iBAAiB,OAAO,OAAO,6BAC/B,iBAAiB,OAAO,OAAO,wBAE/B,OAAO;CACT,IAAI,MAAM,UAAU,2BAA2B,QAAQ,OAAO;CAC9D,IAAI,WAAW,OAAO,WAAW,OAAQ,WAAW,UAAa,UAAU,KACzE,OAAO;CACT,IACE,iBAAiB,OAAO,OAAO,yBAC/B,iBAAiB,OAAO,OAAO,kBAC/B,iBAAiB,OAAO,OAAO,sBAE/B,OAAO;CACT,OAAO;AACT"}
1
+ {"version":3,"file":"payment-service.error.js","names":[],"sources":["../../src/infrastructure/stripe/payment-service.error.ts"],"sourcesContent":["import Stripe from 'stripe'\n\nimport { AppError, type FailureClassification } from '@/core'\n\n/** A final Stripe request failure. The caller logs once; native details remain in cause. */\nexport class PaymentServiceError extends AppError {\n readonly _tag = 'PaymentServiceError'\n readonly classification: FailureClassification\n\n /** Classify the final SDK error without scheduling another payment attempt. */\n constructor(\n readonly operation: string,\n cause: Stripe.errors.StripeError,\n ) {\n super(`Stripe request failed: ${operation}`, { cause })\n this.classification = classifyStripeError(cause)\n }\n}\n\nfunction classifyStripeError(error: Stripe.errors.StripeError): FailureClassification {\n if (error.headers?.['stripe-should-retry'] === 'false') return 'terminal'\n const status = error.statusCode\n if (status !== undefined && status >= 400 && status < 500 && status !== 409 && status !== 429)\n return 'terminal'\n if (\n error instanceof Stripe.errors.StripeAuthenticationError ||\n error instanceof Stripe.errors.StripePermissionError ||\n error instanceof Stripe.errors.StripeInvalidRequestError ||\n error instanceof Stripe.errors.StripeIdempotencyError ||\n error instanceof Stripe.errors.StripeCardError\n )\n return 'terminal'\n if (error.headers?.['stripe-should-retry'] === 'true') return 'transient'\n if (status === 409 || status === 429 || (status !== undefined && status >= 500))\n return 'transient'\n if (\n error instanceof Stripe.errors.StripeConnectionError ||\n error instanceof Stripe.errors.StripeAPIError ||\n error instanceof Stripe.errors.StripeRateLimitError\n )\n return 'transient'\n return 'unknown'\n}\n"],"mappings":";;;;;AAKA,IAAa,sBAAb,cAAyC,SAAS;CAMrC;CALX,AAAS,OAAO;CAChB,AAAS;;CAGT,YACE,AAAS,WACT,OACA;EACA,MAAM,0BAA0B,aAAa,EAAE,MAAM,CAAC;EAH7C;EAIT,KAAK,iBAAiB,oBAAoB,KAAK;CACjD;AACF;AAEA,SAAS,oBAAoB,OAAyD;CACpF,IAAI,MAAM,UAAU,2BAA2B,SAAS,OAAO;CAC/D,MAAM,SAAS,MAAM;CACrB,IAAI,WAAW,UAAa,UAAU,OAAO,SAAS,OAAO,WAAW,OAAO,WAAW,KACxF,OAAO;CACT,IACE,iBAAiB,OAAO,OAAO,6BAC/B,iBAAiB,OAAO,OAAO,yBAC/B,iBAAiB,OAAO,OAAO,6BAC/B,iBAAiB,OAAO,OAAO,0BAC/B,iBAAiB,OAAO,OAAO,iBAE/B,OAAO;CACT,IAAI,MAAM,UAAU,2BAA2B,QAAQ,OAAO;CAC9D,IAAI,WAAW,OAAO,WAAW,OAAQ,WAAW,UAAa,UAAU,KACzE,OAAO;CACT,IACE,iBAAiB,OAAO,OAAO,yBAC/B,iBAAiB,OAAO,OAAO,kBAC/B,iBAAiB,OAAO,OAAO,sBAE/B,OAAO;CACT,OAAO;AACT"}
@@ -1,10 +1,9 @@
1
- import { PaymentServiceError } from "./payment-service.error.js";
2
- import Stripe from "stripe";
1
+ import { stripeRequest } from "./request.js";
3
2
 
4
3
  //#region src/infrastructure/stripe/payment-service.ts
5
4
  /**
6
5
  * Ported from Typist's StripeClient request boundary; the native SDK now owns retries.
7
- * Each request defaults to two SDK retries. Native request options can override retries and timeout.
6
+ * The injected client owns retry and timeout defaults. Native request options can override them.
8
7
  */
9
8
  var StripePaymentService = class {
10
9
  stripe;
@@ -14,56 +13,33 @@ var StripePaymentService = class {
14
13
  }
15
14
  /** Create a customer with a stable application key and preserve the native response. */
16
15
  createCustomer(input, options) {
17
- return this.request("customers.create", () => this.stripe.customers.create(input, {
18
- maxNetworkRetries: 2,
19
- ...options
20
- }));
16
+ return stripeRequest("customers.create", () => this.stripe.customers.create(input, options));
21
17
  }
22
18
  /** Force payment mode after caller parameters; return the native Checkout Session. */
23
19
  createCheckout(input, options) {
24
- return this.request("checkout.sessions.create", () => this.stripe.checkout.sessions.create({
20
+ return stripeRequest("checkout.sessions.create", () => this.stripe.checkout.sessions.create({
25
21
  ...input,
26
22
  mode: "payment"
27
- }, {
28
- maxNetworkRetries: 2,
29
- ...options
30
- }));
23
+ }, options));
31
24
  }
32
25
  /** Force setup mode after caller parameters; this operation does not charge the customer. */
33
26
  setupPaymentMethod(input, options) {
34
- return this.request("checkout.sessions.create", () => this.stripe.checkout.sessions.create({
27
+ return stripeRequest("checkout.sessions.create", () => this.stripe.checkout.sessions.create({
35
28
  ...input,
36
29
  mode: "setup"
37
- }, {
38
- maxNetworkRetries: 2,
39
- ...options
40
- }));
30
+ }, options));
41
31
  }
42
- /** Force confirmation and off-session use; preserve native card failures for application handling. */
32
+ /** Force confirmation and off-session use; retain native card failures in PaymentServiceError.cause. */
43
33
  chargeSavedMethod(input, options) {
44
- return this.request("paymentIntents.create", () => this.stripe.paymentIntents.create({
34
+ return stripeRequest("paymentIntents.create", () => this.stripe.paymentIntents.create({
45
35
  ...input,
46
36
  confirm: true,
47
37
  off_session: true
48
- }, {
49
- maxNetworkRetries: 2,
50
- ...options
51
- }));
38
+ }, options));
52
39
  }
53
40
  /** Preserve every native status and response field when reconciling an existing payment. */
54
41
  getPayment(paymentId, params, options) {
55
- return this.request("paymentIntents.retrieve", () => this.stripe.paymentIntents.retrieve(paymentId, params, {
56
- maxNetworkRetries: 2,
57
- ...options
58
- }));
59
- }
60
- async request(operation, work) {
61
- try {
62
- return await work();
63
- } catch (cause) {
64
- if (!(cause instanceof Stripe.errors.StripeError) || cause instanceof Stripe.errors.StripeCardError) throw cause;
65
- throw new PaymentServiceError(operation, cause);
66
- }
42
+ return stripeRequest("paymentIntents.retrieve", () => this.stripe.paymentIntents.retrieve(paymentId, params, options));
67
43
  }
68
44
  };
69
45
 
@@ -1 +1 @@
1
- {"version":3,"file":"payment-service.js","names":[],"sources":["../../src/infrastructure/stripe/payment-service.ts"],"sourcesContent":["import Stripe from 'stripe'\n\nimport { PaymentServiceError } from './payment-service.error.ts'\n\n/** Supply the same key and parameters when repeating one durable payment attempt. */\nexport type PaymentRequestOptions = Stripe.RequestOptions &\n Required<Pick<Stripe.RequestOptions, 'idempotencyKey'>>\n\n/** Native Checkout parameters; the service selects payment or setup mode. */\nexport type CheckoutInput = Omit<Stripe.Checkout.SessionCreateParams, 'mode'>\n\n/** A saved customer method; the service confirms the payment off-session. */\nexport type SavedMethodCharge = Omit<Stripe.PaymentIntentCreateParams, 'confirm' | 'off_session'> &\n Required<Pick<Stripe.PaymentIntentCreateParams, 'customer' | 'payment_method'>>\n\n/** Native customer response, including Stripe request metadata. */\nexport type CustomerResult = Awaited<ReturnType<Stripe['customers']['create']>>\n\n/** Native Checkout response. Its URL can be null; this does not confirm payment. */\nexport type CheckoutResult = Awaited<ReturnType<Stripe['checkout']['sessions']['create']>>\n\n/** Native PaymentIntent response. Applications decide what each status permits. */\nexport type PaymentResult = Awaited<ReturnType<Stripe['paymentIntents']['create']>>\n\n/**\n * Stripe payment operations, with native types and stable keys for mutations.\n * Rejects with PaymentServiceError or native StripeCardError; unexpected defects propagate.\n */\nexport interface IPaymentService {\n /** Create a customer; the application owns persistence and reuse of the customer link. */\n createCustomer(\n input: Stripe.CustomerCreateParams,\n options: PaymentRequestOptions,\n ): Promise<CustomerResult>\n /** Create one-time payment checkout; application fulfillment requires verified payment confirmation. */\n createCheckout(input: CheckoutInput, options: PaymentRequestOptions): Promise<CheckoutResult>\n /** Create checkout in setup mode. The application records consent and verifies the saved method's owner. */\n setupPaymentMethod(input: CheckoutInput, options: PaymentRequestOptions): Promise<CheckoutResult>\n /** Confirm a saved method off-session. Native card errors can require customer authentication. */\n chargeSavedMethod(\n input: SavedMethodCharge,\n options: PaymentRequestOptions,\n ): Promise<PaymentResult>\n /** Retrieve an existing payment without starting another payment attempt. */\n getPayment(\n paymentId: string,\n params?: Stripe.PaymentIntentRetrieveParams,\n options?: Stripe.RequestOptions,\n ): Promise<PaymentResult>\n}\n\n/**\n * Ported from Typist's StripeClient request boundary; the native SDK now owns retries.\n * Each request defaults to two SDK retries. Native request options can override retries and timeout.\n */\nexport class StripePaymentService implements IPaymentService {\n /** Inject a native SDK instance; its transport, credentials, and API version remain caller-owned. */\n constructor(private readonly stripe: Stripe) {}\n\n /** Create a customer with a stable application key and preserve the native response. */\n createCustomer(\n input: Stripe.CustomerCreateParams,\n options: PaymentRequestOptions,\n ): Promise<CustomerResult> {\n return this.request('customers.create', () =>\n this.stripe.customers.create(input, { maxNetworkRetries: 2, ...options }),\n )\n }\n\n /** Force payment mode after caller parameters; return the native Checkout Session. */\n createCheckout(input: CheckoutInput, options: PaymentRequestOptions): Promise<CheckoutResult> {\n return this.request('checkout.sessions.create', () =>\n this.stripe.checkout.sessions.create(\n { ...input, mode: 'payment' },\n { maxNetworkRetries: 2, ...options },\n ),\n )\n }\n\n /** Force setup mode after caller parameters; this operation does not charge the customer. */\n setupPaymentMethod(\n input: CheckoutInput,\n options: PaymentRequestOptions,\n ): Promise<CheckoutResult> {\n return this.request('checkout.sessions.create', () =>\n this.stripe.checkout.sessions.create(\n { ...input, mode: 'setup' },\n { maxNetworkRetries: 2, ...options },\n ),\n )\n }\n\n /** Force confirmation and off-session use; preserve native card failures for application handling. */\n chargeSavedMethod(\n input: SavedMethodCharge,\n options: PaymentRequestOptions,\n ): Promise<PaymentResult> {\n return this.request('paymentIntents.create', () =>\n this.stripe.paymentIntents.create(\n { ...input, confirm: true, off_session: true },\n { maxNetworkRetries: 2, ...options },\n ),\n )\n }\n\n /** Preserve every native status and response field when reconciling an existing payment. */\n getPayment(\n paymentId: string,\n params?: Stripe.PaymentIntentRetrieveParams,\n options?: Stripe.RequestOptions,\n ): Promise<PaymentResult> {\n return this.request('paymentIntents.retrieve', () =>\n this.stripe.paymentIntents.retrieve(paymentId, params, { maxNetworkRetries: 2, ...options }),\n )\n }\n\n private async request<T>(operation: string, work: () => Promise<T>): Promise<T> {\n try {\n return await work()\n } catch (cause) {\n if (\n !(cause instanceof Stripe.errors.StripeError) ||\n cause instanceof Stripe.errors.StripeCardError\n )\n throw cause\n throw new PaymentServiceError(operation, cause)\n }\n }\n}\n"],"mappings":";;;;;;;;AAuDA,IAAa,uBAAb,MAA6D;CAE9B;;CAA7B,YAAY,AAAiB,QAAgB;EAAhB;CAAiB;;CAG9C,eACE,OACA,SACyB;EACzB,OAAO,KAAK,QAAQ,0BAClB,KAAK,OAAO,UAAU,OAAO,OAAO;GAAE,mBAAmB;GAAG,GAAG;EAAQ,CAAC,CAC1E;CACF;;CAGA,eAAe,OAAsB,SAAyD;EAC5F,OAAO,KAAK,QAAQ,kCAClB,KAAK,OAAO,SAAS,SAAS,OAC5B;GAAE,GAAG;GAAO,MAAM;EAAU,GAC5B;GAAE,mBAAmB;GAAG,GAAG;EAAQ,CACrC,CACF;CACF;;CAGA,mBACE,OACA,SACyB;EACzB,OAAO,KAAK,QAAQ,kCAClB,KAAK,OAAO,SAAS,SAAS,OAC5B;GAAE,GAAG;GAAO,MAAM;EAAQ,GAC1B;GAAE,mBAAmB;GAAG,GAAG;EAAQ,CACrC,CACF;CACF;;CAGA,kBACE,OACA,SACwB;EACxB,OAAO,KAAK,QAAQ,+BAClB,KAAK,OAAO,eAAe,OACzB;GAAE,GAAG;GAAO,SAAS;GAAM,aAAa;EAAK,GAC7C;GAAE,mBAAmB;GAAG,GAAG;EAAQ,CACrC,CACF;CACF;;CAGA,WACE,WACA,QACA,SACwB;EACxB,OAAO,KAAK,QAAQ,iCAClB,KAAK,OAAO,eAAe,SAAS,WAAW,QAAQ;GAAE,mBAAmB;GAAG,GAAG;EAAQ,CAAC,CAC7F;CACF;CAEA,MAAc,QAAW,WAAmB,MAAoC;EAC9E,IAAI;GACF,OAAO,MAAM,KAAK;EACpB,SAAS,OAAO;GACd,IACE,EAAE,iBAAiB,OAAO,OAAO,gBACjC,iBAAiB,OAAO,OAAO,iBAE/B,MAAM;GACR,MAAM,IAAI,oBAAoB,WAAW,KAAK;EAChD;CACF;AACF"}
1
+ {"version":3,"file":"payment-service.js","names":[],"sources":["../../src/infrastructure/stripe/payment-service.ts"],"sourcesContent":["import type Stripe from 'stripe'\n\nimport { stripeRequest } from './request.ts'\n\n/** Supply the same key and parameters when repeating one durable payment attempt. */\nexport type PaymentRequestOptions = Stripe.RequestOptions &\n Required<Pick<Stripe.RequestOptions, 'idempotencyKey'>>\n\n/** Native Checkout parameters; the service selects payment or setup mode. */\nexport type CheckoutInput = Omit<Stripe.Checkout.SessionCreateParams, 'mode'>\n\n/** A saved customer method; the service confirms the payment off-session. */\nexport type SavedMethodCharge = Omit<Stripe.PaymentIntentCreateParams, 'confirm' | 'off_session'> &\n Required<Pick<Stripe.PaymentIntentCreateParams, 'customer' | 'payment_method'>>\n\n/** Native customer response, including Stripe request metadata. */\nexport type CustomerResult = Awaited<ReturnType<Stripe['customers']['create']>>\n\n/** Native Checkout response. Its URL can be null; this does not confirm payment. */\nexport type CheckoutResult = Awaited<ReturnType<Stripe['checkout']['sessions']['create']>>\n\n/** Native PaymentIntent response. Applications decide what each status permits. */\nexport type PaymentResult = Awaited<ReturnType<Stripe['paymentIntents']['create']>>\n\n/**\n * Stripe payment operations, with native types and stable keys for mutations.\n * Rejects with PaymentServiceError for Stripe failures; non-Stripe failures propagate unchanged.\n */\nexport interface IPaymentService {\n /** Create a customer; the application owns persistence and reuse of the customer link. */\n createCustomer(\n input: Stripe.CustomerCreateParams,\n options: PaymentRequestOptions,\n ): Promise<CustomerResult>\n /** Create one-time payment checkout; application fulfillment requires verified payment confirmation. */\n createCheckout(input: CheckoutInput, options: PaymentRequestOptions): Promise<CheckoutResult>\n /** Create checkout in setup mode. The application records consent and verifies the saved method's owner. */\n setupPaymentMethod(input: CheckoutInput, options: PaymentRequestOptions): Promise<CheckoutResult>\n /** Confirm a saved method off-session. PaymentServiceError retains card errors in cause. */\n chargeSavedMethod(\n input: SavedMethodCharge,\n options: PaymentRequestOptions,\n ): Promise<PaymentResult>\n /** Retrieve an existing payment without starting another payment attempt. */\n getPayment(\n paymentId: string,\n params?: Stripe.PaymentIntentRetrieveParams,\n options?: Stripe.RequestOptions,\n ): Promise<PaymentResult>\n}\n\n/**\n * Ported from Typist's StripeClient request boundary; the native SDK now owns retries.\n * The injected client owns retry and timeout defaults. Native request options can override them.\n */\nexport class StripePaymentService implements IPaymentService {\n /** Inject a native SDK instance; its transport, credentials, and API version remain caller-owned. */\n constructor(private readonly stripe: Stripe) {}\n\n /** Create a customer with a stable application key and preserve the native response. */\n createCustomer(\n input: Stripe.CustomerCreateParams,\n options: PaymentRequestOptions,\n ): Promise<CustomerResult> {\n return stripeRequest('customers.create', () => this.stripe.customers.create(input, options))\n }\n\n /** Force payment mode after caller parameters; return the native Checkout Session. */\n createCheckout(input: CheckoutInput, options: PaymentRequestOptions): Promise<CheckoutResult> {\n return stripeRequest('checkout.sessions.create', () =>\n this.stripe.checkout.sessions.create({ ...input, mode: 'payment' }, options),\n )\n }\n\n /** Force setup mode after caller parameters; this operation does not charge the customer. */\n setupPaymentMethod(\n input: CheckoutInput,\n options: PaymentRequestOptions,\n ): Promise<CheckoutResult> {\n return stripeRequest('checkout.sessions.create', () =>\n this.stripe.checkout.sessions.create({ ...input, mode: 'setup' }, options),\n )\n }\n\n /** Force confirmation and off-session use; retain native card failures in PaymentServiceError.cause. */\n chargeSavedMethod(\n input: SavedMethodCharge,\n options: PaymentRequestOptions,\n ): Promise<PaymentResult> {\n return stripeRequest('paymentIntents.create', () =>\n this.stripe.paymentIntents.create({ ...input, confirm: true, off_session: true }, options),\n )\n }\n\n /** Preserve every native status and response field when reconciling an existing payment. */\n getPayment(\n paymentId: string,\n params?: Stripe.PaymentIntentRetrieveParams,\n options?: Stripe.RequestOptions,\n ): Promise<PaymentResult> {\n return stripeRequest('paymentIntents.retrieve', () =>\n this.stripe.paymentIntents.retrieve(paymentId, params, options),\n )\n }\n}\n"],"mappings":";;;;;;;AAuDA,IAAa,uBAAb,MAA6D;CAE9B;;CAA7B,YAAY,AAAiB,QAAgB;EAAhB;CAAiB;;CAG9C,eACE,OACA,SACyB;EACzB,OAAO,cAAc,0BAA0B,KAAK,OAAO,UAAU,OAAO,OAAO,OAAO,CAAC;CAC7F;;CAGA,eAAe,OAAsB,SAAyD;EAC5F,OAAO,cAAc,kCACnB,KAAK,OAAO,SAAS,SAAS,OAAO;GAAE,GAAG;GAAO,MAAM;EAAU,GAAG,OAAO,CAC7E;CACF;;CAGA,mBACE,OACA,SACyB;EACzB,OAAO,cAAc,kCACnB,KAAK,OAAO,SAAS,SAAS,OAAO;GAAE,GAAG;GAAO,MAAM;EAAQ,GAAG,OAAO,CAC3E;CACF;;CAGA,kBACE,OACA,SACwB;EACxB,OAAO,cAAc,+BACnB,KAAK,OAAO,eAAe,OAAO;GAAE,GAAG;GAAO,SAAS;GAAM,aAAa;EAAK,GAAG,OAAO,CAC3F;CACF;;CAGA,WACE,WACA,QACA,SACwB;EACxB,OAAO,cAAc,iCACnB,KAAK,OAAO,eAAe,SAAS,WAAW,QAAQ,OAAO,CAChE;CACF;AACF"}
@@ -1,4 +1,4 @@
1
- import { t as WebhookVerificationError } from "../webhook-verification-qSFXv5H3.js";
1
+ import { t as WebhookVerificationError } from "../webhook-verification-CEUARY9X.js";
2
2
  import Stripe from "stripe";
3
3
 
4
4
  //#region src/infrastructure/stripe/payment-webhook.ts