Skip to content

Commit

Permalink
server/checkout: generate a customer session after confirmed checkotu
Browse files Browse the repository at this point in the history
  • Loading branch information
frankie567 committed Dec 6, 2024
1 parent 67436a2 commit 93ae45f
Show file tree
Hide file tree
Showing 6 changed files with 44 additions and 5 deletions.
3 changes: 2 additions & 1 deletion server/polar/checkout/endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
CheckoutCreate,
CheckoutCreatePublic,
CheckoutPublic,
CheckoutPublicConfirmed,
CheckoutUpdate,
CheckoutUpdatePublic,
)
Expand Down Expand Up @@ -212,7 +213,7 @@ async def client_update(

@router.post(
"/client/{client_secret}/confirm",
response_model=CheckoutPublic,
response_model=CheckoutPublicConfirmed,
summary="Confirm Checkout Session from Client",
responses={
200: {"description": "Checkout session confirmed."},
Expand Down
12 changes: 12 additions & 0 deletions server/polar/checkout/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -416,3 +416,15 @@ class CheckoutPublic(CheckoutBase):
discount: CheckoutDiscount | None
organization: Organization
attached_custom_fields: list[AttachedCustomField]


class CheckoutPublicConfirmed(CheckoutPublic):
"""
Checkout session data retrieved using the client secret after confirmation.
It contains a customer session token to retrieve order information
right after the checkout.
"""

status: Literal[CheckoutStatus.confirmed]
customer_session_token: str
15 changes: 11 additions & 4 deletions server/polar/checkout/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from polar.config import settings
from polar.custom_field.data import validate_custom_field_data
from polar.customer.service import customer as customer_service
from polar.customer_session.service import customer_session as customer_session_service
from polar.discount.service import DiscountNotRedeemableError
from polar.discount.service import discount as discount_service
from polar.enums import PaymentProcessor
Expand Down Expand Up @@ -54,10 +55,7 @@
UserOrganization,
)
from polar.models.checkout import CheckoutStatus
from polar.models.product_price import (
ProductPriceAmountType,
ProductPriceFree,
)
from polar.models.product_price import ProductPriceAmountType, ProductPriceFree
from polar.models.webhook_endpoint import WebhookEventType
from polar.organization.service import organization as organization_service
from polar.postgres import AsyncSession
Expand Down Expand Up @@ -782,6 +780,15 @@ async def _confirm_inner(

await self._after_checkout_updated(session, checkout)

assert checkout.customer is not None
(
customer_session_token,
_,
) = await customer_session_service.create_customer_session(
session, checkout.customer
)
checkout.customer_session_token = customer_session_token

return checkout

async def handle_stripe_success(
Expand Down
8 changes: 8 additions & 0 deletions server/polar/models/checkout.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,14 @@ def is_payment_form_required(self) -> bool:
def url(self) -> str:
return settings.generate_frontend_url(f"/checkout/{self.client_secret}")

@property
def customer_session_token(self) -> str | None:
return getattr(self, "_customer_session_token", None)

@customer_session_token.setter
def customer_session_token(self, value: str) -> None:
self._customer_session_token = value

attached_custom_fields: AssociationProxy[Sequence["AttachedCustomFieldMixin"]] = (
association_proxy("product", "attached_custom_fields")
)
Expand Down
3 changes: 3 additions & 0 deletions server/tests/checkout/test_endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,3 +316,6 @@ async def test_valid(
)

assert response.status_code == 200

json = response.json()
assert "customer_session_token" in json
8 changes: 8 additions & 0 deletions server/tests/checkout/test_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
PaymentRequired,
)
from polar.checkout.service import checkout as checkout_service
from polar.customer_session.service import customer_session as customer_session_service
from polar.discount.service import discount as discount_service
from polar.enums import PaymentProcessor
from polar.exceptions import PolarRequestValidationError
Expand Down Expand Up @@ -1867,6 +1868,13 @@ async def test_valid_stripe(
**expected_tax_metadata,
}

assert checkout.customer_session_token is not None
customer_session = await customer_session_service.get_by_token(
session, checkout.customer_session_token
)
assert customer_session is not None
assert customer_session.customer == checkout.customer

@pytest.mark.parametrize(
"customer_billing_address,expected_tax_metadata",
[
Expand Down

0 comments on commit 93ae45f

Please sign in to comment.