Metadata-Version: 2.4
Name: transaierp-payment
Version: 0.1.0
Summary: Server-side client for TransAI Payment Center V1
Requires-Python: >=3.10
Description-Content-Type: text/markdown

# TransAI Payment SDK for Python

Requires Python 3.10+. Runtime uses only the standard library.
This is a **server-side** SDK, not a desktop/mobile/browser SDK.

## Install

From the workspace root:

```sh
python -m pip install ./payment-sdk/python
```

The distribution is named `transaierp-payment`; the import is `transai_payment`.
This source package has not been published to PyPI.

## Orders

```python
import os
from transai_payment import APIError, Client, CreateOrderRequest

client = Client(
    os.environ["PAYMENT_CENTER_BASE_URL"],
    os.environ["PAYMENT_CENTER_API_KEY"],
    timeout=15,
)

try:
    result = client.create_order(CreateOrderRequest(
        business_order_no="enterprise-20260918-001",
        title="Enterprise service",
        amount=9900,
        currency="CNY",
        buyer_reference="customer-123",
        metadata={"local_order_id": "enterprise-20260918-001"},
    ))
except APIError as error:
    # Handle error.status and error.message without logging credentials.
    raise

checkout_url = result["checkout_url"]  # Return only this URL to your frontend.
order = client.get_order(result["order"]["id"])
same_order = client.find_order("enterprise-20260918-001")
```

The base URL is an HTTPS origin (or deployment prefix), without
`/api/payment-center/v1`, credentials, query, or fragment. Amounts are integer
CNY cents. A business order number identifies one immutable order; retry with
the same number and same fields after a network failure, not a new number.
HTTP redirects are disabled. API responses are limited to 1 MiB.
Network errors retain the standard library exception types; malformed responses
raise `ProtocolError`. There are no automatic retries.

## Notifications

```python
from transai_payment import WebhookError, verify_webhook

# raw_body must be the exact incoming bytes, before JSON parsing.
# headers is a mapping of header names to strings or lists of values.
try:
    event = verify_webhook(
        raw_body,
        headers,
        [os.environ["PAYMENT_CENTER_WEBHOOK_SECRET"]],
    )
except WebhookError:
    # Reject the request, without delivering any entitlement.
    raise
```

For a blocking binary request stream use
`read_webhook(stream, headers, secrets)`, which reads at most 64 KiB + 1.
Keep an equivalent request body limit in your web server. Preserve duplicate
header values so the SDK can reject them.

Verification checks HMAC-SHA256 over `<timestamp>.<raw-body>`, a +/-300 second
window, event ID, `payment.succeeded`, version `v1`, and paid CNY order fields.
Pass both old and new notification secrets during rotation.

Before granting entitlements, compare the application ID, business order number,
amount, and currency to your own stored order. Deduplicate both event ID and
business order within your fulfillment transaction. Only then return HTTP 2xx.
A repeated, already committed event should also receive 2xx. A browser return
URL is not proof of payment.

## Test

Run from `payment-sdk/python`:

```sh
python -m unittest discover -s tests -v
```

V1 has no refund methods, new payment-provider implementations, or entitlement
logic. API keys and webhook secrets must never be shipped to end-user software.
