@open-mercato/gateway-stripe 0.6.7-develop.6572.1.3d8e83062f → 0.6.7-develop.6574.1.05dcf72ba8

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.
@@ -5,7 +5,9 @@ import {
5
5
  fromCents,
6
6
  buildStripeMetadata,
7
7
  normalizeStripePaymentElementSettings,
8
- resolveStripeRendererKey
8
+ resolveStripeRendererKey,
9
+ resolveStripeRefundStatus,
10
+ stripeIdempotencyOptions
9
11
  } from "../shared.js";
10
12
  import { verifyStripeWebhook } from "../webhook-handler.js";
11
13
  const stripeAdapterV20231016 = {
@@ -49,9 +51,13 @@ const stripeAdapterV20231016 = {
49
51
  async capture(input) {
50
52
  const stripe = resolveStripeClient(input.credentials, "2023-10-16");
51
53
  const paymentIntent = input.amount ? await stripe.paymentIntents.retrieve(input.sessionId) : null;
52
- const captured = await stripe.paymentIntents.capture(input.sessionId, {
53
- amount_to_capture: input.amount && paymentIntent ? toCents(input.amount, paymentIntent.currency) : void 0
54
- });
54
+ const captured = await stripe.paymentIntents.capture(
55
+ input.sessionId,
56
+ {
57
+ amount_to_capture: input.amount && paymentIntent ? toCents(input.amount, paymentIntent.currency) : void 0
58
+ },
59
+ stripeIdempotencyOptions(input.idempotencyKey)
60
+ );
55
61
  return {
56
62
  status: mapStripeStatus(captured.status),
57
63
  capturedAmount: fromCents(captured.amount_received, captured.currency),
@@ -61,20 +67,27 @@ const stripeAdapterV20231016 = {
61
67
  async refund(input) {
62
68
  const stripe = resolveStripeClient(input.credentials, "2023-10-16");
63
69
  const paymentIntent = input.amount ? await stripe.paymentIntents.retrieve(input.sessionId) : null;
64
- const refund = await stripe.refunds.create({
65
- payment_intent: input.sessionId,
66
- amount: input.amount && paymentIntent ? toCents(input.amount, paymentIntent.currency) : void 0,
67
- reason: mapRefundReason(input.reason)
68
- });
70
+ const refund = await stripe.refunds.create(
71
+ {
72
+ payment_intent: input.sessionId,
73
+ amount: input.amount && paymentIntent ? toCents(input.amount, paymentIntent.currency) : void 0,
74
+ reason: mapRefundReason(input.reason)
75
+ },
76
+ stripeIdempotencyOptions(input.idempotencyKey)
77
+ );
69
78
  return {
70
79
  refundId: refund.id,
71
- status: refund.status === "succeeded" ? "refunded" : "pending",
80
+ status: await resolveStripeRefundStatus(stripe, refund, paymentIntent?.amount_received),
72
81
  refundedAmount: fromCents(refund.amount, refund.currency)
73
82
  };
74
83
  },
75
84
  async cancel(input) {
76
85
  const stripe = resolveStripeClient(input.credentials, "2023-10-16");
77
- const cancelled = await stripe.paymentIntents.cancel(input.sessionId);
86
+ const cancelled = await stripe.paymentIntents.cancel(
87
+ input.sessionId,
88
+ {},
89
+ stripeIdempotencyOptions(input.idempotencyKey)
90
+ );
78
91
  return { status: mapStripeStatus(cancelled.status) };
79
92
  },
80
93
  async getStatus(input) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/gateway_stripe/lib/adapters/v2023-10-16.ts"],
4
- "sourcesContent": ["import type {\n GatewayAdapter,\n CreateSessionInput,\n CreateSessionResult,\n CaptureInput,\n CaptureResult,\n RefundInput,\n RefundResult,\n CancelInput,\n CancelResult,\n GetStatusInput,\n GatewayPaymentStatus,\n VerifyWebhookInput,\n WebhookEvent,\n UnifiedPaymentStatus,\n} from '@open-mercato/shared/modules/payment_gateways/types'\nimport { resolveStripeClient } from '../client'\nimport { mapRefundReason, mapStripeStatus, mapWebhookEventToStatus } from '../status-map'\nimport {\n toCents,\n fromCents,\n buildStripeMetadata,\n normalizeStripePaymentElementSettings,\n resolveStripeRendererKey,\n} from '../shared'\nimport { verifyStripeWebhook } from '../webhook-handler'\n\nexport const stripeAdapterV20231016: GatewayAdapter = {\n providerKey: 'stripe',\n\n async createSession(input: CreateSessionInput): Promise<CreateSessionResult> {\n const stripe = resolveStripeClient(input.credentials, '2023-10-16')\n const rendererKey = resolveStripeRendererKey(input.presentation)\n const rendererSettings = normalizeStripePaymentElementSettings(input.presentation?.rendererSettings)\n\n const paymentIntent = await stripe.paymentIntents.create({\n amount: toCents(input.amount, input.currencyCode),\n currency: input.currencyCode.toLowerCase(),\n capture_method: input.captureMethod ?? 'automatic',\n metadata: buildStripeMetadata({\n paymentId: input.paymentId,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n orderId: input.orderId,\n }),\n payment_method_types: input.paymentTypes ?? ['card'],\n description: input.description,\n }, input.idempotencyKey ? { idempotencyKey: input.idempotencyKey } : undefined)\n\n return {\n sessionId: paymentIntent.id,\n clientSecret: paymentIntent.client_secret ?? undefined,\n status: mapStripeStatus(paymentIntent.status),\n providerData: {\n paymentIntentId: paymentIntent.id,\n publishableKey: typeof input.credentials.publishableKey === 'string'\n ? input.credentials.publishableKey\n : undefined,\n },\n clientSession: paymentIntent.client_secret && typeof input.credentials.publishableKey === 'string'\n ? {\n type: 'embedded',\n rendererKey,\n payload: {\n clientSecret: paymentIntent.client_secret,\n publishableKey: input.credentials.publishableKey,\n },\n settings: rendererSettings,\n }\n : undefined,\n }\n },\n\n async capture(input: CaptureInput): Promise<CaptureResult> {\n const stripe = resolveStripeClient(input.credentials, '2023-10-16')\n const paymentIntent = input.amount\n ? await stripe.paymentIntents.retrieve(input.sessionId)\n : null\n const captured = await stripe.paymentIntents.capture(input.sessionId, {\n amount_to_capture: input.amount && paymentIntent\n ? toCents(input.amount, paymentIntent.currency)\n : undefined,\n })\n return {\n status: mapStripeStatus(captured.status),\n capturedAmount: fromCents(captured.amount_received, captured.currency),\n providerData: { chargeId: captured.latest_charge },\n }\n },\n\n async refund(input: RefundInput): Promise<RefundResult> {\n const stripe = resolveStripeClient(input.credentials, '2023-10-16')\n const paymentIntent = input.amount\n ? await stripe.paymentIntents.retrieve(input.sessionId)\n : null\n const refund = await stripe.refunds.create({\n payment_intent: input.sessionId,\n amount: input.amount && paymentIntent\n ? toCents(input.amount, paymentIntent.currency)\n : undefined,\n reason: mapRefundReason(input.reason),\n })\n return {\n refundId: refund.id,\n status: refund.status === 'succeeded' ? 'refunded' : 'pending',\n refundedAmount: fromCents(refund.amount, refund.currency),\n }\n },\n\n async cancel(input: CancelInput): Promise<CancelResult> {\n const stripe = resolveStripeClient(input.credentials, '2023-10-16')\n const cancelled = await stripe.paymentIntents.cancel(input.sessionId)\n return { status: mapStripeStatus(cancelled.status) }\n },\n\n async getStatus(input: GetStatusInput): Promise<GatewayPaymentStatus> {\n const stripe = resolveStripeClient(input.credentials, '2023-10-16')\n const pi = await stripe.paymentIntents.retrieve(input.sessionId)\n return {\n status: mapStripeStatus(pi.status),\n amount: fromCents(pi.amount, pi.currency),\n amountReceived: fromCents(pi.amount_received, pi.currency),\n currencyCode: pi.currency.toUpperCase(),\n }\n },\n\n async verifyWebhook(input: VerifyWebhookInput): Promise<WebhookEvent> {\n return verifyStripeWebhook(input)\n },\n\n mapStatus(providerStatus: string, eventType?: string): UnifiedPaymentStatus {\n if (eventType) {\n const mappedEvent = mapWebhookEventToStatus(eventType)\n if (mappedEvent) return mappedEvent\n }\n return mapStripeStatus(providerStatus)\n },\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type {\n GatewayAdapter,\n CreateSessionInput,\n CreateSessionResult,\n CaptureInput,\n CaptureResult,\n RefundInput,\n RefundResult,\n CancelInput,\n CancelResult,\n GetStatusInput,\n GatewayPaymentStatus,\n VerifyWebhookInput,\n WebhookEvent,\n UnifiedPaymentStatus,\n} from '@open-mercato/shared/modules/payment_gateways/types'\nimport { resolveStripeClient } from '../client'\nimport { mapRefundReason, mapStripeStatus, mapWebhookEventToStatus } from '../status-map'\nimport {\n toCents,\n fromCents,\n buildStripeMetadata,\n normalizeStripePaymentElementSettings,\n resolveStripeRendererKey,\n resolveStripeRefundStatus,\n stripeIdempotencyOptions,\n} from '../shared'\nimport { verifyStripeWebhook } from '../webhook-handler'\n\nexport const stripeAdapterV20231016: GatewayAdapter = {\n providerKey: 'stripe',\n\n async createSession(input: CreateSessionInput): Promise<CreateSessionResult> {\n const stripe = resolveStripeClient(input.credentials, '2023-10-16')\n const rendererKey = resolveStripeRendererKey(input.presentation)\n const rendererSettings = normalizeStripePaymentElementSettings(input.presentation?.rendererSettings)\n\n const paymentIntent = await stripe.paymentIntents.create({\n amount: toCents(input.amount, input.currencyCode),\n currency: input.currencyCode.toLowerCase(),\n capture_method: input.captureMethod ?? 'automatic',\n metadata: buildStripeMetadata({\n paymentId: input.paymentId,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n orderId: input.orderId,\n }),\n payment_method_types: input.paymentTypes ?? ['card'],\n description: input.description,\n }, input.idempotencyKey ? { idempotencyKey: input.idempotencyKey } : undefined)\n\n return {\n sessionId: paymentIntent.id,\n clientSecret: paymentIntent.client_secret ?? undefined,\n status: mapStripeStatus(paymentIntent.status),\n providerData: {\n paymentIntentId: paymentIntent.id,\n publishableKey: typeof input.credentials.publishableKey === 'string'\n ? input.credentials.publishableKey\n : undefined,\n },\n clientSession: paymentIntent.client_secret && typeof input.credentials.publishableKey === 'string'\n ? {\n type: 'embedded',\n rendererKey,\n payload: {\n clientSecret: paymentIntent.client_secret,\n publishableKey: input.credentials.publishableKey,\n },\n settings: rendererSettings,\n }\n : undefined,\n }\n },\n\n async capture(input: CaptureInput): Promise<CaptureResult> {\n const stripe = resolveStripeClient(input.credentials, '2023-10-16')\n const paymentIntent = input.amount\n ? await stripe.paymentIntents.retrieve(input.sessionId)\n : null\n const captured = await stripe.paymentIntents.capture(\n input.sessionId,\n {\n amount_to_capture: input.amount && paymentIntent\n ? toCents(input.amount, paymentIntent.currency)\n : undefined,\n },\n stripeIdempotencyOptions(input.idempotencyKey),\n )\n return {\n status: mapStripeStatus(captured.status),\n capturedAmount: fromCents(captured.amount_received, captured.currency),\n providerData: { chargeId: captured.latest_charge },\n }\n },\n\n async refund(input: RefundInput): Promise<RefundResult> {\n const stripe = resolveStripeClient(input.credentials, '2023-10-16')\n const paymentIntent = input.amount\n ? await stripe.paymentIntents.retrieve(input.sessionId)\n : null\n const refund = await stripe.refunds.create(\n {\n payment_intent: input.sessionId,\n amount: input.amount && paymentIntent\n ? toCents(input.amount, paymentIntent.currency)\n : undefined,\n reason: mapRefundReason(input.reason),\n },\n stripeIdempotencyOptions(input.idempotencyKey),\n )\n return {\n refundId: refund.id,\n status: await resolveStripeRefundStatus(stripe, refund, paymentIntent?.amount_received),\n refundedAmount: fromCents(refund.amount, refund.currency),\n }\n },\n\n async cancel(input: CancelInput): Promise<CancelResult> {\n const stripe = resolveStripeClient(input.credentials, '2023-10-16')\n const cancelled = await stripe.paymentIntents.cancel(\n input.sessionId,\n {},\n stripeIdempotencyOptions(input.idempotencyKey),\n )\n return { status: mapStripeStatus(cancelled.status) }\n },\n\n async getStatus(input: GetStatusInput): Promise<GatewayPaymentStatus> {\n const stripe = resolveStripeClient(input.credentials, '2023-10-16')\n const pi = await stripe.paymentIntents.retrieve(input.sessionId)\n return {\n status: mapStripeStatus(pi.status),\n amount: fromCents(pi.amount, pi.currency),\n amountReceived: fromCents(pi.amount_received, pi.currency),\n currencyCode: pi.currency.toUpperCase(),\n }\n },\n\n async verifyWebhook(input: VerifyWebhookInput): Promise<WebhookEvent> {\n return verifyStripeWebhook(input)\n },\n\n mapStatus(providerStatus: string, eventType?: string): UnifiedPaymentStatus {\n if (eventType) {\n const mappedEvent = mapWebhookEventToStatus(eventType)\n if (mappedEvent) return mappedEvent\n }\n return mapStripeStatus(providerStatus)\n },\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -5,7 +5,9 @@ import {
5
5
  fromCents,
6
6
  buildStripeMetadata,
7
7
  normalizeStripePaymentElementSettings,
8
- resolveStripeRendererKey
8
+ resolveStripeRendererKey,
9
+ resolveStripeRefundStatus,
10
+ stripeIdempotencyOptions
9
11
  } from "../shared.js";
10
12
  import { verifyStripeWebhook } from "../webhook-handler.js";
11
13
  const STRIPE_API_VERSION = "2024-12-18";
@@ -50,9 +52,13 @@ const stripeAdapterV20241218 = {
50
52
  async capture(input) {
51
53
  const stripe = resolveStripeClient(input.credentials, STRIPE_API_VERSION);
52
54
  const paymentIntent = input.amount ? await stripe.paymentIntents.retrieve(input.sessionId) : null;
53
- const captured = await stripe.paymentIntents.capture(input.sessionId, {
54
- amount_to_capture: input.amount && paymentIntent ? toCents(input.amount, paymentIntent.currency) : void 0
55
- });
55
+ const captured = await stripe.paymentIntents.capture(
56
+ input.sessionId,
57
+ {
58
+ amount_to_capture: input.amount && paymentIntent ? toCents(input.amount, paymentIntent.currency) : void 0
59
+ },
60
+ stripeIdempotencyOptions(input.idempotencyKey)
61
+ );
56
62
  const capturedCurrencyCode = captured.currency.toUpperCase();
57
63
  return {
58
64
  status: mapStripeStatus(captured.status),
@@ -63,24 +69,29 @@ const stripeAdapterV20241218 = {
63
69
  async refund(input) {
64
70
  const stripe = resolveStripeClient(input.credentials, STRIPE_API_VERSION);
65
71
  const paymentIntent = input.amount ? await stripe.paymentIntents.retrieve(input.sessionId) : null;
66
- const refund = await stripe.refunds.create({
67
- payment_intent: input.sessionId,
68
- amount: input.amount && paymentIntent ? toCents(input.amount, paymentIntent.currency) : void 0,
69
- reason: mapRefundReason(input.reason),
70
- metadata: input.metadata
71
- });
72
+ const refund = await stripe.refunds.create(
73
+ {
74
+ payment_intent: input.sessionId,
75
+ amount: input.amount && paymentIntent ? toCents(input.amount, paymentIntent.currency) : void 0,
76
+ reason: mapRefundReason(input.reason),
77
+ metadata: input.metadata
78
+ },
79
+ stripeIdempotencyOptions(input.idempotencyKey)
80
+ );
72
81
  const refundCurrencyCode = refund.currency.toUpperCase();
73
82
  return {
74
83
  refundId: refund.id,
75
- status: refund.status === "succeeded" ? "refunded" : "pending",
84
+ status: await resolveStripeRefundStatus(stripe, refund, paymentIntent?.amount_received),
76
85
  refundedAmount: fromCents(refund.amount, refundCurrencyCode)
77
86
  };
78
87
  },
79
88
  async cancel(input) {
80
89
  const stripe = resolveStripeClient(input.credentials, STRIPE_API_VERSION);
81
- const cancelled = await stripe.paymentIntents.cancel(input.sessionId, {
82
- cancellation_reason: "requested_by_customer"
83
- });
90
+ const cancelled = await stripe.paymentIntents.cancel(
91
+ input.sessionId,
92
+ { cancellation_reason: "requested_by_customer" },
93
+ stripeIdempotencyOptions(input.idempotencyKey)
94
+ );
84
95
  return {
85
96
  status: mapStripeStatus(cancelled.status)
86
97
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/gateway_stripe/lib/adapters/v2024-12-18.ts"],
4
- "sourcesContent": ["import type {\n GatewayAdapter,\n CreateSessionInput,\n CreateSessionResult,\n CaptureInput,\n CaptureResult,\n RefundInput,\n RefundResult,\n CancelInput,\n CancelResult,\n GetStatusInput,\n GatewayPaymentStatus,\n VerifyWebhookInput,\n WebhookEvent,\n UnifiedPaymentStatus,\n} from '@open-mercato/shared/modules/payment_gateways/types'\nimport { resolveStripeClient } from '../client'\nimport { mapRefundReason, mapStripeStatus, mapWebhookEventToStatus } from '../status-map'\nimport {\n toCents,\n fromCents,\n buildStripeMetadata,\n normalizeStripePaymentElementSettings,\n resolveStripeRendererKey,\n} from '../shared'\nimport { verifyStripeWebhook } from '../webhook-handler'\n\nconst STRIPE_API_VERSION = '2024-12-18'\n\nexport const stripeAdapterV20241218: GatewayAdapter = {\n providerKey: 'stripe',\n\n async createSession(input: CreateSessionInput): Promise<CreateSessionResult> {\n const stripe = resolveStripeClient(input.credentials, STRIPE_API_VERSION)\n const rendererKey = resolveStripeRendererKey(input.presentation)\n const rendererSettings = normalizeStripePaymentElementSettings(input.presentation?.rendererSettings)\n\n const paymentIntent = await stripe.paymentIntents.create({\n amount: toCents(input.amount, input.currencyCode),\n currency: input.currencyCode.toLowerCase(),\n capture_method: input.captureMethod ?? 'automatic',\n metadata: buildStripeMetadata({\n paymentId: input.paymentId,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n orderId: input.orderId,\n }),\n payment_method_types: input.paymentTypes ?? ['card'],\n description: input.description,\n }, input.idempotencyKey ? { idempotencyKey: input.idempotencyKey } : undefined)\n\n return {\n sessionId: paymentIntent.id,\n clientSecret: paymentIntent.client_secret ?? undefined,\n status: mapStripeStatus(paymentIntent.status),\n providerData: {\n paymentIntentId: paymentIntent.id,\n publishableKey: typeof input.credentials.publishableKey === 'string'\n ? input.credentials.publishableKey\n : undefined,\n },\n clientSession: paymentIntent.client_secret && typeof input.credentials.publishableKey === 'string'\n ? {\n type: 'embedded',\n rendererKey,\n payload: {\n clientSecret: paymentIntent.client_secret,\n publishableKey: input.credentials.publishableKey,\n },\n settings: rendererSettings,\n }\n : undefined,\n }\n },\n\n async capture(input: CaptureInput): Promise<CaptureResult> {\n const stripe = resolveStripeClient(input.credentials, STRIPE_API_VERSION)\n const paymentIntent = input.amount\n ? await stripe.paymentIntents.retrieve(input.sessionId)\n : null\n\n const captured = await stripe.paymentIntents.capture(input.sessionId, {\n amount_to_capture: input.amount && paymentIntent\n ? toCents(input.amount, paymentIntent.currency)\n : undefined,\n })\n\n const capturedCurrencyCode = captured.currency.toUpperCase()\n\n return {\n status: mapStripeStatus(captured.status),\n capturedAmount: fromCents(captured.amount_received, capturedCurrencyCode),\n providerData: { chargeId: captured.latest_charge },\n }\n },\n\n async refund(input: RefundInput): Promise<RefundResult> {\n const stripe = resolveStripeClient(input.credentials, STRIPE_API_VERSION)\n const paymentIntent = input.amount\n ? await stripe.paymentIntents.retrieve(input.sessionId)\n : null\n\n const refund = await stripe.refunds.create({\n payment_intent: input.sessionId,\n amount: input.amount && paymentIntent\n ? toCents(input.amount, paymentIntent.currency)\n : undefined,\n reason: mapRefundReason(input.reason),\n metadata: input.metadata as Record<string, string> | undefined,\n })\n\n const refundCurrencyCode = refund.currency.toUpperCase()\n\n return {\n refundId: refund.id,\n status: refund.status === 'succeeded' ? 'refunded' : 'pending',\n refundedAmount: fromCents(refund.amount, refundCurrencyCode),\n }\n },\n\n async cancel(input: CancelInput): Promise<CancelResult> {\n const stripe = resolveStripeClient(input.credentials, STRIPE_API_VERSION)\n\n const cancelled = await stripe.paymentIntents.cancel(input.sessionId, {\n cancellation_reason: 'requested_by_customer',\n })\n\n return {\n status: mapStripeStatus(cancelled.status),\n }\n },\n\n async getStatus(input: GetStatusInput): Promise<GatewayPaymentStatus> {\n const stripe = resolveStripeClient(input.credentials, STRIPE_API_VERSION)\n\n const pi = await stripe.paymentIntents.retrieve(input.sessionId)\n return {\n status: mapStripeStatus(pi.status),\n amount: fromCents(pi.amount, pi.currency),\n amountReceived: fromCents(pi.amount_received, pi.currency),\n currencyCode: pi.currency.toUpperCase(),\n }\n },\n\n async verifyWebhook(input: VerifyWebhookInput): Promise<WebhookEvent> {\n return verifyStripeWebhook(input)\n },\n\n mapStatus(providerStatus: string, eventType?: string): UnifiedPaymentStatus {\n if (eventType) {\n const mappedEvent = mapWebhookEventToStatus(eventType)\n if (mappedEvent) return mappedEvent\n }\n return mapStripeStatus(providerStatus)\n },\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type {\n GatewayAdapter,\n CreateSessionInput,\n CreateSessionResult,\n CaptureInput,\n CaptureResult,\n RefundInput,\n RefundResult,\n CancelInput,\n CancelResult,\n GetStatusInput,\n GatewayPaymentStatus,\n VerifyWebhookInput,\n WebhookEvent,\n UnifiedPaymentStatus,\n} from '@open-mercato/shared/modules/payment_gateways/types'\nimport { resolveStripeClient } from '../client'\nimport { mapRefundReason, mapStripeStatus, mapWebhookEventToStatus } from '../status-map'\nimport {\n toCents,\n fromCents,\n buildStripeMetadata,\n normalizeStripePaymentElementSettings,\n resolveStripeRendererKey,\n resolveStripeRefundStatus,\n stripeIdempotencyOptions,\n} from '../shared'\nimport { verifyStripeWebhook } from '../webhook-handler'\n\nconst STRIPE_API_VERSION = '2024-12-18'\n\nexport const stripeAdapterV20241218: GatewayAdapter = {\n providerKey: 'stripe',\n\n async createSession(input: CreateSessionInput): Promise<CreateSessionResult> {\n const stripe = resolveStripeClient(input.credentials, STRIPE_API_VERSION)\n const rendererKey = resolveStripeRendererKey(input.presentation)\n const rendererSettings = normalizeStripePaymentElementSettings(input.presentation?.rendererSettings)\n\n const paymentIntent = await stripe.paymentIntents.create({\n amount: toCents(input.amount, input.currencyCode),\n currency: input.currencyCode.toLowerCase(),\n capture_method: input.captureMethod ?? 'automatic',\n metadata: buildStripeMetadata({\n paymentId: input.paymentId,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n orderId: input.orderId,\n }),\n payment_method_types: input.paymentTypes ?? ['card'],\n description: input.description,\n }, input.idempotencyKey ? { idempotencyKey: input.idempotencyKey } : undefined)\n\n return {\n sessionId: paymentIntent.id,\n clientSecret: paymentIntent.client_secret ?? undefined,\n status: mapStripeStatus(paymentIntent.status),\n providerData: {\n paymentIntentId: paymentIntent.id,\n publishableKey: typeof input.credentials.publishableKey === 'string'\n ? input.credentials.publishableKey\n : undefined,\n },\n clientSession: paymentIntent.client_secret && typeof input.credentials.publishableKey === 'string'\n ? {\n type: 'embedded',\n rendererKey,\n payload: {\n clientSecret: paymentIntent.client_secret,\n publishableKey: input.credentials.publishableKey,\n },\n settings: rendererSettings,\n }\n : undefined,\n }\n },\n\n async capture(input: CaptureInput): Promise<CaptureResult> {\n const stripe = resolveStripeClient(input.credentials, STRIPE_API_VERSION)\n const paymentIntent = input.amount\n ? await stripe.paymentIntents.retrieve(input.sessionId)\n : null\n\n const captured = await stripe.paymentIntents.capture(\n input.sessionId,\n {\n amount_to_capture: input.amount && paymentIntent\n ? toCents(input.amount, paymentIntent.currency)\n : undefined,\n },\n stripeIdempotencyOptions(input.idempotencyKey),\n )\n\n const capturedCurrencyCode = captured.currency.toUpperCase()\n\n return {\n status: mapStripeStatus(captured.status),\n capturedAmount: fromCents(captured.amount_received, capturedCurrencyCode),\n providerData: { chargeId: captured.latest_charge },\n }\n },\n\n async refund(input: RefundInput): Promise<RefundResult> {\n const stripe = resolveStripeClient(input.credentials, STRIPE_API_VERSION)\n const paymentIntent = input.amount\n ? await stripe.paymentIntents.retrieve(input.sessionId)\n : null\n\n const refund = await stripe.refunds.create(\n {\n payment_intent: input.sessionId,\n amount: input.amount && paymentIntent\n ? toCents(input.amount, paymentIntent.currency)\n : undefined,\n reason: mapRefundReason(input.reason),\n metadata: input.metadata as Record<string, string> | undefined,\n },\n stripeIdempotencyOptions(input.idempotencyKey),\n )\n\n const refundCurrencyCode = refund.currency.toUpperCase()\n\n return {\n refundId: refund.id,\n status: await resolveStripeRefundStatus(stripe, refund, paymentIntent?.amount_received),\n refundedAmount: fromCents(refund.amount, refundCurrencyCode),\n }\n },\n\n async cancel(input: CancelInput): Promise<CancelResult> {\n const stripe = resolveStripeClient(input.credentials, STRIPE_API_VERSION)\n\n const cancelled = await stripe.paymentIntents.cancel(\n input.sessionId,\n { cancellation_reason: 'requested_by_customer' },\n stripeIdempotencyOptions(input.idempotencyKey),\n )\n\n return {\n status: mapStripeStatus(cancelled.status),\n }\n },\n\n async getStatus(input: GetStatusInput): Promise<GatewayPaymentStatus> {\n const stripe = resolveStripeClient(input.credentials, STRIPE_API_VERSION)\n\n const pi = await stripe.paymentIntents.retrieve(input.sessionId)\n return {\n status: mapStripeStatus(pi.status),\n amount: fromCents(pi.amount, pi.currency),\n amountReceived: fromCents(pi.amount_received, pi.currency),\n currencyCode: pi.currency.toUpperCase(),\n }\n },\n\n async verifyWebhook(input: VerifyWebhookInput): Promise<WebhookEvent> {\n return verifyStripeWebhook(input)\n },\n\n mapStatus(providerStatus: string, eventType?: string): UnifiedPaymentStatus {\n if (eventType) {\n const mappedEvent = mapWebhookEventToStatus(eventType)\n if (mappedEvent) return mappedEvent\n }\n return mapStripeStatus(providerStatus)\n },\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -5,7 +5,9 @@ import {
5
5
  fromCents,
6
6
  buildStripeMetadata,
7
7
  normalizeStripePaymentElementSettings,
8
- resolveStripeRendererKey
8
+ resolveStripeRendererKey,
9
+ resolveStripeRefundStatus,
10
+ stripeIdempotencyOptions
9
11
  } from "../shared.js";
10
12
  import { verifyStripeWebhook } from "../webhook-handler.js";
11
13
  const STRIPE_API_VERSION = "2025-02-24.acacia";
@@ -50,9 +52,13 @@ const stripeAdapterV20250224Acacia = {
50
52
  async capture(input) {
51
53
  const stripe = resolveStripeClient(input.credentials, STRIPE_API_VERSION);
52
54
  const paymentIntent = input.amount ? await stripe.paymentIntents.retrieve(input.sessionId) : null;
53
- const captured = await stripe.paymentIntents.capture(input.sessionId, {
54
- amount_to_capture: input.amount && paymentIntent ? toCents(input.amount, paymentIntent.currency) : void 0
55
- });
55
+ const captured = await stripe.paymentIntents.capture(
56
+ input.sessionId,
57
+ {
58
+ amount_to_capture: input.amount && paymentIntent ? toCents(input.amount, paymentIntent.currency) : void 0
59
+ },
60
+ stripeIdempotencyOptions(input.idempotencyKey)
61
+ );
56
62
  return {
57
63
  status: mapStripeStatus(captured.status),
58
64
  capturedAmount: fromCents(captured.amount_received, captured.currency),
@@ -62,23 +68,28 @@ const stripeAdapterV20250224Acacia = {
62
68
  async refund(input) {
63
69
  const stripe = resolveStripeClient(input.credentials, STRIPE_API_VERSION);
64
70
  const paymentIntent = input.amount ? await stripe.paymentIntents.retrieve(input.sessionId) : null;
65
- const refund = await stripe.refunds.create({
66
- payment_intent: input.sessionId,
67
- amount: input.amount && paymentIntent ? toCents(input.amount, paymentIntent.currency) : void 0,
68
- reason: mapRefundReason(input.reason),
69
- metadata: input.metadata
70
- });
71
+ const refund = await stripe.refunds.create(
72
+ {
73
+ payment_intent: input.sessionId,
74
+ amount: input.amount && paymentIntent ? toCents(input.amount, paymentIntent.currency) : void 0,
75
+ reason: mapRefundReason(input.reason),
76
+ metadata: input.metadata
77
+ },
78
+ stripeIdempotencyOptions(input.idempotencyKey)
79
+ );
71
80
  return {
72
81
  refundId: refund.id,
73
- status: refund.status === "succeeded" ? "refunded" : "pending",
82
+ status: await resolveStripeRefundStatus(stripe, refund, paymentIntent?.amount_received),
74
83
  refundedAmount: fromCents(refund.amount, refund.currency)
75
84
  };
76
85
  },
77
86
  async cancel(input) {
78
87
  const stripe = resolveStripeClient(input.credentials, STRIPE_API_VERSION);
79
- const cancelled = await stripe.paymentIntents.cancel(input.sessionId, {
80
- cancellation_reason: "requested_by_customer"
81
- });
88
+ const cancelled = await stripe.paymentIntents.cancel(
89
+ input.sessionId,
90
+ { cancellation_reason: "requested_by_customer" },
91
+ stripeIdempotencyOptions(input.idempotencyKey)
92
+ );
82
93
  return {
83
94
  status: mapStripeStatus(cancelled.status)
84
95
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/gateway_stripe/lib/adapters/v2025-02-24.acacia.ts"],
4
- "sourcesContent": ["import type {\n GatewayAdapter,\n CreateSessionInput,\n CreateSessionResult,\n CaptureInput,\n CaptureResult,\n RefundInput,\n RefundResult,\n CancelInput,\n CancelResult,\n GetStatusInput,\n GatewayPaymentStatus,\n VerifyWebhookInput,\n WebhookEvent,\n UnifiedPaymentStatus,\n} from '@open-mercato/shared/modules/payment_gateways/types'\nimport { resolveStripeClient } from '../client'\nimport { mapRefundReason, mapStripeStatus, mapWebhookEventToStatus } from '../status-map'\nimport {\n toCents,\n fromCents,\n buildStripeMetadata,\n normalizeStripePaymentElementSettings,\n resolveStripeRendererKey,\n} from '../shared'\nimport { verifyStripeWebhook } from '../webhook-handler'\n\nconst STRIPE_API_VERSION = '2025-02-24.acacia'\n\nexport const stripeAdapterV20250224Acacia: GatewayAdapter = {\n providerKey: 'stripe',\n\n async createSession(input: CreateSessionInput): Promise<CreateSessionResult> {\n const stripe = resolveStripeClient(input.credentials, STRIPE_API_VERSION)\n const rendererKey = resolveStripeRendererKey(input.presentation)\n const rendererSettings = normalizeStripePaymentElementSettings(input.presentation?.rendererSettings)\n\n const paymentIntent = await stripe.paymentIntents.create({\n amount: toCents(input.amount, input.currencyCode),\n currency: input.currencyCode.toLowerCase(),\n capture_method: input.captureMethod ?? 'automatic',\n metadata: buildStripeMetadata({\n paymentId: input.paymentId,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n orderId: input.orderId,\n }),\n payment_method_types: input.paymentTypes ?? ['card'],\n description: input.description,\n }, input.idempotencyKey ? { idempotencyKey: input.idempotencyKey } : undefined)\n\n return {\n sessionId: paymentIntent.id,\n clientSecret: paymentIntent.client_secret ?? undefined,\n status: mapStripeStatus(paymentIntent.status),\n providerData: {\n paymentIntentId: paymentIntent.id,\n publishableKey: typeof input.credentials.publishableKey === 'string'\n ? input.credentials.publishableKey\n : undefined,\n },\n clientSession: paymentIntent.client_secret && typeof input.credentials.publishableKey === 'string'\n ? {\n type: 'embedded',\n rendererKey,\n payload: {\n clientSecret: paymentIntent.client_secret,\n publishableKey: input.credentials.publishableKey,\n },\n settings: rendererSettings,\n }\n : undefined,\n }\n },\n\n async capture(input: CaptureInput): Promise<CaptureResult> {\n const stripe = resolveStripeClient(input.credentials, STRIPE_API_VERSION)\n const paymentIntent = input.amount\n ? await stripe.paymentIntents.retrieve(input.sessionId)\n : null\n\n const captured = await stripe.paymentIntents.capture(input.sessionId, {\n amount_to_capture: input.amount && paymentIntent\n ? toCents(input.amount, paymentIntent.currency)\n : undefined,\n })\n\n return {\n status: mapStripeStatus(captured.status),\n capturedAmount: fromCents(captured.amount_received, captured.currency),\n providerData: { chargeId: captured.latest_charge },\n }\n },\n\n async refund(input: RefundInput): Promise<RefundResult> {\n const stripe = resolveStripeClient(input.credentials, STRIPE_API_VERSION)\n const paymentIntent = input.amount\n ? await stripe.paymentIntents.retrieve(input.sessionId)\n : null\n\n const refund = await stripe.refunds.create({\n payment_intent: input.sessionId,\n amount: input.amount && paymentIntent\n ? toCents(input.amount, paymentIntent.currency)\n : undefined,\n reason: mapRefundReason(input.reason),\n metadata: input.metadata as Record<string, string> | undefined,\n })\n\n return {\n refundId: refund.id,\n status: refund.status === 'succeeded' ? 'refunded' : 'pending',\n refundedAmount: fromCents(refund.amount, refund.currency),\n }\n },\n\n async cancel(input: CancelInput): Promise<CancelResult> {\n const stripe = resolveStripeClient(input.credentials, STRIPE_API_VERSION)\n\n const cancelled = await stripe.paymentIntents.cancel(input.sessionId, {\n cancellation_reason: 'requested_by_customer',\n })\n\n return {\n status: mapStripeStatus(cancelled.status),\n }\n },\n\n async getStatus(input: GetStatusInput): Promise<GatewayPaymentStatus> {\n const stripe = resolveStripeClient(input.credentials, STRIPE_API_VERSION)\n\n const pi = await stripe.paymentIntents.retrieve(input.sessionId)\n return {\n status: mapStripeStatus(pi.status),\n amount: fromCents(pi.amount, pi.currency),\n amountReceived: fromCents(pi.amount_received, pi.currency),\n currencyCode: pi.currency.toUpperCase(),\n }\n },\n\n async verifyWebhook(input: VerifyWebhookInput): Promise<WebhookEvent> {\n return verifyStripeWebhook(input)\n },\n\n mapStatus(providerStatus: string, eventType?: string): UnifiedPaymentStatus {\n if (eventType) {\n const mappedEvent = mapWebhookEventToStatus(eventType)\n if (mappedEvent) return mappedEvent\n }\n return mapStripeStatus(providerStatus)\n },\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type {\n GatewayAdapter,\n CreateSessionInput,\n CreateSessionResult,\n CaptureInput,\n CaptureResult,\n RefundInput,\n RefundResult,\n CancelInput,\n CancelResult,\n GetStatusInput,\n GatewayPaymentStatus,\n VerifyWebhookInput,\n WebhookEvent,\n UnifiedPaymentStatus,\n} from '@open-mercato/shared/modules/payment_gateways/types'\nimport { resolveStripeClient } from '../client'\nimport { mapRefundReason, mapStripeStatus, mapWebhookEventToStatus } from '../status-map'\nimport {\n toCents,\n fromCents,\n buildStripeMetadata,\n normalizeStripePaymentElementSettings,\n resolveStripeRendererKey,\n resolveStripeRefundStatus,\n stripeIdempotencyOptions,\n} from '../shared'\nimport { verifyStripeWebhook } from '../webhook-handler'\n\nconst STRIPE_API_VERSION = '2025-02-24.acacia'\n\nexport const stripeAdapterV20250224Acacia: GatewayAdapter = {\n providerKey: 'stripe',\n\n async createSession(input: CreateSessionInput): Promise<CreateSessionResult> {\n const stripe = resolveStripeClient(input.credentials, STRIPE_API_VERSION)\n const rendererKey = resolveStripeRendererKey(input.presentation)\n const rendererSettings = normalizeStripePaymentElementSettings(input.presentation?.rendererSettings)\n\n const paymentIntent = await stripe.paymentIntents.create({\n amount: toCents(input.amount, input.currencyCode),\n currency: input.currencyCode.toLowerCase(),\n capture_method: input.captureMethod ?? 'automatic',\n metadata: buildStripeMetadata({\n paymentId: input.paymentId,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n orderId: input.orderId,\n }),\n payment_method_types: input.paymentTypes ?? ['card'],\n description: input.description,\n }, input.idempotencyKey ? { idempotencyKey: input.idempotencyKey } : undefined)\n\n return {\n sessionId: paymentIntent.id,\n clientSecret: paymentIntent.client_secret ?? undefined,\n status: mapStripeStatus(paymentIntent.status),\n providerData: {\n paymentIntentId: paymentIntent.id,\n publishableKey: typeof input.credentials.publishableKey === 'string'\n ? input.credentials.publishableKey\n : undefined,\n },\n clientSession: paymentIntent.client_secret && typeof input.credentials.publishableKey === 'string'\n ? {\n type: 'embedded',\n rendererKey,\n payload: {\n clientSecret: paymentIntent.client_secret,\n publishableKey: input.credentials.publishableKey,\n },\n settings: rendererSettings,\n }\n : undefined,\n }\n },\n\n async capture(input: CaptureInput): Promise<CaptureResult> {\n const stripe = resolveStripeClient(input.credentials, STRIPE_API_VERSION)\n const paymentIntent = input.amount\n ? await stripe.paymentIntents.retrieve(input.sessionId)\n : null\n\n const captured = await stripe.paymentIntents.capture(\n input.sessionId,\n {\n amount_to_capture: input.amount && paymentIntent\n ? toCents(input.amount, paymentIntent.currency)\n : undefined,\n },\n stripeIdempotencyOptions(input.idempotencyKey),\n )\n\n return {\n status: mapStripeStatus(captured.status),\n capturedAmount: fromCents(captured.amount_received, captured.currency),\n providerData: { chargeId: captured.latest_charge },\n }\n },\n\n async refund(input: RefundInput): Promise<RefundResult> {\n const stripe = resolveStripeClient(input.credentials, STRIPE_API_VERSION)\n const paymentIntent = input.amount\n ? await stripe.paymentIntents.retrieve(input.sessionId)\n : null\n\n const refund = await stripe.refunds.create(\n {\n payment_intent: input.sessionId,\n amount: input.amount && paymentIntent\n ? toCents(input.amount, paymentIntent.currency)\n : undefined,\n reason: mapRefundReason(input.reason),\n metadata: input.metadata as Record<string, string> | undefined,\n },\n stripeIdempotencyOptions(input.idempotencyKey),\n )\n\n return {\n refundId: refund.id,\n status: await resolveStripeRefundStatus(stripe, refund, paymentIntent?.amount_received),\n refundedAmount: fromCents(refund.amount, refund.currency),\n }\n },\n\n async cancel(input: CancelInput): Promise<CancelResult> {\n const stripe = resolveStripeClient(input.credentials, STRIPE_API_VERSION)\n\n const cancelled = await stripe.paymentIntents.cancel(\n input.sessionId,\n { cancellation_reason: 'requested_by_customer' },\n stripeIdempotencyOptions(input.idempotencyKey),\n )\n\n return {\n status: mapStripeStatus(cancelled.status),\n }\n },\n\n async getStatus(input: GetStatusInput): Promise<GatewayPaymentStatus> {\n const stripe = resolveStripeClient(input.credentials, STRIPE_API_VERSION)\n\n const pi = await stripe.paymentIntents.retrieve(input.sessionId)\n return {\n status: mapStripeStatus(pi.status),\n amount: fromCents(pi.amount, pi.currency),\n amountReceived: fromCents(pi.amount_received, pi.currency),\n currencyCode: pi.currency.toUpperCase(),\n }\n },\n\n async verifyWebhook(input: VerifyWebhookInput): Promise<WebhookEvent> {\n return verifyStripeWebhook(input)\n },\n\n mapStatus(providerStatus: string, eventType?: string): UnifiedPaymentStatus {\n if (eventType) {\n const mappedEvent = mapWebhookEventToStatus(eventType)\n if (mappedEvent) return mappedEvent\n }\n return mapStripeStatus(providerStatus)\n },\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -25,6 +25,18 @@ function toCents(amount, currencyCode) {
25
25
  function fromCents(amount, currencyCode) {
26
26
  return isZeroDecimalCurrency(currencyCode) ? amount : amount / 100;
27
27
  }
28
+ function stripeIdempotencyOptions(idempotencyKey) {
29
+ return idempotencyKey ? { idempotencyKey } : void 0;
30
+ }
31
+ async function resolveStripeRefundStatus(stripe, refund, capturedAmount) {
32
+ if (refund.status !== "succeeded") return "pending";
33
+ const chargeId = typeof refund.charge === "string" ? refund.charge : refund.charge?.id;
34
+ if (chargeId) {
35
+ const charge = await stripe.charges.retrieve(chargeId);
36
+ return charge.amount_refunded < charge.amount ? "partially_refunded" : "refunded";
37
+ }
38
+ return capturedAmount !== void 0 && refund.amount < capturedAmount ? "partially_refunded" : "refunded";
39
+ }
28
40
  function buildStripeMetadata(input) {
29
41
  const meta = {
30
42
  paymentId: input.paymentId,
@@ -65,7 +77,9 @@ export {
65
77
  buildStripeMetadata,
66
78
  fromCents,
67
79
  normalizeStripePaymentElementSettings,
80
+ resolveStripeRefundStatus,
68
81
  resolveStripeRendererKey,
82
+ stripeIdempotencyOptions,
69
83
  toCents
70
84
  };
71
85
  //# sourceMappingURL=shared.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/gateway_stripe/lib/shared.ts"],
4
- "sourcesContent": ["import type { PaymentGatewayPresentationRequest } from '@open-mercato/shared/modules/payment_gateways/types'\n\nconst ZERO_DECIMAL_CURRENCIES = new Set([\n 'BIF',\n 'CLP',\n 'DJF',\n 'GNF',\n 'JPY',\n 'KMF',\n 'KRW',\n 'MGA',\n 'PYG',\n 'RWF',\n 'UGX',\n 'VND',\n 'VUV',\n 'XAF',\n 'XOF',\n 'XPF',\n])\n\nfunction isZeroDecimalCurrency(currencyCode: string): boolean {\n return ZERO_DECIMAL_CURRENCIES.has(currencyCode.toUpperCase())\n}\n\nexport function toCents(amount: number, currencyCode: string): number {\n return isZeroDecimalCurrency(currencyCode)\n ? Math.round(amount)\n : Math.round(amount * 100)\n}\n\nexport function fromCents(amount: number, currencyCode: string): number {\n return isZeroDecimalCurrency(currencyCode)\n ? amount\n : amount / 100\n}\n\nexport function buildStripeMetadata(input: {\n paymentId: string\n tenantId: string\n organizationId: string\n orderId?: string\n}): Record<string, string> {\n const meta: Record<string, string> = {\n paymentId: input.paymentId,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n }\n if (input.orderId) {\n meta.orderId = input.orderId\n }\n return meta\n}\n\nconst STRIPE_PAYMENT_METHOD_ORDER = new Set([\n 'card',\n 'apple_pay',\n 'google_pay',\n 'link',\n])\n\nexport type StripePaymentElementSettings = {\n layout?: 'tabs' | 'accordion'\n paymentMethodOrder?: string[]\n billingDetails?: 'auto' | 'never' | 'if_required'\n}\n\nexport function resolveStripeRendererKey(request?: PaymentGatewayPresentationRequest): string {\n return request?.rendererKey === 'stripe.payment_element'\n ? request.rendererKey\n : 'stripe.payment_element'\n}\n\nexport function normalizeStripePaymentElementSettings(\n settings: Record<string, unknown> | null | undefined,\n): StripePaymentElementSettings | undefined {\n if (!settings) return undefined\n\n const layout = settings.layout === 'tabs' || settings.layout === 'accordion'\n ? settings.layout\n : undefined\n const billingDetails = settings.billingDetails === 'auto'\n || settings.billingDetails === 'never'\n || settings.billingDetails === 'if_required'\n ? settings.billingDetails\n : undefined\n const paymentMethodOrder = Array.isArray(settings.paymentMethodOrder)\n ? settings.paymentMethodOrder.filter(\n (value): value is string => typeof value === 'string' && STRIPE_PAYMENT_METHOD_ORDER.has(value),\n )\n : []\n\n if (!layout && !billingDetails && paymentMethodOrder.length === 0) {\n return undefined\n }\n\n return {\n ...(layout ? { layout } : {}),\n ...(billingDetails ? { billingDetails } : {}),\n ...(paymentMethodOrder.length > 0 ? { paymentMethodOrder } : {}),\n }\n}\n"],
5
- "mappings": "AAEA,MAAM,0BAA0B,oBAAI,IAAI;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,SAAS,sBAAsB,cAA+B;AAC5D,SAAO,wBAAwB,IAAI,aAAa,YAAY,CAAC;AAC/D;AAEO,SAAS,QAAQ,QAAgB,cAA8B;AACpE,SAAO,sBAAsB,YAAY,IACrC,KAAK,MAAM,MAAM,IACjB,KAAK,MAAM,SAAS,GAAG;AAC7B;AAEO,SAAS,UAAU,QAAgB,cAA8B;AACtE,SAAO,sBAAsB,YAAY,IACrC,SACA,SAAS;AACf;AAEO,SAAS,oBAAoB,OAKT;AACzB,QAAM,OAA+B;AAAA,IACnC,WAAW,MAAM;AAAA,IACjB,UAAU,MAAM;AAAA,IAChB,gBAAgB,MAAM;AAAA,EACxB;AACA,MAAI,MAAM,SAAS;AACjB,SAAK,UAAU,MAAM;AAAA,EACvB;AACA,SAAO;AACT;AAEA,MAAM,8BAA8B,oBAAI,IAAI;AAAA,EAC1C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAQM,SAAS,yBAAyB,SAAqD;AAC5F,SAAO,SAAS,gBAAgB,2BAC5B,QAAQ,cACR;AACN;AAEO,SAAS,sCACd,UAC0C;AAC1C,MAAI,CAAC,SAAU,QAAO;AAEtB,QAAM,SAAS,SAAS,WAAW,UAAU,SAAS,WAAW,cAC7D,SAAS,SACT;AACJ,QAAM,iBAAiB,SAAS,mBAAmB,UAC9C,SAAS,mBAAmB,WAC5B,SAAS,mBAAmB,gBAC7B,SAAS,iBACT;AACJ,QAAM,qBAAqB,MAAM,QAAQ,SAAS,kBAAkB,IAChE,SAAS,mBAAmB;AAAA,IAC1B,CAAC,UAA2B,OAAO,UAAU,YAAY,4BAA4B,IAAI,KAAK;AAAA,EAChG,IACA,CAAC;AAEL,MAAI,CAAC,UAAU,CAAC,kBAAkB,mBAAmB,WAAW,GAAG;AACjE,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;AAAA,IAC3B,GAAI,iBAAiB,EAAE,eAAe,IAAI,CAAC;AAAA,IAC3C,GAAI,mBAAmB,SAAS,IAAI,EAAE,mBAAmB,IAAI,CAAC;AAAA,EAChE;AACF;",
4
+ "sourcesContent": ["import type { PaymentGatewayPresentationRequest } from '@open-mercato/shared/modules/payment_gateways/types'\nimport type Stripe from 'stripe'\n\nconst ZERO_DECIMAL_CURRENCIES = new Set([\n 'BIF',\n 'CLP',\n 'DJF',\n 'GNF',\n 'JPY',\n 'KMF',\n 'KRW',\n 'MGA',\n 'PYG',\n 'RWF',\n 'UGX',\n 'VND',\n 'VUV',\n 'XAF',\n 'XOF',\n 'XPF',\n])\n\nfunction isZeroDecimalCurrency(currencyCode: string): boolean {\n return ZERO_DECIMAL_CURRENCIES.has(currencyCode.toUpperCase())\n}\n\nexport function toCents(amount: number, currencyCode: string): number {\n return isZeroDecimalCurrency(currencyCode)\n ? Math.round(amount)\n : Math.round(amount * 100)\n}\n\nexport function fromCents(amount: number, currencyCode: string): number {\n return isZeroDecimalCurrency(currencyCode)\n ? amount\n : amount / 100\n}\n\nexport function stripeIdempotencyOptions(idempotencyKey?: string): { idempotencyKey: string } | undefined {\n return idempotencyKey ? { idempotencyKey } : undefined\n}\n\nexport async function resolveStripeRefundStatus(\n stripe: Pick<Stripe, 'charges'>,\n refund: Stripe.Refund,\n capturedAmount?: number,\n): Promise<'pending' | 'partially_refunded' | 'refunded'> {\n if (refund.status !== 'succeeded') return 'pending'\n const chargeId = typeof refund.charge === 'string' ? refund.charge : refund.charge?.id\n if (chargeId) {\n const charge = await stripe.charges.retrieve(chargeId)\n return charge.amount_refunded < charge.amount ? 'partially_refunded' : 'refunded'\n }\n return capturedAmount !== undefined && refund.amount < capturedAmount\n ? 'partially_refunded'\n : 'refunded'\n}\n\nexport function buildStripeMetadata(input: {\n paymentId: string\n tenantId: string\n organizationId: string\n orderId?: string\n}): Record<string, string> {\n const meta: Record<string, string> = {\n paymentId: input.paymentId,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n }\n if (input.orderId) {\n meta.orderId = input.orderId\n }\n return meta\n}\n\nconst STRIPE_PAYMENT_METHOD_ORDER = new Set([\n 'card',\n 'apple_pay',\n 'google_pay',\n 'link',\n])\n\nexport type StripePaymentElementSettings = {\n layout?: 'tabs' | 'accordion'\n paymentMethodOrder?: string[]\n billingDetails?: 'auto' | 'never' | 'if_required'\n}\n\nexport function resolveStripeRendererKey(request?: PaymentGatewayPresentationRequest): string {\n return request?.rendererKey === 'stripe.payment_element'\n ? request.rendererKey\n : 'stripe.payment_element'\n}\n\nexport function normalizeStripePaymentElementSettings(\n settings: Record<string, unknown> | null | undefined,\n): StripePaymentElementSettings | undefined {\n if (!settings) return undefined\n\n const layout = settings.layout === 'tabs' || settings.layout === 'accordion'\n ? settings.layout\n : undefined\n const billingDetails = settings.billingDetails === 'auto'\n || settings.billingDetails === 'never'\n || settings.billingDetails === 'if_required'\n ? settings.billingDetails\n : undefined\n const paymentMethodOrder = Array.isArray(settings.paymentMethodOrder)\n ? settings.paymentMethodOrder.filter(\n (value): value is string => typeof value === 'string' && STRIPE_PAYMENT_METHOD_ORDER.has(value),\n )\n : []\n\n if (!layout && !billingDetails && paymentMethodOrder.length === 0) {\n return undefined\n }\n\n return {\n ...(layout ? { layout } : {}),\n ...(billingDetails ? { billingDetails } : {}),\n ...(paymentMethodOrder.length > 0 ? { paymentMethodOrder } : {}),\n }\n}\n"],
5
+ "mappings": "AAGA,MAAM,0BAA0B,oBAAI,IAAI;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,SAAS,sBAAsB,cAA+B;AAC5D,SAAO,wBAAwB,IAAI,aAAa,YAAY,CAAC;AAC/D;AAEO,SAAS,QAAQ,QAAgB,cAA8B;AACpE,SAAO,sBAAsB,YAAY,IACrC,KAAK,MAAM,MAAM,IACjB,KAAK,MAAM,SAAS,GAAG;AAC7B;AAEO,SAAS,UAAU,QAAgB,cAA8B;AACtE,SAAO,sBAAsB,YAAY,IACrC,SACA,SAAS;AACf;AAEO,SAAS,yBAAyB,gBAAiE;AACxG,SAAO,iBAAiB,EAAE,eAAe,IAAI;AAC/C;AAEA,eAAsB,0BACpB,QACA,QACA,gBACwD;AACxD,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,QAAM,WAAW,OAAO,OAAO,WAAW,WAAW,OAAO,SAAS,OAAO,QAAQ;AACpF,MAAI,UAAU;AACZ,UAAM,SAAS,MAAM,OAAO,QAAQ,SAAS,QAAQ;AACrD,WAAO,OAAO,kBAAkB,OAAO,SAAS,uBAAuB;AAAA,EACzE;AACA,SAAO,mBAAmB,UAAa,OAAO,SAAS,iBACnD,uBACA;AACN;AAEO,SAAS,oBAAoB,OAKT;AACzB,QAAM,OAA+B;AAAA,IACnC,WAAW,MAAM;AAAA,IACjB,UAAU,MAAM;AAAA,IAChB,gBAAgB,MAAM;AAAA,EACxB;AACA,MAAI,MAAM,SAAS;AACjB,SAAK,UAAU,MAAM;AAAA,EACvB;AACA,SAAO;AACT;AAEA,MAAM,8BAA8B,oBAAI,IAAI;AAAA,EAC1C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAQM,SAAS,yBAAyB,SAAqD;AAC5F,SAAO,SAAS,gBAAgB,2BAC5B,QAAQ,cACR;AACN;AAEO,SAAS,sCACd,UAC0C;AAC1C,MAAI,CAAC,SAAU,QAAO;AAEtB,QAAM,SAAS,SAAS,WAAW,UAAU,SAAS,WAAW,cAC7D,SAAS,SACT;AACJ,QAAM,iBAAiB,SAAS,mBAAmB,UAC9C,SAAS,mBAAmB,WAC5B,SAAS,mBAAmB,gBAC7B,SAAS,iBACT;AACJ,QAAM,qBAAqB,MAAM,QAAQ,SAAS,kBAAkB,IAChE,SAAS,mBAAmB;AAAA,IAC1B,CAAC,UAA2B,OAAO,UAAU,YAAY,4BAA4B,IAAI,KAAK;AAAA,EAChG,IACA,CAAC;AAEL,MAAI,CAAC,UAAU,CAAC,kBAAkB,mBAAmB,WAAW,GAAG;AACjE,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;AAAA,IAC3B,GAAI,iBAAiB,EAAE,eAAe,IAAI,CAAC;AAAA,IAC3C,GAAI,mBAAmB,SAAS,IAAI,EAAE,mBAAmB,IAAI,CAAC;AAAA,EAChE;AACF;",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/gateway-stripe",
3
- "version": "0.6.7-develop.6572.1.3d8e83062f",
3
+ "version": "0.6.7-develop.6574.1.05dcf72ba8",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -62,21 +62,21 @@
62
62
  }
63
63
  },
64
64
  "dependencies": {
65
- "@open-mercato/core": "0.6.7-develop.6572.1.3d8e83062f",
66
- "@open-mercato/queue": "0.6.7-develop.6572.1.3d8e83062f",
67
- "@open-mercato/ui": "0.6.7-develop.6572.1.3d8e83062f",
65
+ "@open-mercato/core": "0.6.7-develop.6574.1.05dcf72ba8",
66
+ "@open-mercato/queue": "0.6.7-develop.6574.1.05dcf72ba8",
67
+ "@open-mercato/ui": "0.6.7-develop.6574.1.05dcf72ba8",
68
68
  "@stripe/react-stripe-js": "^6.6.0",
69
69
  "@stripe/stripe-js": "^9.8.0",
70
70
  "stripe": "^22.3.0"
71
71
  },
72
72
  "peerDependencies": {
73
73
  "@mikro-orm/postgresql": "^7.0.14",
74
- "@open-mercato/shared": "0.6.7-develop.6572.1.3d8e83062f",
74
+ "@open-mercato/shared": "0.6.7-develop.6574.1.05dcf72ba8",
75
75
  "react": "^19.0.0",
76
76
  "react-dom": "^19.0.0"
77
77
  },
78
78
  "devDependencies": {
79
- "@open-mercato/shared": "0.6.7-develop.6572.1.3d8e83062f",
79
+ "@open-mercato/shared": "0.6.7-develop.6574.1.05dcf72ba8",
80
80
  "@types/jest": "^30.0.0",
81
81
  "@types/react": "^19.2.17",
82
82
  "@types/react-dom": "^19.2.3",
@@ -8,12 +8,101 @@ jest.mock('../lib/client', () => ({
8
8
  resolveStripeClient: jest.fn(),
9
9
  }))
10
10
 
11
+ const capture = jest.fn()
12
+ const createRefund = jest.fn()
13
+ const cancel = jest.fn()
14
+ const retrievePaymentIntent = jest.fn()
15
+ const retrieveCharge = jest.fn()
16
+
17
+ const stripe = {
18
+ paymentIntents: {
19
+ capture,
20
+ cancel,
21
+ retrieve: retrievePaymentIntent,
22
+ },
23
+ refunds: { create: createRefund },
24
+ charges: { retrieve: retrieveCharge },
25
+ }
26
+
11
27
  const adapters: Array<[string, GatewayAdapter]> = [
12
28
  ['2023-10-16', stripeAdapterV20231016],
13
29
  ['2024-12-18', stripeAdapterV20241218],
14
30
  ['2025-02-24.acacia', stripeAdapterV20250224Acacia],
15
31
  ]
16
32
 
33
+ describe('Stripe manual payment operation idempotency', () => {
34
+ beforeEach(() => {
35
+ jest.clearAllMocks()
36
+ ;(resolveStripeClient as jest.Mock).mockReturnValue(stripe)
37
+ capture.mockResolvedValue({ status: 'succeeded', amount_received: 1000, currency: 'usd', latest_charge: 'ch_1' })
38
+ createRefund.mockResolvedValue({ id: 're_1', status: 'succeeded', amount: 1000, currency: 'usd' })
39
+ cancel.mockResolvedValue({ status: 'canceled' })
40
+ })
41
+
42
+ it.each(adapters)('%s forwards the core idempotency key to capture', async (_version, adapter) => {
43
+ await adapter.capture({
44
+ sessionId: 'pi_1',
45
+ credentials: { secretKey: 'sk_test' },
46
+ idempotencyKey: 'payment-operation-key',
47
+ })
48
+
49
+ expect(capture).toHaveBeenCalledWith(
50
+ 'pi_1',
51
+ expect.any(Object),
52
+ { idempotencyKey: 'payment-operation-key' },
53
+ )
54
+ })
55
+
56
+ it.each(adapters)('%s forwards the core idempotency key to refund', async (_version, adapter) => {
57
+ await adapter.refund({
58
+ sessionId: 'pi_1',
59
+ credentials: { secretKey: 'sk_test' },
60
+ idempotencyKey: 'payment-operation-key',
61
+ })
62
+
63
+ expect(createRefund).toHaveBeenCalledWith(
64
+ expect.objectContaining({ payment_intent: 'pi_1' }),
65
+ { idempotencyKey: 'payment-operation-key' },
66
+ )
67
+ })
68
+
69
+ it.each(adapters)('%s keeps a successful partial refund non-terminal', async (_version, adapter) => {
70
+ retrievePaymentIntent.mockResolvedValue({ amount_received: 4000, currency: 'usd' })
71
+ createRefund.mockResolvedValue({
72
+ id: 're_partial',
73
+ status: 'succeeded',
74
+ amount: 1000,
75
+ currency: 'usd',
76
+ charge: 'ch_1',
77
+ })
78
+ retrieveCharge.mockResolvedValue({ amount: 4000, amount_refunded: 1000 })
79
+
80
+ const result = await adapter.refund({
81
+ sessionId: 'pi_1',
82
+ amount: 10,
83
+ credentials: { secretKey: 'sk_test' },
84
+ idempotencyKey: 'partial-refund-operation',
85
+ })
86
+
87
+ expect(result.status).toBe('partially_refunded')
88
+ expect(retrieveCharge).toHaveBeenCalledWith('ch_1')
89
+ })
90
+
91
+ it.each(adapters)('%s forwards the core idempotency key to cancel', async (_version, adapter) => {
92
+ await adapter.cancel({
93
+ sessionId: 'pi_1',
94
+ credentials: { secretKey: 'sk_test' },
95
+ idempotencyKey: 'payment-operation-key',
96
+ })
97
+
98
+ expect(cancel).toHaveBeenCalledWith(
99
+ 'pi_1',
100
+ expect.anything(),
101
+ { idempotencyKey: 'payment-operation-key' },
102
+ )
103
+ })
104
+ })
105
+
17
106
  describe.each(adapters)('Stripe adapter %s session idempotency', (_version, adapter) => {
18
107
  it('passes the stable operation key to Stripe PaymentIntent creation', async () => {
19
108
  const create = jest.fn(async () => ({
@@ -22,6 +22,8 @@ import {
22
22
  buildStripeMetadata,
23
23
  normalizeStripePaymentElementSettings,
24
24
  resolveStripeRendererKey,
25
+ resolveStripeRefundStatus,
26
+ stripeIdempotencyOptions,
25
27
  } from '../shared'
26
28
  import { verifyStripeWebhook } from '../webhook-handler'
27
29
 
@@ -76,11 +78,15 @@ export const stripeAdapterV20231016: GatewayAdapter = {
76
78
  const paymentIntent = input.amount
77
79
  ? await stripe.paymentIntents.retrieve(input.sessionId)
78
80
  : null
79
- const captured = await stripe.paymentIntents.capture(input.sessionId, {
80
- amount_to_capture: input.amount && paymentIntent
81
- ? toCents(input.amount, paymentIntent.currency)
82
- : undefined,
83
- })
81
+ const captured = await stripe.paymentIntents.capture(
82
+ input.sessionId,
83
+ {
84
+ amount_to_capture: input.amount && paymentIntent
85
+ ? toCents(input.amount, paymentIntent.currency)
86
+ : undefined,
87
+ },
88
+ stripeIdempotencyOptions(input.idempotencyKey),
89
+ )
84
90
  return {
85
91
  status: mapStripeStatus(captured.status),
86
92
  capturedAmount: fromCents(captured.amount_received, captured.currency),
@@ -93,23 +99,30 @@ export const stripeAdapterV20231016: GatewayAdapter = {
93
99
  const paymentIntent = input.amount
94
100
  ? await stripe.paymentIntents.retrieve(input.sessionId)
95
101
  : null
96
- const refund = await stripe.refunds.create({
97
- payment_intent: input.sessionId,
98
- amount: input.amount && paymentIntent
99
- ? toCents(input.amount, paymentIntent.currency)
100
- : undefined,
101
- reason: mapRefundReason(input.reason),
102
- })
102
+ const refund = await stripe.refunds.create(
103
+ {
104
+ payment_intent: input.sessionId,
105
+ amount: input.amount && paymentIntent
106
+ ? toCents(input.amount, paymentIntent.currency)
107
+ : undefined,
108
+ reason: mapRefundReason(input.reason),
109
+ },
110
+ stripeIdempotencyOptions(input.idempotencyKey),
111
+ )
103
112
  return {
104
113
  refundId: refund.id,
105
- status: refund.status === 'succeeded' ? 'refunded' : 'pending',
114
+ status: await resolveStripeRefundStatus(stripe, refund, paymentIntent?.amount_received),
106
115
  refundedAmount: fromCents(refund.amount, refund.currency),
107
116
  }
108
117
  },
109
118
 
110
119
  async cancel(input: CancelInput): Promise<CancelResult> {
111
120
  const stripe = resolveStripeClient(input.credentials, '2023-10-16')
112
- const cancelled = await stripe.paymentIntents.cancel(input.sessionId)
121
+ const cancelled = await stripe.paymentIntents.cancel(
122
+ input.sessionId,
123
+ {},
124
+ stripeIdempotencyOptions(input.idempotencyKey),
125
+ )
113
126
  return { status: mapStripeStatus(cancelled.status) }
114
127
  },
115
128
 
@@ -22,6 +22,8 @@ import {
22
22
  buildStripeMetadata,
23
23
  normalizeStripePaymentElementSettings,
24
24
  resolveStripeRendererKey,
25
+ resolveStripeRefundStatus,
26
+ stripeIdempotencyOptions,
25
27
  } from '../shared'
26
28
  import { verifyStripeWebhook } from '../webhook-handler'
27
29
 
@@ -79,11 +81,15 @@ export const stripeAdapterV20241218: GatewayAdapter = {
79
81
  ? await stripe.paymentIntents.retrieve(input.sessionId)
80
82
  : null
81
83
 
82
- const captured = await stripe.paymentIntents.capture(input.sessionId, {
83
- amount_to_capture: input.amount && paymentIntent
84
- ? toCents(input.amount, paymentIntent.currency)
85
- : undefined,
86
- })
84
+ const captured = await stripe.paymentIntents.capture(
85
+ input.sessionId,
86
+ {
87
+ amount_to_capture: input.amount && paymentIntent
88
+ ? toCents(input.amount, paymentIntent.currency)
89
+ : undefined,
90
+ },
91
+ stripeIdempotencyOptions(input.idempotencyKey),
92
+ )
87
93
 
88
94
  const capturedCurrencyCode = captured.currency.toUpperCase()
89
95
 
@@ -100,20 +106,23 @@ export const stripeAdapterV20241218: GatewayAdapter = {
100
106
  ? await stripe.paymentIntents.retrieve(input.sessionId)
101
107
  : null
102
108
 
103
- const refund = await stripe.refunds.create({
104
- payment_intent: input.sessionId,
105
- amount: input.amount && paymentIntent
106
- ? toCents(input.amount, paymentIntent.currency)
107
- : undefined,
108
- reason: mapRefundReason(input.reason),
109
- metadata: input.metadata as Record<string, string> | undefined,
110
- })
109
+ const refund = await stripe.refunds.create(
110
+ {
111
+ payment_intent: input.sessionId,
112
+ amount: input.amount && paymentIntent
113
+ ? toCents(input.amount, paymentIntent.currency)
114
+ : undefined,
115
+ reason: mapRefundReason(input.reason),
116
+ metadata: input.metadata as Record<string, string> | undefined,
117
+ },
118
+ stripeIdempotencyOptions(input.idempotencyKey),
119
+ )
111
120
 
112
121
  const refundCurrencyCode = refund.currency.toUpperCase()
113
122
 
114
123
  return {
115
124
  refundId: refund.id,
116
- status: refund.status === 'succeeded' ? 'refunded' : 'pending',
125
+ status: await resolveStripeRefundStatus(stripe, refund, paymentIntent?.amount_received),
117
126
  refundedAmount: fromCents(refund.amount, refundCurrencyCode),
118
127
  }
119
128
  },
@@ -121,9 +130,11 @@ export const stripeAdapterV20241218: GatewayAdapter = {
121
130
  async cancel(input: CancelInput): Promise<CancelResult> {
122
131
  const stripe = resolveStripeClient(input.credentials, STRIPE_API_VERSION)
123
132
 
124
- const cancelled = await stripe.paymentIntents.cancel(input.sessionId, {
125
- cancellation_reason: 'requested_by_customer',
126
- })
133
+ const cancelled = await stripe.paymentIntents.cancel(
134
+ input.sessionId,
135
+ { cancellation_reason: 'requested_by_customer' },
136
+ stripeIdempotencyOptions(input.idempotencyKey),
137
+ )
127
138
 
128
139
  return {
129
140
  status: mapStripeStatus(cancelled.status),
@@ -22,6 +22,8 @@ import {
22
22
  buildStripeMetadata,
23
23
  normalizeStripePaymentElementSettings,
24
24
  resolveStripeRendererKey,
25
+ resolveStripeRefundStatus,
26
+ stripeIdempotencyOptions,
25
27
  } from '../shared'
26
28
  import { verifyStripeWebhook } from '../webhook-handler'
27
29
 
@@ -79,11 +81,15 @@ export const stripeAdapterV20250224Acacia: GatewayAdapter = {
79
81
  ? await stripe.paymentIntents.retrieve(input.sessionId)
80
82
  : null
81
83
 
82
- const captured = await stripe.paymentIntents.capture(input.sessionId, {
83
- amount_to_capture: input.amount && paymentIntent
84
- ? toCents(input.amount, paymentIntent.currency)
85
- : undefined,
86
- })
84
+ const captured = await stripe.paymentIntents.capture(
85
+ input.sessionId,
86
+ {
87
+ amount_to_capture: input.amount && paymentIntent
88
+ ? toCents(input.amount, paymentIntent.currency)
89
+ : undefined,
90
+ },
91
+ stripeIdempotencyOptions(input.idempotencyKey),
92
+ )
87
93
 
88
94
  return {
89
95
  status: mapStripeStatus(captured.status),
@@ -98,18 +104,21 @@ export const stripeAdapterV20250224Acacia: GatewayAdapter = {
98
104
  ? await stripe.paymentIntents.retrieve(input.sessionId)
99
105
  : null
100
106
 
101
- const refund = await stripe.refunds.create({
102
- payment_intent: input.sessionId,
103
- amount: input.amount && paymentIntent
104
- ? toCents(input.amount, paymentIntent.currency)
105
- : undefined,
106
- reason: mapRefundReason(input.reason),
107
- metadata: input.metadata as Record<string, string> | undefined,
108
- })
107
+ const refund = await stripe.refunds.create(
108
+ {
109
+ payment_intent: input.sessionId,
110
+ amount: input.amount && paymentIntent
111
+ ? toCents(input.amount, paymentIntent.currency)
112
+ : undefined,
113
+ reason: mapRefundReason(input.reason),
114
+ metadata: input.metadata as Record<string, string> | undefined,
115
+ },
116
+ stripeIdempotencyOptions(input.idempotencyKey),
117
+ )
109
118
 
110
119
  return {
111
120
  refundId: refund.id,
112
- status: refund.status === 'succeeded' ? 'refunded' : 'pending',
121
+ status: await resolveStripeRefundStatus(stripe, refund, paymentIntent?.amount_received),
113
122
  refundedAmount: fromCents(refund.amount, refund.currency),
114
123
  }
115
124
  },
@@ -117,9 +126,11 @@ export const stripeAdapterV20250224Acacia: GatewayAdapter = {
117
126
  async cancel(input: CancelInput): Promise<CancelResult> {
118
127
  const stripe = resolveStripeClient(input.credentials, STRIPE_API_VERSION)
119
128
 
120
- const cancelled = await stripe.paymentIntents.cancel(input.sessionId, {
121
- cancellation_reason: 'requested_by_customer',
122
- })
129
+ const cancelled = await stripe.paymentIntents.cancel(
130
+ input.sessionId,
131
+ { cancellation_reason: 'requested_by_customer' },
132
+ stripeIdempotencyOptions(input.idempotencyKey),
133
+ )
123
134
 
124
135
  return {
125
136
  status: mapStripeStatus(cancelled.status),
@@ -1,4 +1,5 @@
1
1
  import type { PaymentGatewayPresentationRequest } from '@open-mercato/shared/modules/payment_gateways/types'
2
+ import type Stripe from 'stripe'
2
3
 
3
4
  const ZERO_DECIMAL_CURRENCIES = new Set([
4
5
  'BIF',
@@ -35,6 +36,26 @@ export function fromCents(amount: number, currencyCode: string): number {
35
36
  : amount / 100
36
37
  }
37
38
 
39
+ export function stripeIdempotencyOptions(idempotencyKey?: string): { idempotencyKey: string } | undefined {
40
+ return idempotencyKey ? { idempotencyKey } : undefined
41
+ }
42
+
43
+ export async function resolveStripeRefundStatus(
44
+ stripe: Pick<Stripe, 'charges'>,
45
+ refund: Stripe.Refund,
46
+ capturedAmount?: number,
47
+ ): Promise<'pending' | 'partially_refunded' | 'refunded'> {
48
+ if (refund.status !== 'succeeded') return 'pending'
49
+ const chargeId = typeof refund.charge === 'string' ? refund.charge : refund.charge?.id
50
+ if (chargeId) {
51
+ const charge = await stripe.charges.retrieve(chargeId)
52
+ return charge.amount_refunded < charge.amount ? 'partially_refunded' : 'refunded'
53
+ }
54
+ return capturedAmount !== undefined && refund.amount < capturedAmount
55
+ ? 'partially_refunded'
56
+ : 'refunded'
57
+ }
58
+
38
59
  export function buildStripeMetadata(input: {
39
60
  paymentId: string
40
61
  tenantId: string