# 35pay Python SDK

单文件 `pay35.py`，drop-in 任何 Python 3.9+ 项目。

- **同步 `Pay`**：零依赖（仅用 stdlib urllib + hmac），适合 Flask / Django sync / 脚本
- **异步 `AsyncPay`**：基于 httpx，适合 FastAPI / Starlette / Litestar / aiohttp 等 async 框架

## Install

```bash
# 方式 1：drop-in 单文件（同步用法）
curl -O https://pay.35team.com/sdk/python/pay35.py

# 方式 2：从 PyPI（同步用法）
pip install pay35

# 方式 3：async 用法（额外装 httpx）
pip install pay35[async]
```

## Usage（同步）

```python
import os
from pay35 import Pay

pay = Pay(api_key=os.environ["PAY_KEY"])

# 创建支付（dev: sk_test_xxx → 自动 mock；prod: sk_live_xxx → 真支付）
session = pay.create_checkout(
    amount=9900,                  # 单位：分
    currency="CNY",
    description="追思视频套餐",
    metadata={"order_id": str(order.id)},
    success_url="https://yoursite.com/orders/123",
)

# 跳到 35pay 托管收银台
return redirect(session["url"])
```

## Usage（异步，FastAPI / Starlette / Litestar / aiohttp）

```python
import os
from contextlib import asynccontextmanager
from fastapi import FastAPI
from pay35 import AsyncPay

@asynccontextmanager
async def lifespan(app: FastAPI):
    app.state.pay = AsyncPay(api_key=os.environ["PAY_KEY"])
    try:
        yield
    finally:
        await app.state.pay.aclose()

app = FastAPI(lifespan=lifespan)

@app.post("/checkout")
async def checkout():
    session = await app.state.pay.create_checkout(
        amount=9900, currency="CNY",
        metadata={"order_id": "..."},
        success_url="https://yoursite.com/done",
    )
    return {"url": session["url"]}
```

> async 内部使用单例 `httpx.AsyncClient`，复用 TCP 连接池。建议进程级单例（如 FastAPI 的 `lifespan`），不要每个请求 `new` 一次。

## Webhook 验签

```python
from pay35 import Pay

@app.route("/webhook", methods=["POST"])
def webhook():
    sig = request.headers.get("X-35pay-Signature", "")
    if not Pay.verify_webhook_signature(
        request.data.decode(), sig, WEBHOOK_SECRET
    ):
        return "invalid", 401
    event = request.get_json()
    if event["type"] == "payment.paid":
        order_id = event["data"]["metadata"]["order_id"]
        mark_order_paid(order_id)
    return "ok"
```

## API

同步 `Pay` 和异步 `AsyncPay` 方法签名一致，async 方法加 `await`。

| 方法 | 说明 |
|---|---|
| `Pay(api_key, base_url=..., timeout=15.0)` | 同步客户端 |
| `AsyncPay(api_key, base_url=..., timeout=15.0)` | 异步客户端，需 `pip install pay35[async]` |
| `(a)pay.create_checkout(amount, currency, ...)` | 创建支付 session，返回 `{"url": ..., "id": ...}` |
| `(a)pay.get_session(session_id)` | 查询状态 |
| `(a)pay.refund(record_id, amount=None, reason=None)` | 退款 |
| `await async_pay.aclose()` | 释放底层 httpx 连接池（推荐用 `async with`） |
| `Pay.verify_webhook_signature(payload, header, secret)` | static 方法，验签（同步/异步通用） |

## 兼容性

- Python 3.9+
- 默认零依赖（同步 `Pay` 用 `urllib`）
- 异步可选：`pip install pay35[async]` 拉入 `httpx>=0.24`
- 异常：HTTP 非 2xx 抛 `PayError(status, body)`
