diff options
| author | stuppie | 2026-09-23 19:34:12 -0600 |
|---|---|---|
| committer | stuppie | 2026-09-23 19:34:12 -0600 |
| commit | 6aaf8d26bb3e2cc3a004fcb1c94ddc85a1fc6a45 (patch) | |
| tree | 2063d4a901f4929ebe49840914a23e414b845bb1 | |
| parent | 785adc51b2cc2b4d3e845a454f38b2a5197cc7f0 (diff) | |
| download | amt-jb-6aaf8d26bb3e2cc3a004fcb1c94ddc85a1fc6a45.tar.gz amt-jb-6aaf8d26bb3e2cc3a004fcb1c94ddc85a1fc6a45.zip | |
move create_paypal_cashout_method_if_not_exists into fastapi background task. Change CashoutRequest max ->
| -rw-r--r-- | jb/managers/thl.py | 2 | ||||
| -rw-r--r-- | jb/models/wallet.py | 2 | ||||
| -rw-r--r-- | jb/views/auth.py | 88 |
3 files changed, 76 insertions, 16 deletions
diff --git a/jb/managers/thl.py b/jb/managers/thl.py index 191a56b..96f624e 100644 --- a/jb/managers/thl.py +++ b/jb/managers/thl.py @@ -26,7 +26,7 @@ def user_cashout_request( product_user_id: str, amount: USDCent, cashout_method_id: str ) -> CashoutRequestInfo: assert isinstance(amount, USDCent) - assert USDCent(0) < amount < USDCent(10_00) + assert USDCent(0) < amount <= USDCent(100_00) url = f"{settings.fsb_host}{settings.product_id}/cashout/" body: dict[str, str | int] = { diff --git a/jb/models/wallet.py b/jb/models/wallet.py index 226e71f..3d4d10b 100644 --- a/jb/models/wallet.py +++ b/jb/models/wallet.py @@ -6,7 +6,7 @@ from pydantic import BaseModel, ConfigDict, Field class CashoutRequest(BaseModel): model_config = ConfigDict(extra="forbid") - amount: USDCent = Field(gt=0, lt=10_00, description="Amount in USD cents") + amount: USDCent = Field(gt=0, le=100_00, description="Amount in USD cents") cashout_method_id: UUIDStr = Field(description="Cashout method ID") diff --git a/jb/views/auth.py b/jb/views/auth.py index aa3cf03..ba1a6f8 100644 --- a/jb/views/auth.py +++ b/jb/views/auth.py @@ -2,7 +2,16 @@ import secrets from typing import Annotated from urllib.parse import urlencode -from fastapi import APIRouter, Depends, Header, HTTPException, Request, Response, status +from fastapi import ( + APIRouter, + BackgroundTasks, + Depends, + Header, + HTTPException, + Request, + Response, + status, +) from fastapi.responses import HTMLResponse, RedirectResponse from jb.api.auth import ( @@ -39,6 +48,19 @@ from jb.views.utils import get_client_ip auth_router = APIRouter(prefix="/auth", tags=["Auth"]) +def try_create_paypal_cashout_method_if_not_exists( + product_user_id: str, email: str, client_ip: str +) -> None: + try: + create_paypal_cashout_method_if_not_exists( + product_user_id=product_user_id, + email=email, + client_ip=client_ip, + ) + except Exception: + LOG.exception("Failed to create PayPal cashout method for %s", product_user_id) + + def authenticate_invite_amt_account_link( authorization: Annotated[str | None, Header()] = None, ) -> None: @@ -79,6 +101,7 @@ def request_magic_link(body: AccountLogin) -> dict[str, str]: @auth_router.get("/magic-link/", response_class=HTMLResponse, include_in_schema=False) def magic_link_landing_page( request: Request, + background_tasks: BackgroundTasks, gr_api: Annotated[GRApiManager, Depends(get_gr_api_manager)], token: str | None = None, ) -> Response: @@ -91,7 +114,13 @@ def magic_link_landing_page( ) response = RedirectResponse(url="/", status_code=status.HTTP_303_SEE_OTHER) client_ip = get_client_ip(request) - _exchange_magic_link(token, response, gr_api, client_ip=client_ip) + _exchange_magic_link( + token, + response, + gr_api, + client_ip=client_ip, + background_tasks=background_tasks, + ) return response return HTMLResponse( render_base_html(), @@ -106,26 +135,38 @@ def magic_link_landing_page( @auth_router.post("/magic-link/exchange/", status_code=status.HTTP_204_NO_CONTENT) def exchange_magic_link( request: Request, + background_tasks: BackgroundTasks, body: MagicLinkExchangeRequest, response: Response, gr_api: Annotated[GRApiManager, Depends(get_gr_api_manager)], ) -> None: """Exchange a magic link only after its landing page makes an explicit POST.""" client_ip = get_client_ip(request) - _exchange_magic_link(body.token, response, gr_api, client_ip=client_ip) + _exchange_magic_link( + body.token, + response, + gr_api, + client_ip=client_ip, + background_tasks=background_tasks, + ) def _exchange_magic_link( - token: str, response: Response, gr_api: GRApiManager, client_ip: str + token: str, + response: Response, + gr_api: GRApiManager, + client_ip: str, + background_tasks: BackgroundTasks, ) -> None: user_email = consume_magic_token(token) user = gr_api.ensure_user_exists(User.model_validate({"email": user_email})) - # create_paypal_cashout_method_if_not_exists( - # product_user_id=user.product_user_id, email=user.email - # ) - create_paypal_cashout_method_if_not_exists( - product_user_id=user.product_user_id, email=user.email, client_ip=client_ip + # Cashout setup is not required for login and should not delay the response. + background_tasks.add_task( + try_create_paypal_cashout_method_if_not_exists, + product_user_id=user.product_user_id, + email=str(user.email), + client_ip=client_ip, ) response.set_cookie( @@ -178,6 +219,7 @@ def invite_amt_account_link( @auth_router.get("/debug/", response_class=HTMLResponse, include_in_schema=False) def link_amt_account_landing_page( request: Request, + background_tasks: BackgroundTasks, gr_api: Annotated[GRApiManager, Depends(get_gr_api_manager)], token: str | None = None, ) -> HTMLResponse: @@ -195,7 +237,11 @@ def link_amt_account_landing_page( client_ip = get_client_ip(request) _response = RedirectResponse(url="/", status_code=status.HTTP_303_SEE_OTHER) _exchange_amt_account_link( - token=token, response=_response, gr_api=gr_api, client_ip=client_ip + token=token, + response=_response, + gr_api=gr_api, + client_ip=client_ip, + background_tasks=background_tasks, ) return HTMLResponse( @@ -211,6 +257,7 @@ def link_amt_account_landing_page( @auth_router.post("/link-amt/exchange/", status_code=status.HTTP_204_NO_CONTENT) def exchange_amt_account_link( request: Request, + background_tasks: BackgroundTasks, body: MagicLinkExchangeRequest, response: Response, gr_api: Annotated[GRApiManager, Depends(get_gr_api_manager)], @@ -218,14 +265,24 @@ def exchange_amt_account_link( """Validate the email link, then transition the bound AMT account.""" client_ip = get_client_ip(request) try: - _exchange_amt_account_link(body.token, response, gr_api, client_ip=client_ip) + _exchange_amt_account_link( + body.token, + response, + gr_api, + client_ip=client_ip, + background_tasks=background_tasks, + ) except ValueError as e: LOG.error(f"Failed to exchange AMT account link: {e}") raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) def _exchange_amt_account_link( - token: str, response: Response, gr_api: GRApiManager, client_ip: str + token: str, + response: Response, + gr_api: GRApiManager, + client_ip: str, + background_tasks: BackgroundTasks, ): token_data = consume_amt_account_link_token(token) email = token_data.email @@ -233,8 +290,11 @@ def _exchange_amt_account_link( user = User(email=email) user = gr_api.transition_user_from_amt(user=user, amt_worker_id=amt_worker_id) - create_paypal_cashout_method_if_not_exists( - product_user_id=user.product_user_id, email=user.email, client_ip=client_ip + background_tasks.add_task( + try_create_paypal_cashout_method_if_not_exists, + product_user_id=user.product_user_id, + email=str(user.email), + client_ip=client_ip, ) # In Mautic, associate the email with the worker ID (AFTER the user has transitioned) |
