You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

29 KiB

支付 SDK 封装服务

文档版本:v3.1
最后更新:2026-01-29
服务类型:技术服务(SDK 封装)


目录

  1. 服务概述
  2. 支付方式
  3. 架构设计
  4. 服务实现
  5. 配置说明
  6. 使用示例
  7. 测试

服务概述

支付服务是一个纯技术服务,封装了微信支付和支付宝支付的 SDK 调用。它不管理任何业务数据,不暴露 API 接口,只被 recharge-service 内部调用。

服务定位

  • 技术服务:封装第三方支付 SDK
  • 无状态:不存储任何数据
  • 内部调用:只被 recharge-service 使用
  • 非业务服务:不包含业务逻辑
  • 无数据表:不管理数据库表
  • 无 API 路由:不对外暴露接口

职责

  • 调用微信支付 SDK 创建支付订单
  • 调用支付宝 SDK 创建支付订单
  • 验证微信支付回调签名
  • 验证支付宝回调签名

支付方式

1. 微信支付

支持的支付类型:

  • Native 扫码支付:生成二维码,用户扫码支付
  • H5 支付:移动端网页支付
  • 小程序支付:微信小程序内支付
  • JSAPI 支付:微信公众号内支付

2. 支付宝支付

支持的支付类型:

  • 扫码支付:生成二维码,用户扫码支付
  • H5 支付:移动端网页支付
  • APP 支付:移动应用内支付

架构设计

服务分层

┌─────────────────────────────────────────────┐
│ recharge-service (充值业务服务)              │
│ - 管理 recharge_orders 表                   │
│ - 管理 payment_callbacks 表                 │
│ - 处理充值业务逻辑                           │
└─────────────────────────────────────────────┘
                    ↓ 调用
┌─────────────────────────────────────────────┐
│ payment-service (支付 SDK 封装)             │
│ - WechatPayment 类                          │
│ - AlipayPayment 类                          │
│ - 纯技术服务,无业务逻辑                     │
└─────────────────────────────────────────────┘
                    ↓ 调用
┌─────────────────────────────────────────────┐
│ 第三方支付平台                               │
│ - 微信支付 API                              │
│ - 支付宝 API                                │
└─────────────────────────────────────────────┘

调用流程

recharge-service.create_order()
    ↓
payment-service.create_payment(order_no, amount, payment_method)
    ↓
WechatPayment.create_payment() / AlipayPayment.create_payment()
    ↓
返回支付凭证 (qrcode_url, h5_url 等)
    ↓
recharge-service 返回给前端

服务实现

PaymentService 类

# app/services/payment_service.py
import logging
from decimal import Decimal
from typing import TypedDict
from app.services.payment.wechat_payment import WechatPayment
from app.services.payment.alipay_payment import AlipayPayment
from app.core.exceptions import ValidationError, PaymentError

logger = logging.getLogger(__name__)


class PaymentResult(TypedDict):
    """支付结果类型定义"""
    qrcode_url: str
    expires_in: int


class PaymentService:
    """支付 SDK 封装服务(纯技术服务,无状态)"""
    
    def __init__(self):
        """初始化支付客户端"""
        self.wechat = WechatPayment()
        self.alipay = AlipayPayment()
    
    async def create_payment(
        self,
        order_no: str,
        amount: Decimal,
        payment_method: str,
        description: str
    ) -> PaymentResult:
        """创建支付订单
        
        Args:
            order_no: 订单号(由 recharge-service 生成)
            amount: 支付金额(元,使用 Decimal 保证精度)
            payment_method: 支付方式(wechat/alipay)
            description: 支付描述
            
        Returns:
            PaymentResult: 支付凭证,包含:
            - qrcode_url: 二维码 URL(扫码支付)
            - expires_in: 过期时间(秒)
            
        Raises:
            ValidationError: 支付方式不支持或参数错误
            PaymentError: 支付订单创建失败
        """
        logger.info(
            "创建支付订单: order_no=%s, amount=%s, method=%s",
            order_no, amount, payment_method
        )
        
        try:
            if payment_method == 'wechat':
                return await self.wechat.create_payment(order_no, amount, description)
            elif payment_method == 'alipay':
                return await self.alipay.create_payment(order_no, amount, description)
            else:
                raise ValidationError(f"不支持的支付方式: {payment_method}")
        except ValidationError:
            raise  # 直接向上抛出验证错误
        except Exception as e:
            logger.error(
                "创建支付订单失败: order_no=%s",
                order_no,
                exc_info=True
            )
            raise PaymentError(f"支付订单创建失败: {str(e)}") from e
    
    async def verify_callback(
        self,
        payment_method: str,
        callback_data: dict
    ) -> bool:
        """验证支付回调签名
        
        Args:
            payment_method: 支付方式(wechat/alipay)
            callback_data: 回调数据(包含签名)
            
        Returns:
            bool: 签名是否有效
        """
        logger.info(
            "验证支付回调: method=%s, order_no=%s",
            payment_method,
            callback_data.get('order_no')
        )
        
        try:
            if payment_method == 'wechat':
                return await self.wechat.verify_callback(callback_data)
            elif payment_method == 'alipay':
                return await self.alipay.verify_callback(callback_data)
            else:
                logger.warning("不支持的支付方式: method=%s", payment_method)
                return False
        except Exception as e:
            logger.error(
                "验证回调签名失败: method=%s",
                payment_method,
                exc_info=True
            )
            return False

