Skip to main content

统一支付基础设施 SDK,支持微信支付和支付宝

Project description

pay-kit

PyPI version Python versions License CI

统一支付基础设施 SDK,支持微信支付和支付宝商户。一个 SDK 即可处理支付、转账、余额查询。

特性

  • 双平台支持 — 微信支付 V3 + 支付宝 V3
  • 多种支付方式 — 扫码、JSAPI、H5、App、电脑网站
  • 统一接口 — PayProvider / WithdrawProvider / WalletProvider 三个 ABC
  • 类型安全 — 全面类型注解,mypy strict 通过
  • 纯异步 — 基于 httpx,async/await 原生支持
  • 安全 — RSA-SHA256 签名/验签、AES-GCM 回调解密、SecretStr 敏感字段保护

安装

pip install pay-kit

快速开始

微信支付 — Native 扫码支付

from pay_kit.providers.wechat import WechatPay, WechatPayConfig
from pay_kit.models import PaymentRequest
from pay_kit.types import Fen

config = WechatPayConfig(
    appid="wx_your_appid",
    mch_id="your_mch_id",
    private_key=open("apiclient_key.pem").read(),      # 商户 API 私钥
    cert_serial_no="YOUR_CERT_SERIAL_NO",               # 商户证书序列号
    wechat_public_key=open("wechat_public_key.pem").read(),  # 微信支付公钥
    wechat_public_key_id="YOUR_PUB_KEY_ID",
    apiv3_key="your_apiv3_key",
    notify_url="https://your-domain.com/callback/wechat",
)

async with WechatPay(config) as pay:
    # 创建支付订单
    result = await pay.create_payment(
        PaymentRequest(out_trade_no="ORDER_001", total_amount=Fen(100), subject="商品名称")
    )
    print(result.credential["code_url"])  # 用此 URL 生成二维码

支付宝 — 当面付扫码支付

from pay_kit.providers.alipay import Alipay, AlipayConfig
from pay_kit.models import PaymentRequest
from pay_kit.types import Fen

config = AlipayConfig(
    app_id="your_app_id",
    private_key=open("app_private_key.pem").read(),     # 应用私钥
    alipay_public_key=open("alipay_public_key.pem").read(),  # 支付宝公钥
    notify_url="https://your-domain.com/callback/alipay",
)

async with Alipay(config) as pay:
    result = await pay.create_payment(
        PaymentRequest(out_trade_no="ORDER_001", total_amount=Fen(100), subject="商品名称")
    )
    print(result.credential["qr_code"])  # 二维码 URL

支付方式

微信支付

async with WechatPay(config) as pay:
    # Native 扫码支付(通用接口)
    result = await pay.create_payment(request)
    # credential: {"code_url": "weixin://wxpay/..."}

    # JSAPI 公众号/小程序支付
    result = await pay.create_jsapi_payment(request, openid="user_openid")
    # credential: {"appId", "timeStamp", "nonceStr", "package", "signType", "paySign"}

    # H5 手机浏览器支付
    result = await pay.create_h5_payment(request, payer_client_ip="1.2.3.4")
    # credential: {"h5_url": "https://wx.tenpay.com/..."}

    # App 支付
    result = await pay.create_app_payment(request)
    # credential: {"appId", "partnerId", "prepayId", "package", "nonceStr", "timeStamp", "sign"}

支付宝

async with Alipay(config) as pay:
    # 当面付扫码支付(通用接口)
    result = await pay.create_payment(request)
    # credential: {"qr_code": "https://qr.alipay.com/..."}

    # 电脑网站支付
    result = await pay.create_page_payment(request, return_url="https://your-site.com/return")
    # credential: {"pay_url": "https://openapi.alipay.com/..."}

    # 手机网站支付
    result = await pay.create_wap_payment(request, return_url="https://your-site.com/return")
    # credential: {"pay_url": "https://openapi.alipay.com/..."}

    # App 支付
    result = await pay.create_app_payment(request)
    # credential: {"order_str": "alipay_sdk=..."}

通用操作

查询、关单、退款等操作两个平台用法一致:

# 查询订单
result = await pay.query_payment("ORDER_001")
print(result.status)  # PaymentStatus.PAID

# 关闭订单
await pay.close_payment("ORDER_001")

# 发起退款
from pay_kit.models import RefundRequest
refund = await pay.refund(
    RefundRequest(
        out_trade_no="ORDER_001",
        out_refund_no="REFUND_001",
        total_amount=Fen(100),
        refund_amount=Fen(50),
    )
)

# 查询退款
refund = await pay.query_refund("REFUND_001")

转账

from pay_kit.models import WithdrawRequest

