feat: Add support for listing saved payment methods in module and Stripe (#10994)

This commit is contained in:
Stevche Radevski
2025-01-16 16:16:04 +01:00
committed by GitHub
parent 114b2133aa
commit f99f720dd4
8 changed files with 171 additions and 0 deletions

View File

@@ -23,4 +23,13 @@ export const joinerConfig = defineJoinerConfig(Modules.PAYMENT, {
payment_provider_id: PaymentProvider.name,
refund_reason_id: RefundReason.name,
},
alias: [
{
name: ["payment_method", "payment_methods"],
entity: "PaymentMethod",
args: {
methodSuffix: "PaymentMethods",
},
},
],
})

View File

@@ -1,5 +1,6 @@
import {
CreatePaymentProviderSession,
PaymentMethodResponse,
PaymentProviderError,
PaymentProviderSessionResponse,
ProviderWebhookPayload,
@@ -72,6 +73,10 @@ export class SystemProviderService extends AbstractPaymentProvider {
return {}
}
async listPaymentMethods(_): Promise<PaymentMethodResponse[]> {
return []
}
async getWebhookActionAndData(
data: ProviderWebhookPayload["payload"]
): Promise<WebhookActionResult> {

View File

@@ -8,6 +8,7 @@ import {
CreateRefundDTO,
DAL,
FilterablePaymentCollectionProps,
FilterablePaymentMethodProps,
FilterablePaymentProviderProps,
FilterablePaymentSessionProps,
FindConfig,
@@ -20,6 +21,7 @@ import {
PaymentCollectionDTO,
PaymentCollectionUpdatableFields,
PaymentDTO,
PaymentMethodDTO,
PaymentProviderDTO,
PaymentSessionDTO,
ProviderWebhookPayload,
@@ -906,6 +908,33 @@ export default class PaymentModuleService
]
}
@InjectManager()
async listPaymentMethods(
filters: FilterablePaymentMethodProps,
config: FindConfig<PaymentMethodDTO> = {},
@MedusaContext() sharedContext?: Context
): Promise<PaymentMethodDTO[]> {
return await this.paymentProviderService_.listPaymentMethods(
filters.provider_id,
filters.context
)
}
@InjectManager()
async listAndCountPaymentMethods(
filters: FilterablePaymentMethodProps,
config: FindConfig<PaymentMethodDTO> = {},
@MedusaContext() sharedContext?: Context
): Promise<[PaymentMethodDTO[], number]> {
const paymentMethods =
await this.paymentProviderService_.listPaymentMethods(
filters.provider_id,
filters.context
)
return [paymentMethods, paymentMethods.length]
}
@InjectManager()
private async maybeUpdatePaymentCollection_(
paymentCollectionId: string,

View File

@@ -4,7 +4,9 @@ import {
DAL,
IPaymentProvider,
Logger,
PaymentMethodResponse,
PaymentProviderAuthorizeResponse,
PaymentProviderContext,
PaymentProviderDataInput,
PaymentProviderError,
PaymentProviderSessionResponse,
@@ -150,6 +152,14 @@ Please make sure that the provider is registered in the container and it is conf
return res as Record<string, unknown>
}
async listPaymentMethods(
providerId: string,
context: PaymentProviderContext
): Promise<PaymentMethodResponse[]> {
const provider = this.retrieveProvider(providerId)
return await provider.listPaymentMethods(context)
}
async getWebhookActionAndData(
providerId: string,
data: ProviderWebhookPayload["payload"]

View File

@@ -2,6 +2,8 @@ import Stripe from "stripe"
import {
CreatePaymentProviderSession,
PaymentMethodResponse,
PaymentProviderContext,
PaymentProviderError,
PaymentProviderSessionResponse,
ProviderWebhookPayload,
@@ -86,6 +88,8 @@ abstract class StripeBase extends AbstractPaymentProvider<StripeOptions> {
res.payment_method = extra?.payment_method as string | undefined
res.return_url = extra?.return_url as string | undefined
return res
}
@@ -294,6 +298,27 @@ abstract class StripeBase extends AbstractPaymentProvider<StripeOptions> {
}
}
async listPaymentMethods(
context: PaymentProviderContext
): Promise<PaymentMethodResponse[]> {
const customerId = context.customer?.metadata?.stripe_id
if (!customerId) {
return []
}
const paymentMethods = await this.stripe_.customers.listPaymentMethods(
customerId as string,
// In order to keep the interface simple, we just list the maximum payment methods, which should be enough in almost all cases.
// We can always extend the interface to allow additional filtering, if necessary.
{ limit: 100 }
)
return paymentMethods.data.map((method) => ({
id: method.id,
data: method as unknown as Record<string, unknown>,
}))
}
async getWebhookActionAndData(
webhookData: ProviderWebhookPayload["payload"]
): Promise<WebhookActionResult> {