WechatPayment 类

# app/services/payment/wechat_payment.py
import logging
from decimal import Decimal
from typing import TypedDict
from wechatpayv3 import WeChatPay, WeChatPayType
from app.core.config import settings
from app.core.exceptions import PaymentError

logger = logging.getLogger(__name__)


class PaymentResult(TypedDict):
    """支付结果类型定义"""
    qrcode_url: str
    expires_in: int


class WechatPayment:
    """微信支付 SDK 封装"""
    
    def __init__(self):
        """初始化微信支付客户端"""
        self.wxpay = WeChatPay(
            wechatpay_type=WeChatPayType.NATIVE,
            mchid=settings.WECHAT_PAY_MCHID,
            private_key=settings.WECHAT_PAY_PRIVATE_KEY,
            cert_serial_no=settings.WECHAT_PAY_CERT_SERIAL_NO,
            apiv3_key=settings.WECHAT_PAY_APIV3_KEY,
            appid=settings.WECHAT_APPID
        )
    
    async def create_payment(
        self,
        order_no: str,
        amount: Decimal,
        description: str
    ) -> PaymentResult:
        """创建微信支付订单
        
        Args:
            order_no: 订单号
            amount: 支付金额(元,Decimal 类型)
            description: 支付描述
            
        Returns:
            PaymentResult: {
                'qrcode_url': 'weixin://wxpay/bizpayurl?pr=xxx',
                'expires_in': 1800
            }
            
        Raises:
            PaymentError: 微信支付订单创建失败
        """
        logger.info(
            "调用微信支付 API: order_no=%s, amount=%s",
            order_no, amount
        )
        
        # 金额转换为分(整数)
        amount_fen = int(amount * 100)
        
        code, message = self.wxpay.pay(
            description=description,
            out_trade_no=order_no,
            amount={'total': amount_fen},
            notify_url=settings.WECHAT_PAY_NOTIFY_URL
        )
        
        if code == 200:
            logger.info("微信支付订单创建成功: order_no=%s", order_no)
            return {
                'qrcode_url': message.get('code_url'),
                'expires_in': 1800  # 30 分钟
            }
        else:
            logger.error(
                "微信支付订单创建失败: order_no=%s, code=%s, message=%s",
                order_no, code, message
            )
            raise PaymentError(f"微信支付创建失败:{message}")
    
    async def verify_callback(self, callback_data: dict) -> bool:
        """验证微信支付回调签名
        
        Args:
            callback_data: 回调数据,包含:
                - headers: 请求头(包含签名)
                - body: 请求体
                
        Returns:
            bool: 签名是否有效
        """
        try:
            result = self.wxpay.callback(
                headers=callback_data.get('headers'),
                body=callback_data.get('body')
            )
            logger.info(
                "微信支付回调验证成功: order_no=%s",
                callback_data.get('order_no')
            )
            return result is not None
        except Exception as e:
            logger.error(
                "微信支付回调验证失败: order_no=%s",
                callback_data.get('order_no'),
                exc_info=True
            )
            return False

AlipayPayment 类

# app/services/payment/alipay_payment.py
import logging
from decimal import Decimal
from typing import TypedDict
from alipay import AliPay
from app.core.config import settings
from app.core.exceptions import PaymentError