async with WechatPay(config) as pay:
    # 商家转账到零钱
    result = await pay.create_withdrawal(
        WithdrawRequest(
            out_withdrawal_no="TRANSFER_001",
            amount=Fen(1000),
            recipient="user_openid",         # 微信: openid / 支付宝: user_id
            description="佣金提现",
        )
    )
    print(result.status)  # WithdrawStatus.PENDING

    # 查询转账状态
    result = await pay.query_withdrawal("TRANSFER_001")

余额查询

async with WechatPay(config) as pay:
    balance = await pay.query_balance()
    print(f"可用余额: {balance.available} 分")
    print(f"待结算: {balance.pending} 分")

回调验签

# FastAPI 示例
from fastapi import Request

@app.post("/callback/wechat")
async def wechat_callback(request: Request):
    headers = dict(request.headers)
    body = await request.body()

    async with WechatPay(config) as pay:
        event = await pay.verify_callback(headers, body)

    print(event.event_type)     # "TRANSACTION.SUCCESS"
    print(event.out_trade_no)   # "ORDER_001"
    print(event.trade_no)       # 微信交易号
    print(event.raw)            # 完整回调数据

    return {"code": "SUCCESS"}

异常处理

from pay_kit.exceptions import (
    PayKitError,       # 所有异常基类
    ConfigError,       # 配置错误
    NetworkError,      # 网络超时/连接失败
    SignatureError,    # 签名验证失败
    PaymentError,      # 支付业务错误
    WithdrawError,     # 转账业务错误
    WalletError,       # 余额查询错误
)

try:
    result = await pay.create_payment(request)
except NetworkError:
    # 网络问题,可重试
    pass
except SignatureError:
    # 签名验证失败,响应不可信
    pass
except PaymentError as e:
    # 业务错误
    print(e.error_code)      # 支付商错误码,如 "ORDER_CLOSED"
    print(e.provider_name)   # "wechat" 或 "alipay"

金额类型

pay-kit 使用 Fen(分)作为金额单位,避免浮点精度问题:

from pay_kit.types import Fen, Yuan, fen_to_yuan, yuan_to_fen

amount = Fen(100)                    # 100 分 = 1 元
yuan = fen_to_yuan(Fen(100))         # Yuan("1.00")
fen = yuan_to_fen(Yuan("1.00"))      # Fen(100)

完整示例

examples/ 目录包含可运行的完整代码:

文件 场景
wechat_all_methods.py 微信全场景(4 种支付 + 查询/关单/退款/转账/余额)
alipay_all_methods.py 支付宝全场景(4 种支付 + 查询/关单/退款/转账/余额)
fastapi_server.py FastAPI 生产级集成(多 Provider + 回调 + 异常处理)
multi_provider.py 多支付商统一接口(PayProvider ABC 多态)
uv sync --group examples
cp examples/.env.example examples/.env
# 编辑 .env 填入商户配置
uv run python examples/wechat_all_methods.py

详见 examples/README.md

开发

git clone https://github.com/Xinzz995/pay-kit.git
cd pay-kit
uv sync

uv run pytest                        # 运行测试
uv run pytest --cov=pay_kit          # 覆盖率
uv run ruff check .                  # Lint
uv run mypy src/                     # 类型检查

许可证

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pay_py_kit-0.3.2.tar.gz (98.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pay_py_kit-0.3.2-py3-none-any.whl (23.6 kB view details)

Uploaded Python 3

File details

Details for the file pay_py_kit-0.3.2.tar.gz.

File metadata

  • Download URL: pay_py_kit-0.3.2.tar.gz
  • Upload date:
  • Size: 98.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.6 {"installer":{"name":"uv","version":"0.11.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for pay_py_kit-0.3.2.tar.gz
Algorithm Hash digest
SHA256 1aa37bf70eff055b5fd2920c9fe3cca1abc28c7c072fe08ff4be66cac69eec3e
MD5 80e3872d4c0d1dda615f6edf9c4d23a7
BLAKE2b-256 79c13c77683bff3a3c214ce80f63bd17992f703b4ce537a60674280d58c7e077

See more details on using hashes here.

File details

Details for the file pay_py_kit-0.3.2-py3-none-any.whl.

File metadata

  • Download URL: pay_py_kit-0.3.2-py3-none-any.whl
  • Upload date:
  • Size: 23.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.6 {"installer":{"name":"uv","version":"0.11.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for pay_py_kit-0.3.2-py3-none-any.whl
Algorithm Hash digest
SHA256 68d35fa3fb9aa608b297a50805a47242ed09eff17891d17d546dbb063d3a6c89
MD5 4012a1f3087d031340330618a3dcc1cc
BLAKE2b-256 c737128bd35c90a21c70f7f47762a8b11994feb1f5eca5b968d621b106cba415

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page