A production-oriented Python SDK for common EPay-style payment gateways
Project description
epay-sdk
一个面向 Python 的易支付 SDK,重点放在“可上线的支付链路”而不只是拼出下单参数。
当前已提供:
- MD5 签名与验签
- 创建订单 URL / 原始下单请求
- 查询订单与结果解析
- 回调验签
- 回调后二次查单确认
- 原子幂等仓储协议
- 默认 SQLAlchemy 仓储(可选安装)
- Decimal 金额标准化
- 作为库时不主动接管业务日志输出
安装
核心功能:
pip install epay-sdk
如需默认 SQLAlchemy 仓储与 ORM 模型:
pip install epay-sdk[sqlalchemy]
如需使用 Django 示例中的集成模板:
pip install epay-sdk[django]
本地开发:
pip install -e .[dev]
快速开始
from epay_sdk import EPayClient, EPayConfig, PayType, PaymentService
client = EPayClient(
EPayConfig(
pid="1001",
key="your_secret_key",
base_url="https://your-epay.com",
environment="production",
)
)
service = PaymentService(client)
pay_url = service.create_order(
pay_type=PayType.ALIPAY,
order_no="ORDER_10001",
amount="9.90",
subject="会员充值",
notify_url="https://api.example.com/pay/notify",
return_url="https://www.example.com/pay/success",
)
print(pay_url)
PaymentService 还保留了便捷方法:
create_alipay_order(...)create_wxpay_order(...)create_qqpay_order(...)
create_order、create_order_url、create_order(client) 的区别
PaymentService.create_order(...):面向业务层的默认入口。负责金额标准化,并返回可直接跳转/展示给前端的支付 URL。EPayClient.create_order_url(CreateOrderRequest):更底层;当你已经自己构造了CreateOrderRequest时使用。EPayClient.create_order(CreateOrderRequest):直接向网关发起请求并返回原始响应文本,只在你确实需要网关原始返回时使用。
如果你的场景只是“生成付款链接”,优先用 PaymentService.create_order(...) 或相应便捷方法。
推荐回调流程
推荐把异步通知处理为如下链路:
- 收到网关回调表单
client.verify_callback()验签,并校验pid、sign_type、必要字段、金额格式CallbackProcessor.process()加载本地订单并校验金额- 如启用
verify_with_query=True,自动执行query_order()+parse_query_result()做二次确认 - 通过
mark_paid_if_unpaid()做原子状态更新 - 通过
record_callback_attempt(..., stage=...)记录审计阶段 - 由调用方决定是否提交/回滚当前事务
- 首次成功返回
success;失败返回fail以便平台重试
回调审计阶段目前包括:
RECEIVEDRECONCILEDAPPLIEDREJECTED
仓储接口约定
你需要自己实现订单仓储,但推荐遵守下面的协议:
class OrderRepository(Protocol):
def get_order(self, order_no: str) -> Optional[OrderRecord]: ...
def mark_paid_if_unpaid(self, order_no: str, gateway_trade_no: str, raw_payload: str) -> bool: ...
def record_callback_attempt(
self,
order_no: str,
accepted: bool,
reason: Optional[str],
raw_payload: str,
*,
stage: str,
) -> None: ...
其中:
mark_paid_if_unpaid()必须保证原子性,通常用数据库条件更新实现record_callback_attempt()的stage是关键字段,调用方应完整记录- 如果你的仓储具备事务能力,可额外提供
commit()/rollback() CallbackProcessor.process()默认不提交或回滚外部事务;这样可以避免意外提交同一事务里的其它 ORM 改动- 如果你明确希望沿用“由回调处理器负责结束事务”的模式,可显式传入
CallbackProcessor(..., manage_transaction=True),此时它才会在结束时调用仓储的commit()/rollback()
SQLAlchemy 默认实现
安装 epay-sdk[sqlalchemy] 后,顶层模块会按需暴露:
BasePaymentOrderModelCallbackAuditModelSQLAlchemyOrderRepositorycreate_sqlite_engine
示例:
from sqlalchemy.orm import Session
from epay_sdk import Base, PaymentOrderModel, SQLAlchemyOrderRepository, create_sqlite_engine
engine = create_sqlite_engine("sqlite:///./epay.db")
Base.metadata.create_all(engine)
with Session(engine) as session:
session.add(PaymentOrderModel(order_no="A001", amount="9.90", status="UNPAID"))
session.commit()
repo = SQLAlchemyOrderRepository(session)
if repo.mark_paid_if_unpaid("A001", "G100", '{"trade_no":"G100"}'):
repo.record_callback_attempt("A001", True, None, '{"trade_no":"G100"}', stage="APPLIED")
repo.commit()
如果未安装 SQLAlchemy extra,import epay_sdk 仍可正常工作;只有在访问上述 SQLAlchemy 符号时才会提示安装可选依赖。
Django 集成模板
Django 集成采取的策略是:
src/epay_sdk/只保留框架无关的核心能力- Django ORM、Django view、Django URL 与事务边界放在你的应用层实现
- 仓库内提供
examples/django_app/作为可直接参考/复制的 Django 模板
也就是说,Django 支持不会耦合进核心 SDK 包本身;SDK 只要求你在 Django 项目中实现符合协议的仓储与回调接入层。
如果你希望直接运行示例,可安装:
pip install epay-sdk[django]
示例入口见:examples/django_app/
Django 中的事务建议
Django 场景下,推荐把回调处理包在 transaction.atomic() 中,并继续使用 CallbackProcessor 的默认行为:
CallbackProcessor(..., manage_transaction=False)(默认值)- 由 Django 的
transaction.atomic()负责提交/回滚 - Django 仓储本身不需要暴露
commit()/rollback()
示意:
from django.db import transaction
with transaction.atomic():
callback = client.verify_callback(request.POST.dict())
repo = DjangoOrderRepository()
processor = CallbackProcessor(client, repo, verify_with_query=True)
result = processor.process(callback)
return HttpResponse(result.response_text, content_type="text/plain")
这样可以保持和核心 SDK 一致的事务边界:回调处理器负责业务编排,是否提交整个事务由 Django 应用层决定。
Django 仓储如何映射协议
无论使用 Django ORM 还是其他 ORM,仓储仍应满足同一个 OrderRepository 协议。放到 Django 里,通常对应为:
get_order(order_no):从 Django model 读取订单,并映射为OrderRecordmark_paid_if_unpaid(order_no, gateway_trade_no, raw_payload):使用单条条件更新record_callback_attempt(...):写入回调审计表
其中最关键的是 mark_paid_if_unpaid() 的原子性。在 Django 中,推荐明确写成:
updated = PaymentOrder.objects.filter(
order_no=order_no,
status="UNPAID",
).update(
status="PAID",
gateway_trade_no=gateway_trade_no,
raw_payload=raw_payload,
paid_at=timezone.now(),
)
first_success = updated == 1
这对应核心协议里“只允许 UNPAID -> PAID”的幂等要求,避免用“先查再改”的方式破坏并发安全语义。
Django 示例包含什么
examples/django_app/ 当前展示了:
- Django model 设计
DjangoOrderRepository的 duck typing 实现PaymentService.create_order(...)的下单 view 用法client.verify_callback(...) + CallbackProcessor.process(...)的回调处理方式- 使用
transaction.atomic()控制回调事务 - 示例 URL wiring 与 Django 测试
安全与运行注意事项
- 签名协议说明:SDK 按易支付常见协议使用“排序后的非空参数 + 商户密钥”做 MD5。这里是为了兼容网关协议,不代表 MD5 适合独立承担现代传输安全责任。
- 生产环境请使用 HTTPS:生产配置应使用
https://...的base_url,并保持verify_ssl=True。SDK 已禁止在environment="production"时关闭 SSL 校验。 - SQLite 仅适合开发/测试:示例中的 SQLite 适合本地演示;生产支付回调更适合 PostgreSQL/MySQL 等具备更可靠并发/锁语义的数据库。
- 回调必须验签再处理:不要直接信任回调参数;应始终先调用
verify_callback()。 environment字段当前是安全/配置语义字段:它目前只影响校验和告警(例如生产环境 TLS 约束),不会自动切换网关地址或协议行为;可视为保留给部署语义和未来扩展的字段。
示例
examples/flask_app.py:最小 Flask 集成,使用内存仓储演示回调协议examples/fastapi_app.py:FastAPI + SQLAlchemy 示例,并显式把同步 SDK/数据库操作放入线程池,避免误导为真正的异步 I/Oexamples/django_app/:Django 集成模板,演示“核心 SDK 保持通用 + Django 仓储/视图/事务放在应用层”
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file epay_sdk-0.4.0.tar.gz.
File metadata
- Download URL: epay_sdk-0.4.0.tar.gz
- Upload date:
- Size: 24.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
479fa8d023b77562e06ec8a563d7a90e58ec337d4f833cb661050d6a57dc55bf
|
|
| MD5 |
7bc5a4b2c379d8aa0093744ce95e41e8
|
|
| BLAKE2b-256 |
54ffb2f1f4835cff7287334f759c0d8a5391dcd84313a4c040dcbf0fc1493cdb
|
File details
Details for the file epay_sdk-0.4.0-py3-none-any.whl.
File metadata
- Download URL: epay_sdk-0.4.0-py3-none-any.whl
- Upload date:
- Size: 17.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3dade38965a8df700fb7ec93633aa0b93c639905f3282cd0b1d1532f1125c08b
|
|
| MD5 |
76aacb6a4c1e3db3181aabc5b59c07d7
|
|
| BLAKE2b-256 |
0422f51a525586aa68e09c4a4ac578f18752fd0a4beeb60ed4a08737c9fca539
|