logger = logging.getLogger(__name__)


class PaymentResult(TypedDict):
    """支付结果类型定义"""
    qrcode_url: str
    expires_in: int


class AlipayPayment:
    """支付宝 SDK 封装"""
    
    def __init__(self):
        """初始化支付宝客户端"""
        self.alipay = AliPay(
            appid=settings.ALIPAY_APPID,
            app_notify_url=settings.ALIPAY_NOTIFY_URL,
            app_private_key_string=settings.ALIPAY_PRIVATE_KEY,
            alipay_public_key_string=settings.ALIPAY_PUBLIC_KEY,
            sign_type="RSA2"
        )
    
    async def create_payment(
        self,
        order_no: str,
        amount: Decimal,
        description: str
    ) -> PaymentResult:
        """创建支付宝支付订单
        
        Args:
            order_no: 订单号
            amount: 支付金额(元,Decimal 类型)
            description: 支付描述
            
        Returns:
            PaymentResult: {
                'qrcode_url': 'https://qr.alipay.com/xxx',
                'expires_in': 1800
            }
            
        Raises:
            PaymentError: 支付宝订单创建失败
        """
        logger.info(
            "调用支付宝 API: order_no=%s, amount=%s",
            order_no, amount
        )
        
        order_string = self.alipay.api_alipay_trade_precreate(
            subject=description,
            out_trade_no=order_no,
            total_amount=str(amount)
        )
        
        if order_string.get('code') == '10000':
            logger.info("支付宝订单创建成功: order_no=%s", order_no)
            return {
                'qrcode_url': order_string.get('qr_code'),
                'expires_in': 1800  # 30 分钟
            }
        else:
            logger.error(
                "支付宝订单创建失败: order_no=%s, code=%s, msg=%s",
                order_no,
                order_string.get('code'),
                order_string.get('msg')
            )
            raise PaymentError(f"支付宝创建失败:{order_string.get('msg')}")
    
    async def verify_callback(self, callback_data: dict) -> bool:
        """验证支付宝回调签名
        
        Args:
            callback_data: 回调数据(表单参数)
                
        Returns:
            bool: 签名是否有效
        """
        try:
            # 提取签名
            data = callback_data.copy()
            signature = data.pop('sign', None)
            sign_type = data.pop('sign_type', None)
            
            if not signature:
                logger.warning("支付宝回调缺少签名")
                return False
            
            # 验证签名
            is_valid = self.alipay.verify(data, signature)
            
            if is_valid:
                logger.info(
                    "支付宝回调验证成功: order_no=%s",
                    data.get('out_trade_no')
                )
            else:
                logger.warning(
                    "支付宝回调验证失败: order_no=%s",
                    data.get('out_trade_no')
                )
            
            return is_valid
        except Exception as e:
            logger.error(
                "支付宝回调验证异常: order_no=%s",
                callback_data.get('out_trade_no'),
                exc_info=True
            )
            return False

配置说明

环境变量配置

# app/core/config.py
from pydantic_settings import BaseSettings
from pydantic import Field


class Settings(BaseSettings):
    """应用配置(支付相关)"""
    
    # 微信支付配置
    WECHAT_PAY_MCHID: str = Field(..., description="微信支付商户号")
    WECHAT_PAY_PRIVATE_KEY: str = Field(..., description="微信支付私钥(PEM 格式)")
    WECHAT_PAY_CERT_SERIAL_NO: str = Field(..., description="微信支付证书序列号")
    WECHAT_PAY_APIV3_KEY: str = Field(..., description="微信支付 APIv3 密钥")
    WECHAT_APPID: str = Field(..., description="微信公众号/小程序 AppID")
    WECHAT_PAY_NOTIFY_URL: str = Field(..., description="微信支付回调 URL")
    
    # 支付宝配置
    ALIPAY_APPID: str = Field(..., description="支付宝应用 ID")
    ALIPAY_PRIVATE_KEY: str = Field(..., description="支付宝应用私钥(PEM 格式)")
    ALIPAY_PUBLIC_KEY: str = Field(..., description="支付宝公钥(PEM 格式)")
    ALIPAY_NOTIFY_URL: str = Field(..., description="支付宝回调 URL")
    
    model_config = {
        "env_file": ".env",
        "extra": "forbid"  # 禁止额外字段
    }


