Coverage for polar/transaction/service/payment.py: 31%

50 statements  

« prev     ^ index     » next       coverage.py v7.10.6, created at 2025-12-05 16:17 +0000

1from typing import cast 1a

2from uuid import UUID 1a

3 

4import stripe as stripe_lib 1a

5from sqlalchemy import select 1a

6 

7from polar.integrations.stripe.schemas import ProductType 1a

8from polar.integrations.stripe.service import stripe as stripe_service 1a

9from polar.integrations.stripe.utils import get_expandable_id 1a

10from polar.models import Pledge, Transaction 1a

11from polar.models.transaction import Processor, TransactionType 1a

12from polar.postgres import AsyncSession 1a

13from polar.worker import enqueue_job 1a

14 

15from .base import BaseTransactionService, BaseTransactionServiceError 1a

16 

17 

18class PaymentTransactionError(BaseTransactionServiceError): ... 1a

19 

20 

21class PaymentTransactionService(BaseTransactionService): 1a

22 async def get_by_charge_id( 1a

23 self, session: AsyncSession, charge_id: str 

24 ) -> Transaction | None: 

25 statement = select(Transaction).where( 

26 Transaction.type == TransactionType.payment, 

27 Transaction.charge_id == charge_id, 

28 ) 

29 result = await session.execute(statement) 

30 return result.scalar_one_or_none() 

31 

32 async def get_by_order_id( 1a

33 self, session: AsyncSession, order_id: UUID 

34 ) -> Transaction | None: 

35 return await self.get_by( 

36 session, 

37 type=TransactionType.payment, 

38 order_id=order_id, 

39 ) 

40 

41 async def create_payment( 1a

42 self, session: AsyncSession, *, charge: stripe_lib.Charge 

43 ) -> Transaction: 

44 pledge: Pledge | None = None 

45 

46 # Make sure we don't already have this transaction 

47 existing_transaction = await self.get_by_charge_id(session, charge.id) 

48 if existing_transaction is not None: 

49 return existing_transaction 

50 

51 # Retrieve tax amount and country 

52 tax_amount = 0 

53 tax_country = None 

54 tax_state = None 

55 pledge_invoice = False 

56 # Polar Custom Checkout sets tax info in metadata 

57 if "tax_amount" in charge.metadata: 

58 tax_amount = int(charge.metadata["tax_amount"]) 

59 tax_country = charge.metadata["tax_country"] 

60 tax_state = charge.metadata.get("tax_state") 

61 # Stripe Checkout sets tax info in invoice 

62 elif charge.invoice: 

63 stripe_invoice = await stripe_service.get_invoice( 

64 get_expandable_id(charge.invoice) 

65 ) 

66 if stripe_invoice.tax is not None: 

67 tax_amount = stripe_invoice.tax 

68 for total_tax_amount in stripe_invoice.total_tax_amounts: 

69 tax_rate = cast(stripe_lib.TaxRate, total_tax_amount.tax_rate) 

70 tax_country = tax_rate.country 

71 tax_state = tax_rate.state 

72 

73 if ( 

74 stripe_invoice.metadata 

75 and stripe_invoice.metadata.get("type") == ProductType.pledge 

76 ): 

77 pledge_invoice = True 

78 

79 risk = getattr(charge, "outcome", {}) 

80 transaction = Transaction( 

81 type=TransactionType.payment, 

82 processor=Processor.stripe, 

83 currency=charge.currency, 

84 amount=charge.amount - tax_amount, 

85 account_currency=charge.currency, 

86 account_amount=charge.amount - tax_amount, 

87 tax_amount=tax_amount, 

88 tax_country=tax_country, 

89 tax_state=tax_state if tax_country in {"US", "CA"} else None, 

90 presentment_currency=charge.currency, 

91 presentment_amount=charge.amount - tax_amount, 

92 presentment_tax_amount=tax_amount, 

93 customer_id=get_expandable_id(charge.customer) if charge.customer else None, 

94 charge_id=charge.id, 

95 pledge=pledge, 

96 risk_level=risk.get("risk_level"), 

97 risk_score=risk.get("risk_score"), 

98 # Filled when we handle the invoice 

99 order=None, 

100 payment_customer=None, 

101 # Legacy fields for pledges 

102 payment_organization=None, 

103 payment_user=None, 

104 ) 

105 

106 session.add(transaction) 

107 await session.flush() 

108 

109 # Enqueue fees creation 

110 enqueue_job("processor_fee.create_payment_fees", transaction.id) 

111 

112 return transaction 

113 

114 

115payment_transaction = PaymentTransactionService(Transaction) 1a