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>
118 lines
7.5 KiB
Python
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)
|