settings = Settings()

.env 文件示例

# 微信支付
WECHAT_PAY_MCHID=1234567890
WECHAT_PAY_PRIVATE_KEY=-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC...\n-----END PRIVATE KEY-----
WECHAT_PAY_CERT_SERIAL_NO=ABC123DEF456
WECHAT_PAY_APIV3_KEY=your_apiv3_key_32_characters
WECHAT_APPID=wx1234567890abcdef
WECHAT_PAY_NOTIFY_URL=https://api.jointo.ai/api/v1/recharge/callbacks/wechat

# 支付宝
ALIPAY_APPID=2021001234567890
ALIPAY_PRIVATE_KEY=-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA...\n-----END RSA PRIVATE KEY-----
ALIPAY_PUBLIC_KEY=-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...\n-----END PUBLIC KEY-----
ALIPAY_NOTIFY_URL=https://api.jointo.ai/api/v1/recharge/callbacks/alipay

依赖安装

# 微信支付 SDK
pip install wechatpayv3==1.2.6

# 支付宝 SDK
pip install python-alipay-sdk==3.7.0

# 注意:日志使用 Python 标准库 logging,无需额外安装

使用示例

在 recharge-service 中使用

# app/services/recharge_service.py
from decimal import Decimal
from fastapi import Depends
from sqlalchemy.ext.asyncio import AsyncSession
from app.services.payment_service import PaymentService
from app.repositories.recharge_repository import RechargeRepository
from app.services.credit_service import CreditService


class RechargeService:
    """充值服务(业务逻辑层)"""
    
    def __init__(
        self,
        session: AsyncSession,
        repository: RechargeRepository,
        credit_service: CreditService,
        payment_service: PaymentService = Depends()  # 依赖注入
    ):
        self.session = session
        self.repository = repository
        self.credit_service = credit_service
        self.payment_service = payment_service
    
    async def create_order(
        self,
        user_id: str,
        package_id: str,
        payment_method: str
    ) -> dict:
        """创建充值订单
        
        Args:
            user_id: 用户 ID
            package_id: 充值套餐 ID
            payment_method: 支付方式(wechat/alipay)
            
        Returns:
            订单信息和支付凭证
        """
        # 1. 创建订单记录
        order = await self.repository.create(
            user_id=user_id,
            package_id=package_id,
            payment_method=payment_method
        )
        
        # 2. 调用 payment-service 创建支付
        payment_params = await self.payment_service.create_payment(
            order_no=order.order_no,
            amount=Decimal(str(order.amount)),  # 转换为 Decimal
            payment_method=payment_method,
            description=f"充值 {order.credits} 积分"
        )
        
        # 3. 返回订单和支付凭证
        return {
            'order': {
                'order_no': order.order_no,
                'amount': float(order.amount),
                'credits': order.credits,
                'status': order.status
            },
            'paymentParams': payment_params
        }
    
    async def handle_payment_callback(
        self,
        payment_method: str,
        callback_data: dict
    ) -> dict:
        """处理支付回调
        
        Args:
            payment_method: 支付方式(wechat/alipay)
            callback_data: 回调数据
            
        Returns:
            处理结果
        """
        # 1. 验证签名
        is_valid = await self.payment_service.verify_callback(
            payment_method=payment_method,
            callback_data=callback_data
        )
        
        if not is_valid:
            return {'success': False, 'message': '签名验证失败'}
        
        # 2. 更新订单状态
        order_no = callback_data.get('order_no')
        await self.repository.update(order_no, {'status': 'paid'})
        
        # 3. 增加积分
        order = await self.repository.get_by_order_no(order_no)
        await self.credit_service.add_credits(
            user_id=order.user_id,
            amount=order.credits,
            reason=f"充值订单 {order_no}"
        )
        
        return {'success': True, 'message': '充值成功'}

API 路由中的依赖注入

# app/api/v1/recharge.py
from fastapi import APIRouter, Depends
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.database import get_session
from app.services.recharge_service import RechargeService
from app.services.payment_service import PaymentService
from app.repositories.recharge_repository import RechargeRepository
from app.services.credit_service import CreditService


router = APIRouter(prefix="/recharge", tags=["充值"])


