TorbatYar/backend/services/payment/app/models/domain.py
Mortezakoohjani 9fac160258 feat(payment): ship Torbat Pay MVP phases 14.0-14.5
Add independent payment-service (port 8012, payment_db) with foundation licensing, BYO-PSP, merchant accounts, idempotent requests, callbacks, and immutable ledger.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-27 17:57:04 +03:30

118 lines
7.5 KiB
Python

from __future__ import annotations
import uuid
from datetime import datetime
from sqlalchemy import Boolean, DateTime, Integer, String, Text, UniqueConstraint, func
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy.types import JSON
from app.core.database import Base
from app.models.base import UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin, ActorAuditMixin
from app.models.types import GUID
class Standard(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin, ActorAuditMixin):
__abstract__ = True
code: Mapped[str | None] = mapped_column(String(120))
name: Mapped[str | None] = mapped_column(String(255))
status: Mapped[str] = mapped_column(String(40), default="active", nullable=False)
data: Mapped[dict] = mapped_column(JSON, default=dict, nullable=False)
class PaymentWorkspace(Standard):
__tablename__="payment_workspaces"
status: Mapped[str] = mapped_column(String(40), default="enabled", nullable=False)
default_payment_mode: Mapped[str] = mapped_column(String(40), default="byo_psp", nullable=False)
class PaymentBundleDefinition(Standard): __tablename__="payment_bundle_definitions"
class TenantPaymentBundle(Standard):
__tablename__="tenant_payment_bundles"
bundle_code: Mapped[str] = mapped_column(String(120), nullable=False)
is_active: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
__table_args__=(UniqueConstraint("tenant_id","bundle_code",name="uq_tenant_bundle"),)
class PaymentFeatureToggle(Standard):
__tablename__="payment_feature_toggles"
key: Mapped[str] = mapped_column(String(150), nullable=False)
enabled: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
class PaymentProviderAssignment(Standard): __tablename__="payment_provider_assignments"
class PspProviderRegistration(Standard):
__tablename__="psp_provider_registrations"
provider_code: Mapped[str] = mapped_column(String(80), nullable=False)
class CreditProviderRegistration(Standard):
__tablename__="credit_provider_registrations"
provider_code: Mapped[str] = mapped_column(String(80), nullable=False)
class PaymentConfiguration(Standard): __tablename__="payment_configurations"
class PaymentSetting(Standard):
__tablename__="payment_settings"
key: Mapped[str] = mapped_column(String(150), nullable=False)
value: Mapped[dict] = mapped_column(JSON, default=dict, nullable=False)
class PaymentRole(Standard): __tablename__="payment_roles"
class PaymentPermission(Standard): __tablename__="payment_permissions"
class PaymentAuditLog(Base, UUIDPrimaryKeyMixin, TenantMixin):
__tablename__="payment_audit_logs"
entity_type: Mapped[str] = mapped_column(String(100), nullable=False)
entity_id: Mapped[uuid.UUID | None] = mapped_column(GUID())
action: Mapped[str] = mapped_column(String(50), nullable=False)
actor_user_id: Mapped[str | None] = mapped_column(String(100))
changes: Mapped[dict] = mapped_column(JSON, default=dict, nullable=False)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False)
class OutboxEvent(Base, UUIDPrimaryKeyMixin, TenantMixin):
__tablename__="outbox_events"
event_type: Mapped[str] = mapped_column(String(150), nullable=False)
aggregate_type: Mapped[str] = mapped_column(String(100), nullable=False)
aggregate_id: Mapped[str] = mapped_column(String(100), nullable=False)
payload: Mapped[dict] = mapped_column(JSON, default=dict, nullable=False)
status: Mapped[str] = mapped_column(String(30), default="pending", nullable=False)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False)
class PspConnection(Standard):
__tablename__="psp_connections"
provider_code: Mapped[str] = mapped_column(String(80), nullable=False)
credential_vault_ref: Mapped[str | None] = mapped_column(String(255))
secret_config: Mapped[dict] = mapped_column(JSON, default=dict, nullable=False)
class PspCredentialVaultRef(Standard): __tablename__="psp_credential_vault_refs"
class PspConnectionHealthCheck(Standard): __tablename__="psp_connection_health_checks"
class PspRoutingPolicy(Standard): __tablename__="psp_routing_policies"
class MerchantAccount(Standard): __tablename__="merchant_accounts"
class MerchantAccountProfile(Standard): __tablename__="merchant_account_profiles"
class MerchantSettlementProfile(Standard): __tablename__="merchant_settlement_profiles"
class MerchantComplianceRef(Standard): __tablename__="merchant_compliance_refs"
class MerchantAccountStatusHistory(Standard): __tablename__="merchant_account_status_history"
class PaymentRequest(Standard):
__tablename__="payment_requests"
status: Mapped[str] = mapped_column(String(40), default="draft", nullable=False)
idempotency_key: Mapped[str] = mapped_column(String(200), nullable=False)
amount_minor: Mapped[int] = mapped_column(Integer, nullable=False)
currency: Mapped[str] = mapped_column(String(3), default="IRR", nullable=False)
payment_source: Mapped[str] = mapped_column(String(40), default="PSP", nullable=False)
provider_code: Mapped[str] = mapped_column(String(80), default="mock", nullable=False)
provider_transaction_id: Mapped[str | None] = mapped_column(String(200))
redirect_url: Mapped[str | None] = mapped_column(Text)
source_service: Mapped[str | None] = mapped_column(String(80))
source_ref_type: Mapped[str | None] = mapped_column(String(80))
source_ref_id: Mapped[str | None] = mapped_column(String(100))
credit_provider_id: Mapped[uuid.UUID | None] = mapped_column(GUID())
financing_reference: Mapped[str | None] = mapped_column(String(200))
credit_authorization_reference: Mapped[str | None] = mapped_column(String(200))
installment_contract_reference: Mapped[str | None] = mapped_column(String(200))
__table_args__=(UniqueConstraint("tenant_id","idempotency_key",name="uq_payment_request_idem"),)
class PaymentRequestStatusHistory(Standard): __tablename__="payment_request_status_history"
class PaymentRequestLine(Standard): __tablename__="payment_request_lines"
class PaymentCallbackLog(Standard):
__tablename__="payment_callback_logs"
connection_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
provider_code: Mapped[str] = mapped_column(String(80), nullable=False)
replay_key: Mapped[str] = mapped_column(String(250), nullable=False)
__table_args__=(UniqueConstraint("tenant_id","replay_key",name="uq_callback_replay"),)
class PaymentVerificationAttempt(Standard): __tablename__="payment_verification_attempts"
class PaymentTransaction(Standard):
__tablename__="payment_transactions"
payment_request_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
amount_minor: Mapped[int] = mapped_column(Integer, nullable=False)
currency: Mapped[str] = mapped_column(String(3), nullable=False)
provider_transaction_id: Mapped[str | None] = mapped_column(String(200))
__table_args__=(UniqueConstraint("tenant_id","payment_request_id",name="uq_transaction_request"),)
class PaymentTransactionFeeLine(Standard): __tablename__="payment_transaction_fee_lines"
class PaymentLedgerEntry(Standard):
__tablename__="payment_ledger_entries"
payment_transaction_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
amount_minor: Mapped[int] = mapped_column(Integer, nullable=False)
direction: Mapped[str] = mapped_column(String(10), nullable=False)