You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多支付方式安全选型的设计方案探讨

支付方式动态适配与网关匹配方案

背景需求

我们需要动态支持多种支付方式(信用卡、PayPal、Google Pay等),且可切换某一支付方式的实现网关。比如PayPal支付当前用Stripe SDK实现,未来可切换为PayPal SDK。

初始策略模式思路

原本打算用策略模式,通过不同网关SDK实现对应支付服务,示例代码如下:

class PayPalModel : IPaymentModel

class PayPalGateway(val someSdk: SDK) : IPaymentService<PayPalModel> {
     override suspend fun authorizeFunds(model: PayPalModel) {
        // someSdk 可以是 Stripe SDK、PayPal SDK 等
     }
}

调用时注入服务列表,通过PaymentAPI统一处理:

val gateways =
        listOf(
            CreditGateway(),
            PayPalGateway()
        )
 val api = PaymentAPI(gateways)
 api.authorizeFunds(PayPalModel(...))

PaymentAPI内部通过模型匹配对应的服务:

private suspend fun findService(model: IPaymentModel): IPaymentService<IPaymentModel>? {
    for (service in paymentServices) {
        if (service.appliesTo(model)) {
            return service as IPaymentService<IPaymentModel>
        }
    }
    return null
}

实际场景的矛盾

但实际客户端下单时,仅向服务器传递两个字段:

  • 选中的支付方式类型
  • 从Stripe/Square等SDK获取的授权ID/令牌

此时如果用统一的PaymentRequest代替各支付模型,会丢失类型安全,导致无法通过模型匹配服务:

class PayPalGateway : IPaymentService {
    override suspend fun authorizeFunds(model: PaymentRequest) {
      // 无法通过模型类型区分服务
    }
}
class CreditGateway : IPaymentService<PaymentRequest> {
    override suspend fun authorizeFunds(model: PaymentRequest) {
      // 同样无法区分
    }
}

原有的findService函数会失效,因为没有了模型类型的区分度。

解决方案

1. 给支付服务绑定支付方式类型标识

给每个支付服务添加支付方式类型属性,同时让客户端请求携带该类型,直接通过类型匹配服务:

首先定义支付方式枚举:

enum class PaymentMethodType {
    CREDIT_CARD, PAYPAL, GOOGLE_PAY
}

修改支付服务接口,添加类型标识:

interface IPaymentService {
    val methodType: PaymentMethodType
    suspend fun authorizeFunds(token: String)
}

实现具体网关:

class PayPalStripeGateway(val stripeSdk: StripeSDK) : IPaymentService {
    override val methodType = PaymentMethodType.PAYPAL
    override suspend fun authorizeFunds(token: String) {
        stripeSdk.chargePayPalToken(token)
    }
}

class PayPalNativeGateway(val paypalSdk: PayPalSDK) : IPaymentService {
    override val methodType = PaymentMethodType.PAYPAL
    override suspend fun authorizeFunds(token: String) {
        paypalSdk.authorizePayment(token)
    }
}

class CreditCardGateway(val stripeSdk: StripeSDK) : IPaymentService {
    override val methodType = PaymentMethodType.CREDIT_CARD
    override suspend fun authorizeFunds(token: String) {
        stripeSdk.chargeCreditCardToken(token)
    }
}

修改PaymentAPI的匹配逻辑:

class PaymentAPI(private val paymentServices: List<IPaymentService>) {
    suspend fun authorizeFunds(methodType: PaymentMethodType, token: String) {
        val service = paymentServices.firstOrNull { it.methodType == methodType }
            ?: throw IllegalArgumentException("Unsupported payment method: $methodType")
        service.authorizeFunds(token)
    }
}

2. 结合依赖注入实现网关切换

如果需要切换同一支付方式的网关(比如PayPal从Stripe切换到Native SDK),只需在注入服务列表时替换对应的实现即可,无需修改业务逻辑:

// 用Stripe实现PayPal
val gateways = listOf(
    CreditCardGateway(stripeSdk),
    PayPalStripeGateway(stripeSdk)
)

// 切换为Native PayPal SDK
val gateways = listOf(
    CreditCardGateway(stripeSdk),
    PayPalNativeGateway(paypalSdk)
)

3. 保留类型安全的进阶方案(可选)

如果仍想保留模型的类型区分,可以将PaymentRequest设计为带类型标记的密封类:

sealed class PaymentRequest(val methodType: PaymentMethodType) {
    data class CreditCardRequest(val token: String) : PaymentRequest(PaymentMethodType.CREDIT_CARD)
    data class PayPalRequest(val token: String) : PaymentRequest(PaymentMethodType.PAYPAL)
    data class GooglePayRequest(val token: String) : PaymentRequest(PaymentMethodType.GOOGLE_PAY)
}

修改服务接口:

interface IPaymentService<T : PaymentRequest> {
    val methodType: PaymentMethodType
    suspend fun authorizeFunds(request: T)
}

实现服务时绑定具体请求类型:

class PayPalGateway(val sdk: SDK) : IPaymentService<PaymentRequest.PayPalRequest> {
    override val methodType = PaymentMethodType.PAYPAL
    override suspend fun authorizeFunds(request: PaymentRequest.PayPalRequest) {
        sdk.processToken(request.token)
    }
}

PaymentAPI的匹配逻辑可以同时用类型和methodType双重校验:

class PaymentAPI(private val paymentServices: List<IPaymentService<*>>) {
    @Suppress("UNCHECKED_CAST")
    suspend fun authorizeFunds(request: PaymentRequest) {
        val service = paymentServices.firstOrNull { 
            it.methodType == request.methodType && 
            it.javaClass.genericInterfaces[0].let { iface ->
                (iface as ParameterizedType).actualTypeArguments[0] == request.javaClass
            }
        } as? IPaymentService<PaymentRequest>
            ?: throw IllegalArgumentException("Unsupported payment request: ${request.methodType}")
        service.authorizeFunds(request)
    }
}

这种方式既保留了类型安全,又能通过客户端传递的支付方式类型(映射到密封类)匹配服务。


内容的提问来源于stack exchange,提问作者BVtp

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 13:41:38