def get_recharge_service(
    session: AsyncSession = Depends(get_session),
    payment_service: PaymentService = Depends()
) -> RechargeService:
    """获取充值服务实例(依赖注入)"""
    repository = RechargeRepository(session)
    credit_service = CreditService(session)
    return RechargeService(
        session=session,
        repository=repository,
        credit_service=credit_service,
        payment_service=payment_service
    )


@router.post("/orders")
async def create_recharge_order(
    user_id: str,
    package_id: str,
    payment_method: str,
    service: RechargeService = Depends(get_recharge_service)
):
    """创建充值订单"""
    return await service.create_order(user_id, package_id, payment_method)


@router.post("/callbacks/{payment_method}")
async def handle_payment_callback(
    payment_method: str,
    callback_data: dict,
    service: RechargeService = Depends(get_recharge_service)
):
    """处理支付回调"""
    return await service.handle_payment_callback(payment_method, callback_data)

测试

单元测试

测试支付服务的核心逻辑,mock 第三方 SDK:

# tests/unit/test_payment_service.py
import pytest
from decimal import Decimal
from unittest.mock import AsyncMock, patch, MagicMock
from app.services.payment_service import PaymentService
from app.core.exceptions import ValidationError, PaymentError


@pytest.fixture
def payment_service():
    """创建支付服务实例"""
    return PaymentService()


@pytest.mark.asyncio
async def test_create_wechat_payment_success(payment_service):
    """测试创建微信支付订单成功"""
    with patch.object(payment_service.wechat, 'create_payment') as mock_create:
        mock_create.return_value = {
            'qrcode_url': 'weixin://wxpay/bizpayurl?pr=test123',
            'expires_in': 1800
        }
        
        result = await payment_service.create_payment(
            order_no='TEST001',
            amount=Decimal('100.00'),
            payment_method='wechat',
            description='测试订单'
        )
        
        assert result['qrcode_url'].startswith('weixin://')
        assert result['expires_in'] == 1800
        mock_create.assert_called_once_with(
            'TEST001',
            Decimal('100.00'),
            '测试订单'
        )


@pytest.mark.asyncio
async def test_create_alipay_payment_success(payment_service):
    """测试创建支付宝订单成功"""
    with patch.object(payment_service.alipay, 'create_payment') as mock_create:
        mock_create.return_value = {
            'qrcode_url': 'https://qr.alipay.com/test123',
            'expires_in': 1800
        }
        
        result = await payment_service.create_payment(
            order_no='TEST002',
            amount=Decimal('200.00'),
            payment_method='alipay',
            description='测试订单'
        )
        
        assert result['qrcode_url'].startswith('https://qr.alipay.com/')
        assert result['expires_in'] == 1800


@pytest.mark.asyncio
async def test_create_payment_invalid_method(payment_service):
    """测试不支持的支付方式"""
    with pytest.raises(ValidationError) as exc_info:
        await payment_service.create_payment(
            order_no='TEST003',
            amount=Decimal('100.00'),
            payment_method='invalid',
            description='测试订单'
        )
    
    assert '不支持的支付方式' in str(exc_info.value)


@pytest.mark.asyncio
async def test_create_payment_sdk_error(payment_service):
    """测试 SDK 调用失败"""
    with patch.object(payment_service.wechat, 'create_payment') as mock_create:
        mock_create.side_effect = Exception('网络错误')
        
        with pytest.raises(PaymentError) as exc_info:
            await payment_service.create_payment(
                order_no='TEST004',
                amount=Decimal('100.00'),
                payment_method='wechat',
                description='测试订单'
            )
        
        assert '支付订单创建失败' in str(exc_info.value)


@pytest.mark.asyncio
async def test_verify_wechat_callback_success(payment_service):
    """测试验证微信回调成功"""
    callback_data = {
        'headers': {'Wechatpay-Signature': 'test_signature'},
        'body': '{"order_no": "TEST001"}',
        'order_no': 'TEST001'
    }
    
    with patch.object(payment_service.wechat, 'verify_callback') as mock_verify:
        mock_verify.return_value = True
        
        result = await payment_service.verify_callback('wechat', callback_data)
        
        assert result is True
        mock_verify.assert_called_once_with(callback_data)


