Coverage for polar/models/processor_transaction.py: 96%
28 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 datetime
2from enum import StrEnum
3from typing import Any, Self
4from uuid import UUID
6import stripe as stripe_lib
7from sqlalchemy import TIMESTAMP, BigInteger, String, Text, Uuid
8from sqlalchemy.dialects.postgresql import JSONB
9from sqlalchemy.orm import Mapped, mapped_column
11from polar.kit.db.models import Model
12from polar.kit.extensions.sqlalchemy.types import StringEnum
13from polar.kit.utils import generate_uuid, utc_now
16class Processor(StrEnum):
17 """
18 Supported payment or payout processors, i.e rails for transactions.
19 """
21 stripe = "stripe"
24class ProcessorTransaction(Model):
25 __tablename__ = "processor_transactions"
27 id: Mapped[UUID] = mapped_column(Uuid, primary_key=True, default=generate_uuid)
28 timestamp: Mapped[datetime.datetime] = mapped_column(
29 TIMESTAMP(timezone=True), nullable=False, default=utc_now, index=True
30 )
31 processor: Mapped[Processor] = mapped_column(
32 StringEnum(Processor), index=True, nullable=False
33 )
34 processor_id: Mapped[str] = mapped_column(
35 String, nullable=False, index=True, unique=True
36 )
37 type: Mapped[str] = mapped_column(String, nullable=False, index=True)
38 currency: Mapped[str] = mapped_column(String(3), nullable=False)
39 amount: Mapped[int] = mapped_column(BigInteger, nullable=False)
40 fee: Mapped[int] = mapped_column(BigInteger, nullable=False)
41 description: Mapped[str | None] = mapped_column(Text, nullable=True)
42 raw: Mapped[dict[str, Any]] = mapped_column(JSONB, nullable=False, default=dict)
44 @classmethod
45 def from_stripe(cls, bt: stripe_lib.BalanceTransaction) -> Self:
46 return cls(
47 timestamp=datetime.datetime.fromtimestamp(bt.created, tz=datetime.UTC),
48 processor=Processor.stripe,
49 processor_id=bt.id,
50 type=bt.type,
51 currency=bt.currency,
52 amount=bt.amount,
53 fee=bt.fee,
54 description=bt.description,
55 raw=bt,
56 )