Coverage for polar/payout/tasks.py: 38%
53 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 12:42 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 12:42 +0000
1import uuid
3import sentry_sdk
4import stripe as stripe_lib
5import structlog
7from polar.enums import AccountType
8from polar.exceptions import PolarTaskError
9from polar.logging import Logger
10from polar.worker import AsyncSessionMaker, CronTrigger, TaskPriority, actor
12from .repository import PayoutRepository
13from .service import PayoutAlreadyTriggered
14from .service import payout as payout_service
16log: Logger = structlog.get_logger()
19class PayoutTaskError(PolarTaskError): ... 19 ↛ 22line 19 didn't jump to line 22 because
22class PayoutDoesNotExist(PayoutTaskError):
23 def __init__(self, payout_id: uuid.UUID) -> None:
24 self.payout_id = payout_id
25 message = f"The payout with id {payout_id} does not exist."
26 super().__init__(message)
29@actor(actor_name="payout.created", priority=TaskPriority.LOW)
30async def payout_created(payout_id: uuid.UUID) -> None:
31 async with AsyncSessionMaker() as session:
32 repository = PayoutRepository(session)
33 payout = await repository.get_by_id(
34 payout_id, options=repository.get_eager_options()
35 )
36 if payout is None:
37 raise PayoutDoesNotExist(payout_id)
39 if payout.processor == AccountType.stripe:
40 await payout_service.transfer_stripe(session, payout)
43@actor(
44 actor_name="payout.trigger_stripe_payouts",
45 cron_trigger=CronTrigger(minute=15),
46 priority=TaskPriority.LOW,
47)
48async def trigger_stripe_payouts() -> None:
49 async with AsyncSessionMaker() as session:
50 await payout_service.trigger_stripe_payouts(session)
53@actor(actor_name="payout.trigger_stripe_payout", priority=TaskPriority.LOW)
54async def trigger_payout(payout_id: uuid.UUID) -> None:
55 async with AsyncSessionMaker() as session:
56 repository = PayoutRepository(session)
57 payout = await repository.get_by_id(
58 payout_id, options=repository.get_eager_options()
59 )
60 if payout is None:
61 raise PayoutDoesNotExist(payout_id)
63 try:
64 await payout_service.trigger_stripe_payout(session, payout)
65 except PayoutAlreadyTriggered:
66 # Swallow it, since it's likely a task that's being retried
67 # while the payout has already been triggered.
68 pass
69 except stripe_lib.InvalidRequestError as e:
70 # Capture exception in Sentry for debugging purposes
71 sentry_sdk.capture_exception(
72 e,
73 extras={"payout_id": str(payout_id)},
74 )
75 # Do not raise an error here: we know it happens often, because Stripe
76 # has many hidden rules on payout creation that we cannot control.
77 pass
80@actor(actor_name="payout.invoice", priority=TaskPriority.LOW)
81async def order_invoice(payout_id: uuid.UUID) -> None:
82 async with AsyncSessionMaker() as session:
83 repository = PayoutRepository(session)
84 payout = await repository.get_by_id(
85 payout_id, options=repository.get_eager_options()
86 )
87 if payout is None:
88 raise PayoutDoesNotExist(payout_id)
90 await payout_service.generate_invoice(session, payout)