@pytest.mark.asyncio
async def test_verify_alipay_callback_success(payment_service):
    """测试验证支付宝回调成功"""
    callback_data = {
        'out_trade_no': 'TEST002',
        'trade_status': 'TRADE_SUCCESS',
        'sign': 'test_signature',
        'sign_type': 'RSA2'
    }
    
    with patch.object(payment_service.alipay, 'verify_callback') as mock_verify:
        mock_verify.return_value = True
        
        result = await payment_service.verify_callback('alipay', callback_data)
        
        assert result is True


@pytest.mark.asyncio
async def test_verify_callback_invalid_method(payment_service):
    """测试验证不支持的支付方式回调"""
    result = await payment_service.verify_callback('invalid', {})
    assert result is False

集成测试

使用沙箱环境测试真实的支付流程:

# tests/integration/test_payment_integration.py
import pytest
from decimal import Decimal
from app.services.payment_service import PaymentService
from app.core.config import settings


@pytest.mark.integration
@pytest.mark.asyncio
async def test_wechat_payment_sandbox():
    """测试微信支付沙箱环境(需要配置沙箱密钥)"""
    if not settings.WECHAT_PAY_SANDBOX_MODE:
        pytest.skip("未启用微信支付沙箱模式")
    
    service = PaymentService()
    
    result = await service.create_payment(
        order_no='SANDBOX_TEST_001',
        amount=Decimal('0.01'),  # 沙箱环境使用小额测试
        payment_method='wechat',
        description='沙箱测试订单'
    )
    
    assert 'qrcode_url' in result
    assert result['expires_in'] > 0


@pytest.mark.integration
@pytest.mark.asyncio
async def test_alipay_payment_sandbox():
    """测试支付宝沙箱环境(需要配置沙箱密钥)"""
    if not settings.ALIPAY_SANDBOX_MODE:
        pytest.skip("未启用支付宝沙箱模式")
    
    service = PaymentService()
    
    result = await service.create_payment(
        order_no='SANDBOX_TEST_002',
        amount=Decimal('0.01'),
        payment_method='alipay',
        description='沙箱测试订单'
    )
    
    assert 'qrcode_url' in result
    assert result['expires_in'] > 0

运行测试

# 运行所有单元测试
docker exec jointo-server-app pytest tests/unit/test_payment_service.py -v

# 运行集成测试(需要配置沙箱环境)
docker exec jointo-server-app pytest tests/integration/test_payment_integration.py -v --integration

# 查看测试覆盖率
docker exec jointo-server-app pytest tests/unit/test_payment_service.py --cov=app.services.payment_service --cov-report=html

关键设计原则

1. 单一职责

  • payment-service 只负责与第三方支付平台交互
  • 不处理业务逻辑(订单管理、积分增加等)
  • 不存储任何数据

2. 无状态设计

  • 不依赖数据库(无 AsyncSession)
  • 不管理数据表
  • 每次调用都是独立的

3. 内部服务

  • 不暴露 API 路由
  • 只被 recharge-service 调用
  • 不直接面向用户

4. 错误处理

  • 所有异常向上抛出
  • 由调用方(recharge-service)处理
  • 记录详细日志便于排查

相关文档


文档版本:v3.1
最后更新:2026-01-29

变更记录

v3.1 (2026-01-29)

  • 技术栈合规性修复:符合 Jointo 技术栈规范
  • 日志系统:从 loguru 改为标准库 logging
  • 依赖版本:固定版本号(wechatpayv3==1.2.6, python-alipay-sdk==3.7.0)
  • 配置类:添加 Pydantic Field 验证和 extra="forbid"
  • 异常处理:使用项目自定义异常(PaymentError, ValidationError)
  • 类型注解:金额使用 Decimal,定义 PaymentResult TypedDict
  • 依赖注入:补充 FastAPI Depends 使用说明和示例
  • 测试文档:添加完整的单元测试和集成测试示例

v3.0 (2026-01-26)

  • 重构为纯技术服务:移除所有业务逻辑
  • 移除数据表:删除 payments 表定义
  • 移除 Repository 层:不再管理数据
  • 移除 API 路由:不对外暴露接口
  • 移除 Schema 定义:简化为字典返回
  • 简化依赖:不依赖 AsyncSession
  • 明确定位:SDK 封装服务,只被 recharge-service 调用
  • 完善文档:添加架构图、使用示例、设计原则

v2.0 (2026-01-26)

  • 规范化为符合 Jointo 技术栈的实现(已废弃)

v1.0 (2025-01-14)

  • 初始版本(已废弃)