Skip to content

Commit 4d26d0f

Browse files
feat: add checkout endpoint
1 parent a0ac7ce commit 4d26d0f

File tree

10 files changed

+85
-11
lines changed

10 files changed

+85
-11
lines changed

autumn/aio/client.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
if TYPE_CHECKING:
1212
from typing_extensions import Self
13-
from .shed import AttachParams, CheckParams, TrackParams
13+
from .shed import AttachParams, CheckParams, TrackParams, CheckoutParams
1414

1515
__all__ = ("AsyncClient",)
1616

@@ -42,6 +42,7 @@ class AsyncClient(Client):
4242
attach: AttachParams # type: ignore
4343
check: CheckParams # type: ignore
4444
track: TrackParams # type: ignore
45+
checkout: CheckoutParams # type: ignore
4546

4647
def __init__(self, token: str, *, base_url: Optional[str] = None):
4748
from .. import BASE_URL, VERSION

autumn/aio/shed.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
AttachResponse,
1313
CheckResponse,
1414
TrackResponse,
15+
CheckoutResponse,
1516
)
1617

1718

@@ -63,3 +64,13 @@ def __call__(
6364
properties: Optional[Dict[str, Any]] = None,
6465
customer_data: Optional[CustomerData] = None,
6566
) -> Awaitable[TrackResponse]: ...
67+
68+
69+
class CheckoutParams(Protocol):
70+
def __call__(
71+
self,
72+
customer_id: str,
73+
product_id: str,
74+
*,
75+
success_url: Optional[str] = None,
76+
) -> Awaitable[CheckoutResponse]: ...

autumn/asgi/app.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
check_route,
2525
track_route,
2626
cancel_route,
27+
checkout_route,
2728
billing_portal_route,
2829
)
2930
from .routes.customers import create_customer_route, pricing_table_route
@@ -80,6 +81,7 @@ def __init__(
8081
billing_portal_route,
8182
methods={"POST"},
8283
),
84+
Route("/checkout/", checkout_route, methods={"POST"}),
8385
Route(
8486
"/customers/", create_customer_route, methods={"POST", "OPTIONS"}
8587
),

autumn/asgi/routes/core.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,11 @@ async def billing_portal_route(request: Request):
5151
customer_id = identify["customer_id"]
5252
response = await autumn.customers.get_billing_portal(customer_id, **json)
5353
return _build_response(response)
54+
55+
56+
async def checkout_route(request: Request):
57+
identify, autumn, json = await _extract(request)
58+
59+
customer_id = identify["customer_id"]
60+
response = await autumn.checkout(customer_id, **json)
61+
return _build_response(response)

autumn/client.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from .models.response import (
1111
AttachResponse,
1212
CheckResponse,
13+
CheckoutResponse,
1314
TrackResponse,
1415
)
1516

@@ -237,3 +238,13 @@ def track(
237238
"""
238239
payload = _build_payload(locals(), self.track)
239240
return self.http.request("POST", "/track", TrackResponse, json=payload)
241+
242+
def checkout(
243+
self,
244+
customer_id: str,
245+
product_id: str,
246+
*,
247+
success_url: Optional[str] = None,
248+
) -> CheckoutResponse:
249+
payload = _build_payload(locals(), self.checkout)
250+
return self.http.request("POST", "/checkout", CheckoutResponse, json=payload)

autumn/models/meta.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
from pydantic import BaseModel
77

8+
from .products import ProductItem
9+
810
if TYPE_CHECKING:
911
from typing import Dict, Any
1012

@@ -31,3 +33,14 @@ class CustomerData(BaseModel):
3133
name: Optional[str] = None
3234
email: Optional[str] = None
3335
fingerprint: Optional[str] = None
36+
37+
38+
class CheckoutLine(BaseModel):
39+
description: str
40+
amount: int
41+
item: ProductItem
42+
43+
44+
class Cycle(BaseModel):
45+
starts_at: int
46+
total: int

autumn/models/products.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,14 @@ class FreeTrial(BaseModel):
6666

6767

6868
class Product(BaseModel):
69-
autumn_id: str
69+
autumn_id: Optional[str] = None
7070
created_at: float
7171
id: str
7272
name: str
7373
env: Literal["sandbox", "live"]
7474
is_add_on: bool
7575
is_default: bool
76-
group: str
76+
group: Optional[str] = None
7777
version: float
7878
items: List[ProductItem]
7979
free_trial: Optional[FreeTrial] = None

autumn/models/response.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
from pydantic import BaseModel
44

5-
from .meta import AppEnv
6-
from .products import ProductItem, FreeTrial
5+
from .meta import AppEnv, CheckoutLine, AttachOption, Cycle
6+
from .products import ProductItem, FreeTrial, Product
77
from .customers import PricingTableProduct
88
from .features import FeaturePreview
99
from .products import ProductPreview
@@ -46,6 +46,19 @@ class UsageResponse(BaseModel):
4646
feature_id: str
4747

4848

49+
class CheckoutResponse(BaseModel):
50+
url: Optional[str] = None
51+
customer_id: str
52+
has_prorations: bool
53+
total: float
54+
currency: str
55+
lines: List[CheckoutLine]
56+
options: List[AttachOption]
57+
product: Product
58+
current_product: Optional[Product] = None
59+
next_cycle: Optional[Cycle] = None
60+
61+
4962
class BillingPortalResponse(BaseModel):
5063
url: str
5164
customer_id: str

pyproject.toml

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,12 @@ docs = [
3434
"sphinx-autodoc-typehints>=2.3.0",
3535
"sphinx-tabs>=3.4.7",
3636
]
37-
tests = [
38-
"pytest>=8.4.1",
39-
"pytest-asyncio>=1.1.0",
40-
"pytest-cov>=6.2.1",
41-
]
37+
tests = ["pytest>=8.4.1", "pytest-asyncio>=1.1.0", "pytest-cov>=6.2.1"]
4238
dev = ["litestar>=2.16.0", "uvicorn>=0.34.3"]
4339

4440
[tool.pytest.ini_options]
4541
pythonpath = ["."]
42+
asyncio_mode = "auto"
4643

4744
[tool.pyright]
4845
typeCheckingMode = "standard"

tests/test_sandbox.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ async def test_customers(autumn: Autumn):
2525

2626
updated_customer = await autumn.customers.get(customer.id) # type: ignore
2727
assert updated_customer.name == "ay-rod"
28-
2928
await autumn.customers.get_billing_portal(customer.id) # type: ignore
3029

3130
await autumn.customers.delete(customer.id) # type: ignore
@@ -53,3 +52,22 @@ async def test_products(autumn: Autumn):
5352

5453
await autumn.products.delete(default.id) # type: ignore
5554
await autumn.products.delete(add_on.id) # type: ignore
55+
56+
@pytest.mark.asyncio
57+
async def test_checkout(autumn: Autumn):
58+
customer = await autumn.customers.create(
59+
"test_customer_id",
60+
name="John Yeo",
61+
62+
)
63+
product = await autumn.products.create(
64+
"test_product_id",
65+
name="Test Product",
66+
is_add_on=False,
67+
is_default=False,
68+
)
69+
70+
await autumn.checkout(customer.id, product.id) # type: ignore
71+
72+
await autumn.customers.delete(customer.id) # type: ignore
73+
await autumn.products.delete(product.id) # type: ignore

0 commit comments

Comments
 (0)