Metadata-Version: 2.4
Name: deliveryapi
Version: 1.3.0
Summary: Official Python SDK for the DeliveryAPI courier tracking platform
Project-URL: Homepage, https://deliveryapi.co.kr
Project-URL: Documentation, https://docs.deliveryapi.co.kr
Author-email: DeliveryAPI <support@deliveryapi.co.kr>
License: MIT
Keywords: courier,delivery,korea,sdk,tracking,webhook
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: httpx>=0.27.0
Requires-Dist: typing-extensions>=4.0.0
Provides-Extra: dev
Requires-Dist: mypy>=1.9; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Description-Content-Type: text/markdown

# deliveryapi — Python SDK

[DeliveryAPI](https://deliveryapi.co.kr) 택배 추적 및 웹훅 플랫폼의 공식 Python SDK입니다.

## 설치

```bash
pip install deliveryapi
```

Python 3.9 이상을 지원합니다.

## 빠른 시작

```python
from deliveryapi import DeliveryAPIClient

client = DeliveryAPIClient(
    api_key="pk_live_...",
    secret_key="sk_live_...",
)

# 지원 택배사 목록
couriers = client.tracking.get_couriers()
for c in couriers["couriers"]:
    print(c["trackingApiCode"], c["displayName"])

# 운송장 조회 (최대 10건 배치)
result = client.tracking.trace(
    items=[
        {"courierCode": "cj",    "trackingNumber": "1234567890", "clientId": "order_001"},
        {"courierCode": "lotte", "trackingNumber": "9876543210"},
    ]
)
for r in result["results"]:
    if r["success"]:
        print(r["data"]["deliveryStatus"])  # e.g. "IN_TRANSIT"
    else:
        print(r["error"]["code"])
```

## 비동기 지원

```python
import asyncio
from deliveryapi import AsyncDeliveryAPIClient

async def main():
    async with AsyncDeliveryAPIClient(
        api_key="pk_live_...",
        secret_key="sk_live_...",
    ) as client:
        result = await client.tracking.trace(
            items=[{"courierCode": "cj", "trackingNumber": "1234567890"}]
        )
        print(result["results"][0]["data"]["deliveryStatus"])

asyncio.run(main())
```

## 에러 처리

```python
from deliveryapi import DeliveryAPIClient, ApiError, AuthenticationError, RateLimitError

client = DeliveryAPIClient(api_key="pk_...", secret_key="sk_...")

try:
    result = client.tracking.trace(
        items=[{"courierCode": "cj", "trackingNumber": "1234567890"}]
    )
except AuthenticationError:
    print("API 키 또는 시크릿 키가 올바르지 않습니다")
except RateLimitError:
    print("요청 한도를 초과했습니다")
except ApiError as e:
    print(f"[{e.code}] {e} (HTTP {e.status})")
```

## 웹훅 엔드포인트 관리

```python
# 엔드포인트 등록
endpoint = client.webhooks.endpoints.create(
    url="https://my-server.com/webhook",
    name="운영 서버",
)
# webhookSecret은 이 응답에서만 확인 가능합니다 — 안전하게 보관하세요!
print(endpoint["endpointId"])
print(endpoint["webhookSecret"])

# 엔드포인트 목록
result = client.webhooks.endpoints.list()
for ep in result["endpoints"]:
    print(ep["endpointId"], ep["status"])

# 이름 수정
client.webhooks.endpoints.update("ep_xxxx", name="스테이징")

# 시크릿 재발급
new = client.webhooks.endpoints.rotate_secret("ep_xxxx")
print(new["webhookSecret"])

# 삭제
client.webhooks.endpoints.delete("ep_xxxx")
```

## 택배 추적 구독

```python
# 구독 등록
sub = client.webhooks.subscriptions.register(
    items=[
        {"courierCode": "cj", "trackingNumber": "1234567890", "clientId": "order_001"}
    ],
    recurring=True,          # True: 배송 완료까지 반복 추적 / False: 1회 조회
    endpoint_id="ep_xxxx",   # 웹훅 수신 엔드포인트 (선택)
    metadata={"orderId": "ORD-001"},
)
print(sub["requestId"])  # e.g. "req_xxxx"

# 구독 목록 (한 페이지당 최대 50건)
page = client.webhooks.subscriptions.list()
for s in page["subscriptions"]:
    print(s["requestId"], s["isActive"])

# 구독 상세
detail = client.webhooks.subscriptions.get("req_xxxx")
for item in detail["items"]:
    print(item["trackingNumber"], item["currentStatus"])

# 배치 결과 조회
result = client.webhooks.subscriptions.batch_results(
    items=[
        {"courierCode": "cj",    "trackingNumber": "1234567890"},
        {"courierCode": "lotte", "trackingNumber": "9876543210"},
    ]
)

# 구독 취소
client.webhooks.subscriptions.cancel("req_xxxx")
```

## 웹훅 서명 검증

수신된 웹훅의 `X-Webhook-Signature` 헤더를 검증합니다.

```python
from deliveryapi import verify_webhook_signature, WebhookSignatureError

# Flask 예시
from flask import Flask, request

app = Flask(__name__)

@app.route("/webhook", methods=["POST"])
def handle_webhook():
    try:
        verify_webhook_signature(
            payload=request.get_data(as_text=True),
            signature=request.headers["X-Webhook-Signature"],
            timestamp=request.headers["X-Webhook-Timestamp"],
            secret="whsec_...",
        )
    except WebhookSignatureError:
        return "Invalid signature", 400

    event = request.json
    print(event["event"], event["requestId"])
    return "", 200
```

## 컨텍스트 매니저

```python
# Sync
with DeliveryAPIClient(api_key="pk_...", secret_key="sk_...") as client:
    result = client.tracking.trace(items=[...])

# Async
async with AsyncDeliveryAPIClient(api_key="pk_...", secret_key="sk_...") as client:
    result = await client.tracking.trace(items=[...])
```

## 링크

- [홈페이지](https://deliveryapi.co.kr)
- [API 문서](https://docs.deliveryapi.co.kr)

## 라이선스

MIT
