@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.
- package/dist/modules/gateway_stripe/lib/adapters/v2023-10-16.js +24 -11
- package/dist/modules/gateway_stripe/lib/adapters/v2023-10-16.js.map +2 -2
- package/dist/modules/gateway_stripe/lib/adapters/v2024-12-18.js +25 -14
- package/dist/modules/gateway_stripe/lib/adapters/v2024-12-18.js.map +2 -2
- package/dist/modules/gateway_stripe/lib/adapters/v2025-02-24.acacia.js +25 -14
- package/dist/modules/gateway_stripe/lib/adapters/v2025-02-24.acacia.js.map +2 -2
- package/dist/modules/gateway_stripe/lib/shared.js +14 -0
- package/dist/modules/gateway_stripe/lib/shared.js.map +2 -2
- package/package.json +6 -6
- package/src/modules/gateway_stripe/__tests__/adapter-idempotency.test.ts +89 -0
- package/src/modules/gateway_stripe/lib/adapters/v2023-10-16.ts +27 -14
- package/src/modules/gateway_stripe/lib/adapters/v2024-12-18.ts +28 -17
- package/src/modules/gateway_stripe/lib/adapters/v2025-02-24.acacia.ts +28 -17
- package/src/modules/gateway_stripe/lib/shared.ts +21 -0
|
@@ -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(
|
|
53
|
-
|
|
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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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:
|
|
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(
|
|
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
|
|
5
|
-
"mappings": "AAgBA,SAAS,2BAA2B;AACpC,SAAS,iBAAiB,iBAAiB,+BAA+B;AAC1E;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,2BAA2B;AAE7B,MAAM,yBAAyC;AAAA,EACpD,aAAa;AAAA,EAEb,MAAM,cAAc,OAAyD;AAC3E,UAAM,SAAS,oBAAoB,MAAM,aAAa,YAAY;AAClE,UAAM,cAAc,yBAAyB,MAAM,YAAY;AAC/D,UAAM,mBAAmB,sCAAsC,MAAM,cAAc,gBAAgB;AAEnG,UAAM,gBAAgB,MAAM,OAAO,eAAe,OAAO;AAAA,MACvD,QAAQ,QAAQ,MAAM,QAAQ,MAAM,YAAY;AAAA,MAChD,UAAU,MAAM,aAAa,YAAY;AAAA,MACzC,gBAAgB,MAAM,iBAAiB;AAAA,MACvC,UAAU,oBAAoB;AAAA,QAC5B,WAAW,MAAM;AAAA,QACjB,UAAU,MAAM;AAAA,QAChB,gBAAgB,MAAM;AAAA,QACtB,SAAS,MAAM;AAAA,MACjB,CAAC;AAAA,MACD,sBAAsB,MAAM,gBAAgB,CAAC,MAAM;AAAA,MACnD,aAAa,MAAM;AAAA,IACrB,GAAG,MAAM,iBAAiB,EAAE,gBAAgB,MAAM,eAAe,IAAI,MAAS;AAE9E,WAAO;AAAA,MACL,WAAW,cAAc;AAAA,MACzB,cAAc,cAAc,iBAAiB;AAAA,MAC7C,QAAQ,gBAAgB,cAAc,MAAM;AAAA,MAC5C,cAAc;AAAA,QACZ,iBAAiB,cAAc;AAAA,QAC/B,gBAAgB,OAAO,MAAM,YAAY,mBAAmB,WACxD,MAAM,YAAY,iBAClB;AAAA,MACN;AAAA,MACA,eAAe,cAAc,iBAAiB,OAAO,MAAM,YAAY,mBAAmB,WACtF;AAAA,QACE,MAAM;AAAA,QACN;AAAA,QACA,SAAS;AAAA,UACP,cAAc,cAAc;AAAA,UAC5B,gBAAgB,MAAM,YAAY;AAAA,QACpC;AAAA,QACA,UAAU;AAAA,MACZ,IACA;AAAA,IACN;AAAA,EACF;AAAA,EAEA,MAAM,QAAQ,OAA6C;AACzD,UAAM,SAAS,oBAAoB,MAAM,aAAa,YAAY;AAClE,UAAM,gBAAgB,MAAM,SACxB,MAAM,OAAO,eAAe,SAAS,MAAM,SAAS,IACpD;AACJ,UAAM,WAAW,MAAM,OAAO,eAAe,
|
|
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(
|
|
54
|
-
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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:
|
|
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(
|
|
82
|
-
|
|
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
|
|
5
|
-
"mappings": "AAgBA,SAAS,2BAA2B;AACpC,SAAS,iBAAiB,iBAAiB,+BAA+B;AAC1E;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,2BAA2B;AAEpC,MAAM,qBAAqB;AAEpB,MAAM,yBAAyC;AAAA,EACpD,aAAa;AAAA,EAEb,MAAM,cAAc,OAAyD;AAC3E,UAAM,SAAS,oBAAoB,MAAM,aAAa,kBAAkB;AACxE,UAAM,cAAc,yBAAyB,MAAM,YAAY;AAC/D,UAAM,mBAAmB,sCAAsC,MAAM,cAAc,gBAAgB;AAEnG,UAAM,gBAAgB,MAAM,OAAO,eAAe,OAAO;AAAA,MACvD,QAAQ,QAAQ,MAAM,QAAQ,MAAM,YAAY;AAAA,MAChD,UAAU,MAAM,aAAa,YAAY;AAAA,MACzC,gBAAgB,MAAM,iBAAiB;AAAA,MACvC,UAAU,oBAAoB;AAAA,QAC5B,WAAW,MAAM;AAAA,QACjB,UAAU,MAAM;AAAA,QAChB,gBAAgB,MAAM;AAAA,QACtB,SAAS,MAAM;AAAA,MACjB,CAAC;AAAA,MACD,sBAAsB,MAAM,gBAAgB,CAAC,MAAM;AAAA,MACnD,aAAa,MAAM;AAAA,IACrB,GAAG,MAAM,iBAAiB,EAAE,gBAAgB,MAAM,eAAe,IAAI,MAAS;AAE9E,WAAO;AAAA,MACL,WAAW,cAAc;AAAA,MACzB,cAAc,cAAc,iBAAiB;AAAA,MAC7C,QAAQ,gBAAgB,cAAc,MAAM;AAAA,MAC5C,cAAc;AAAA,QACZ,iBAAiB,cAAc;AAAA,QAC/B,gBAAgB,OAAO,MAAM,YAAY,mBAAmB,WACxD,MAAM,YAAY,iBAClB;AAAA,MACN;AAAA,MACA,eAAe,cAAc,iBAAiB,OAAO,MAAM,YAAY,mBAAmB,WACtF;AAAA,QACE,MAAM;AAAA,QACN;AAAA,QACA,SAAS;AAAA,UACP,cAAc,cAAc;AAAA,UAC5B,gBAAgB,MAAM,YAAY;AAAA,QACpC;AAAA,QACA,UAAU;AAAA,MACZ,IACA;AAAA,IACN;AAAA,EACF;AAAA,EAEA,MAAM,QAAQ,OAA6C;AACzD,UAAM,SAAS,oBAAoB,MAAM,aAAa,kBAAkB;AACxE,UAAM,gBAAgB,MAAM,SACxB,MAAM,OAAO,eAAe,SAAS,MAAM,SAAS,IACpD;AAEJ,UAAM,WAAW,MAAM,OAAO,eAAe,
|
|
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(
|
|
54
|
-
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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:
|
|
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(
|
|
80
|
-
|
|
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
|
|
5
|
-
"mappings": "AAgBA,SAAS,2BAA2B;AACpC,SAAS,iBAAiB,iBAAiB,+BAA+B;AAC1E;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,2BAA2B;AAEpC,MAAM,qBAAqB;AAEpB,MAAM,+BAA+C;AAAA,EAC1D,aAAa;AAAA,EAEb,MAAM,cAAc,OAAyD;AAC3E,UAAM,SAAS,oBAAoB,MAAM,aAAa,kBAAkB;AACxE,UAAM,cAAc,yBAAyB,MAAM,YAAY;AAC/D,UAAM,mBAAmB,sCAAsC,MAAM,cAAc,gBAAgB;AAEnG,UAAM,gBAAgB,MAAM,OAAO,eAAe,OAAO;AAAA,MACvD,QAAQ,QAAQ,MAAM,QAAQ,MAAM,YAAY;AAAA,MAChD,UAAU,MAAM,aAAa,YAAY;AAAA,MACzC,gBAAgB,MAAM,iBAAiB;AAAA,MACvC,UAAU,oBAAoB;AAAA,QAC5B,WAAW,MAAM;AAAA,QACjB,UAAU,MAAM;AAAA,QAChB,gBAAgB,MAAM;AAAA,QACtB,SAAS,MAAM;AAAA,MACjB,CAAC;AAAA,MACD,sBAAsB,MAAM,gBAAgB,CAAC,MAAM;AAAA,MACnD,aAAa,MAAM;AAAA,IACrB,GAAG,MAAM,iBAAiB,EAAE,gBAAgB,MAAM,eAAe,IAAI,MAAS;AAE9E,WAAO;AAAA,MACL,WAAW,cAAc;AAAA,MACzB,cAAc,cAAc,iBAAiB;AAAA,MAC7C,QAAQ,gBAAgB,cAAc,MAAM;AAAA,MAC5C,cAAc;AAAA,QACZ,iBAAiB,cAAc;AAAA,QAC/B,gBAAgB,OAAO,MAAM,YAAY,mBAAmB,WACxD,MAAM,YAAY,iBAClB;AAAA,MACN;AAAA,MACA,eAAe,cAAc,iBAAiB,OAAO,MAAM,YAAY,mBAAmB,WACtF;AAAA,QACE,MAAM;AAAA,QACN;AAAA,QACA,SAAS;AAAA,UACP,cAAc,cAAc;AAAA,UAC5B,gBAAgB,MAAM,YAAY;AAAA,QACpC;AAAA,QACA,UAAU;AAAA,MACZ,IACA;AAAA,IACN;AAAA,EACF;AAAA,EAEA,MAAM,QAAQ,OAA6C;AACzD,UAAM,SAAS,oBAAoB,MAAM,aAAa,kBAAkB;AACxE,UAAM,gBAAgB,MAAM,SACxB,MAAM,OAAO,eAAe,SAAS,MAAM,SAAS,IACpD;AAEJ,UAAM,WAAW,MAAM,OAAO,eAAe,
|
|
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": "
|
|
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.
|
|
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.
|
|
66
|
-
"@open-mercato/queue": "0.6.7-develop.
|
|
67
|
-
"@open-mercato/ui": "0.6.7-develop.
|
|
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.
|
|
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.
|
|
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(
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
:
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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:
|
|
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(
|
|
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(
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
:
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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:
|
|
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(
|
|
125
|
-
|
|
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(
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
:
|
|
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
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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:
|
|
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(
|
|
121
|
-
|
|
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
|