```
chore: remove obsolete payment architecture and integration test documentation - Remove AITBC_PAYMENT_ARCHITECTURE.md (dual-currency system documentation) - Remove IMPLEMENTATION_COMPLETE_SUMMARY.md (integration test completion summary) - Remove INTEGRATION_TEST_FIXES.md (test fixes documentation) - Remove INTEGRATION_TEST_UPDATES.md (real features implementation notes) - Remove PAYMENT_INTEGRATION_COMPLETE.md (wallet-coordinator integration docs) - Remove WALLET_COORDINATOR_INTEGRATION.md (payment
This commit is contained in:
@@ -3,8 +3,9 @@
|
||||
from .job import Job
|
||||
from .miner import Miner
|
||||
from .job_receipt import JobReceipt
|
||||
from .marketplace import MarketplaceOffer, MarketplaceBid, OfferStatus
|
||||
from .marketplace import MarketplaceOffer, MarketplaceBid
|
||||
from .user import User, Wallet
|
||||
from .payment import JobPayment, PaymentEscrow
|
||||
|
||||
__all__ = [
|
||||
"Job",
|
||||
@@ -12,7 +13,8 @@ __all__ = [
|
||||
"JobReceipt",
|
||||
"MarketplaceOffer",
|
||||
"MarketplaceBid",
|
||||
"OfferStatus",
|
||||
"User",
|
||||
"Wallet",
|
||||
"JobPayment",
|
||||
"PaymentEscrow",
|
||||
]
|
||||
|
||||
@@ -4,17 +4,18 @@ from datetime import datetime
|
||||
from typing import Optional
|
||||
from uuid import uuid4
|
||||
|
||||
from sqlalchemy import Column, JSON, String
|
||||
from sqlmodel import Field, SQLModel, Relationship
|
||||
|
||||
from ..types import JobState
|
||||
from sqlalchemy import Column, JSON, String, ForeignKey
|
||||
from sqlalchemy.orm import Mapped, relationship
|
||||
from sqlmodel import Field, SQLModel
|
||||
|
||||
|
||||
class Job(SQLModel, table=True):
|
||||
__tablename__ = "job"
|
||||
|
||||
id: str = Field(default_factory=lambda: uuid4().hex, primary_key=True, index=True)
|
||||
client_id: str = Field(index=True)
|
||||
|
||||
state: JobState = Field(default=JobState.queued, sa_column_kwargs={"nullable": False})
|
||||
state: str = Field(default="QUEUED", max_length=20)
|
||||
payload: dict = Field(sa_column=Column(JSON, nullable=False))
|
||||
constraints: dict = Field(default_factory=dict, sa_column=Column(JSON, nullable=False))
|
||||
|
||||
@@ -30,8 +31,8 @@ class Job(SQLModel, table=True):
|
||||
error: Optional[str] = None
|
||||
|
||||
# Payment tracking
|
||||
payment_id: Optional[str] = Field(default=None, foreign_key="job_payments.id", index=True)
|
||||
payment_id: Optional[str] = Field(default=None, sa_column=Column(String, ForeignKey("job_payments.id"), index=True))
|
||||
payment_status: Optional[str] = Field(default=None, max_length=20) # pending, escrowed, released, refunded
|
||||
|
||||
# Relationships
|
||||
payment: Optional["JobPayment"] = Relationship(back_populates="jobs")
|
||||
# payment: Mapped[Optional["JobPayment"]] = relationship(back_populates="jobs")
|
||||
|
||||
@@ -1,27 +1,20 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
from typing import Optional
|
||||
from uuid import uuid4
|
||||
|
||||
from sqlalchemy import Column, Enum as SAEnum, JSON
|
||||
from sqlalchemy import Column, JSON
|
||||
from sqlmodel import Field, SQLModel
|
||||
|
||||
|
||||
class OfferStatus(str, Enum):
|
||||
open = "open"
|
||||
reserved = "reserved"
|
||||
closed = "closed"
|
||||
|
||||
|
||||
class MarketplaceOffer(SQLModel, table=True):
|
||||
id: str = Field(default_factory=lambda: uuid4().hex, primary_key=True)
|
||||
provider: str = Field(index=True)
|
||||
capacity: int = Field(default=0, nullable=False)
|
||||
price: float = Field(default=0.0, nullable=False)
|
||||
sla: str = Field(default="")
|
||||
status: OfferStatus = Field(default=OfferStatus.open, sa_column=Column(SAEnum(OfferStatus), nullable=False))
|
||||
status: str = Field(default="open", max_length=20)
|
||||
created_at: datetime = Field(default_factory=datetime.utcnow, nullable=False, index=True)
|
||||
attributes: dict = Field(default_factory=dict, sa_column=Column(JSON, nullable=False))
|
||||
|
||||
|
||||
@@ -6,10 +6,9 @@ from datetime import datetime
|
||||
from typing import Optional, List
|
||||
from uuid import uuid4
|
||||
|
||||
from sqlalchemy import Column, String, DateTime, Numeric, ForeignKey
|
||||
from sqlmodel import Field, SQLModel, Relationship
|
||||
|
||||
from ..schemas.payments import PaymentStatus, PaymentMethod
|
||||
from sqlalchemy import Column, String, DateTime, Numeric, ForeignKey, JSON
|
||||
from sqlalchemy.orm import Mapped, relationship
|
||||
from sqlmodel import Field, SQLModel
|
||||
|
||||
|
||||
class JobPayment(SQLModel, table=True):
|
||||
@@ -23,8 +22,8 @@ class JobPayment(SQLModel, table=True):
|
||||
# Payment details
|
||||
amount: float = Field(sa_column=Column(Numeric(20, 8), nullable=False))
|
||||
currency: str = Field(default="AITBC", max_length=10)
|
||||
status: PaymentStatus = Field(default=PaymentStatus.PENDING)
|
||||
payment_method: PaymentMethod = Field(default=PaymentMethod.AITBC_TOKEN)
|
||||
status: str = Field(default="pending", max_length=20)
|
||||
payment_method: str = Field(default="aitbc_token", max_length=20)
|
||||
|
||||
# Addresses
|
||||
escrow_address: Optional[str] = Field(default=None, max_length=100)
|
||||
@@ -43,10 +42,10 @@ class JobPayment(SQLModel, table=True):
|
||||
expires_at: Optional[datetime] = None
|
||||
|
||||
# Additional metadata
|
||||
metadata: Optional[dict] = Field(default=None)
|
||||
meta_data: Optional[dict] = Field(default=None, sa_column=Column(JSON))
|
||||
|
||||
# Relationships
|
||||
jobs: List["Job"] = Relationship(back_populates="payment")
|
||||
# jobs: Mapped[List["Job"]] = relationship(back_populates="payment")
|
||||
|
||||
|
||||
class PaymentEscrow(SQLModel, table=True):
|
||||
|
||||
@@ -6,13 +6,6 @@ from sqlmodel import SQLModel, Field, Relationship, Column
|
||||
from sqlalchemy import JSON
|
||||
from datetime import datetime
|
||||
from typing import Optional, List
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class UserStatus(str, Enum):
|
||||
ACTIVE = "active"
|
||||
INACTIVE = "inactive"
|
||||
SUSPENDED = "suspended"
|
||||
|
||||
|
||||
class User(SQLModel, table=True):
|
||||
@@ -20,7 +13,7 @@ class User(SQLModel, table=True):
|
||||
id: str = Field(primary_key=True)
|
||||
email: str = Field(unique=True, index=True)
|
||||
username: str = Field(unique=True, index=True)
|
||||
status: UserStatus = Field(default=UserStatus.ACTIVE)
|
||||
status: str = Field(default="active", max_length=20)
|
||||
created_at: datetime = Field(default_factory=datetime.utcnow)
|
||||
updated_at: datetime = Field(default_factory=datetime.utcnow)
|
||||
last_login: Optional[datetime] = None
|
||||
@@ -44,28 +37,13 @@ class Wallet(SQLModel, table=True):
|
||||
transactions: List["Transaction"] = Relationship(back_populates="wallet")
|
||||
|
||||
|
||||
class TransactionType(str, Enum):
|
||||
DEPOSIT = "deposit"
|
||||
WITHDRAWAL = "withdrawal"
|
||||
PURCHASE = "purchase"
|
||||
REWARD = "reward"
|
||||
REFUND = "refund"
|
||||
|
||||
|
||||
class TransactionStatus(str, Enum):
|
||||
PENDING = "pending"
|
||||
COMPLETED = "completed"
|
||||
FAILED = "failed"
|
||||
CANCELLED = "cancelled"
|
||||
|
||||
|
||||
class Transaction(SQLModel, table=True):
|
||||
"""Transaction model"""
|
||||
id: str = Field(primary_key=True)
|
||||
user_id: str = Field(foreign_key="user.id")
|
||||
wallet_id: Optional[int] = Field(foreign_key="wallet.id")
|
||||
type: TransactionType
|
||||
status: TransactionStatus = Field(default=TransactionStatus.PENDING)
|
||||
type: str = Field(max_length=20)
|
||||
status: str = Field(default="pending", max_length=20)
|
||||
amount: float
|
||||
fee: float = Field(default=0.0)
|
||||
description: Optional[str] = None
|
||||
|
||||
@@ -56,6 +56,8 @@ from ..domain import (
|
||||
MarketplaceBid,
|
||||
User,
|
||||
Wallet,
|
||||
JobPayment,
|
||||
PaymentEscrow,
|
||||
)
|
||||
|
||||
# Service-specific models
|
||||
@@ -101,4 +103,6 @@ __all__ = [
|
||||
"LLMRequest",
|
||||
"FFmpegRequest",
|
||||
"BlenderRequest",
|
||||
"JobPayment",
|
||||
"PaymentEscrow",
|
||||
]
|
||||
|
||||
@@ -4,7 +4,7 @@ Service schemas for common GPU workloads
|
||||
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
from enum import Enum
|
||||
from pydantic import BaseModel, Field, validator
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
import re
|
||||
|
||||
|
||||
@@ -123,7 +123,8 @@ class StableDiffusionRequest(BaseModel):
|
||||
lora: Optional[str] = Field(None, description="LoRA model to use")
|
||||
lora_scale: float = Field(1.0, ge=0.0, le=2.0, description="LoRA strength")
|
||||
|
||||
@validator('seed')
|
||||
@field_validator('seed')
|
||||
@classmethod
|
||||
def validate_seed(cls, v):
|
||||
if v is not None and isinstance(v, list):
|
||||
if len(v) > 4:
|
||||
@@ -289,9 +290,10 @@ class BlenderRequest(BaseModel):
|
||||
transparent: bool = Field(False, description="Transparent background")
|
||||
custom_args: Optional[List[str]] = Field(None, description="Custom Blender arguments")
|
||||
|
||||
@validator('frame_end')
|
||||
def validate_frame_range(cls, v, values):
|
||||
if 'frame_start' in values and v < values['frame_start']:
|
||||
@field_validator('frame_end')
|
||||
@classmethod
|
||||
def validate_frame_range(cls, v, info):
|
||||
if info and info.data and 'frame_start' in info.data and v < info.data['frame_start']:
|
||||
raise ValueError("frame_end must be >= frame_start")
|
||||
return v
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
|
||||
from ..deps import require_client_key
|
||||
from ..schemas import JobCreate, JobView, JobResult
|
||||
from ..schemas.payments import JobPaymentCreate, PaymentMethod
|
||||
from ..schemas import JobCreate, JobView, JobResult, JobPaymentCreate
|
||||
from ..types import JobState
|
||||
from ..services import JobService
|
||||
from ..services.payments import PaymentService
|
||||
@@ -27,11 +26,11 @@ async def submit_job(
|
||||
job_id=job.id,
|
||||
amount=req.payment_amount,
|
||||
currency=req.payment_currency,
|
||||
payment_method=PaymentMethod.AITBC_TOKEN # Jobs use AITBC tokens
|
||||
payment_method="aitbc_token" # Jobs use AITBC tokens
|
||||
)
|
||||
payment = await payment_service.create_payment(job.id, payment_create)
|
||||
job.payment_id = payment.id
|
||||
job.payment_status = payment.status.value
|
||||
job.payment_status = payment.status
|
||||
session.commit()
|
||||
session.refresh(job)
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlmodel import Session, select
|
||||
|
||||
from ..deps import require_admin_key
|
||||
from ..domain import MarketplaceOffer, Miner, OfferStatus
|
||||
from ..domain import MarketplaceOffer, Miner
|
||||
from ..schemas import MarketplaceOfferView
|
||||
from ..storage import SessionDep
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
import logging
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Response, status
|
||||
|
||||
@@ -9,6 +10,8 @@ from ..services import JobService, MinerService
|
||||
from ..services.receipts import ReceiptService
|
||||
from ..storage import SessionDep
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
router = APIRouter(tags=["miner"])
|
||||
|
||||
|
||||
@@ -78,6 +81,23 @@ async def submit_result(
|
||||
job.receipt_id = receipt["receipt_id"] if receipt else None
|
||||
session.add(job)
|
||||
session.commit()
|
||||
|
||||
# Auto-release payment if job has payment
|
||||
if job.payment_id and job.payment_status == "escrowed":
|
||||
from ..services.payments import PaymentService
|
||||
payment_service = PaymentService(session)
|
||||
success = await payment_service.release_payment(
|
||||
job.id,
|
||||
job.payment_id,
|
||||
reason="Job completed successfully"
|
||||
)
|
||||
if success:
|
||||
job.payment_status = "released"
|
||||
session.commit()
|
||||
logger.info(f"Auto-released payment {job.payment_id} for completed job {job.id}")
|
||||
else:
|
||||
logger.error(f"Failed to auto-release payment {job.payment_id} for job {job.id}")
|
||||
|
||||
miner_service.release(
|
||||
miner_id,
|
||||
success=True,
|
||||
@@ -106,5 +126,22 @@ async def submit_failure(
|
||||
job.assigned_miner_id = miner_id
|
||||
session.add(job)
|
||||
session.commit()
|
||||
|
||||
# Auto-refund payment if job has payment
|
||||
if job.payment_id and job.payment_status in ["pending", "escrowed"]:
|
||||
from ..services.payments import PaymentService
|
||||
payment_service = PaymentService(session)
|
||||
success = await payment_service.refund_payment(
|
||||
job.id,
|
||||
job.payment_id,
|
||||
reason=f"Job failed: {req.error_code}: {req.error_message}"
|
||||
)
|
||||
if success:
|
||||
job.payment_status = "refunded"
|
||||
session.commit()
|
||||
logger.info(f"Auto-refunded payment {job.payment_id} for failed job {job.id}")
|
||||
else:
|
||||
logger.error(f"Failed to auto-refund payment {job.payment_id} for job {job.id}")
|
||||
|
||||
miner_service.release(miner_id, success=False)
|
||||
return {"status": "ok"}
|
||||
|
||||
@@ -37,7 +37,7 @@ class PartnerResponse(BaseModel):
|
||||
class WebhookCreate(BaseModel):
|
||||
"""Create a webhook subscription"""
|
||||
url: str = Field(..., pattern=r'^https?://')
|
||||
events: List[str] = Field(..., min_items=1)
|
||||
events: List[str] = Field(..., min_length=1)
|
||||
secret: Optional[str] = Field(max_length=100)
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from typing import List
|
||||
|
||||
from ..deps import require_client_key
|
||||
from ..schemas.payments import (
|
||||
from ..schemas import (
|
||||
JobPaymentCreate,
|
||||
JobPaymentView,
|
||||
PaymentRequest,
|
||||
|
||||
@@ -3,12 +3,75 @@ from __future__ import annotations
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict, Optional, List
|
||||
from base64 import b64encode, b64decode
|
||||
from enum import Enum
|
||||
|
||||
from pydantic import BaseModel, Field, ConfigDict
|
||||
|
||||
from .types import JobState, Constraints
|
||||
|
||||
|
||||
# Payment schemas
|
||||
class JobPaymentCreate(BaseModel):
|
||||
"""Request to create a payment for a job"""
|
||||
job_id: str
|
||||
amount: float
|
||||
currency: str = "AITBC" # Jobs paid with AITBC tokens
|
||||
payment_method: str = "aitbc_token" # Primary method for job payments
|
||||
escrow_timeout_seconds: int = 3600 # 1 hour default
|
||||
|
||||
|
||||
class JobPaymentView(BaseModel):
|
||||
"""Payment information for a job"""
|
||||
job_id: str
|
||||
payment_id: str
|
||||
amount: float
|
||||
currency: str
|
||||
status: str
|
||||
payment_method: str
|
||||
escrow_address: Optional[str] = None
|
||||
refund_address: Optional[str] = None
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
released_at: Optional[datetime] = None
|
||||
refunded_at: Optional[datetime] = None
|
||||
transaction_hash: Optional[str] = None
|
||||
refund_transaction_hash: Optional[str] = None
|
||||
|
||||
|
||||
class PaymentRequest(BaseModel):
|
||||
"""Request to pay for a job"""
|
||||
job_id: str
|
||||
amount: float
|
||||
currency: str = "BTC"
|
||||
refund_address: Optional[str] = None
|
||||
|
||||
|
||||
class PaymentReceipt(BaseModel):
|
||||
"""Receipt for a payment"""
|
||||
payment_id: str
|
||||
job_id: str
|
||||
amount: float
|
||||
currency: str
|
||||
status: str
|
||||
transaction_hash: Optional[str] = None
|
||||
created_at: datetime
|
||||
verified_at: Optional[datetime] = None
|
||||
|
||||
|
||||
class EscrowRelease(BaseModel):
|
||||
"""Request to release escrow payment"""
|
||||
job_id: str
|
||||
payment_id: str
|
||||
reason: Optional[str] = None
|
||||
|
||||
|
||||
class RefundRequest(BaseModel):
|
||||
"""Request to refund a payment"""
|
||||
job_id: str
|
||||
payment_id: str
|
||||
reason: str
|
||||
|
||||
|
||||
# User management schemas
|
||||
class UserCreate(BaseModel):
|
||||
email: str
|
||||
|
||||
@@ -4,32 +4,16 @@ from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Optional, Dict, Any
|
||||
from enum import Enum
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class PaymentStatus(str, Enum):
|
||||
"""Payment status values"""
|
||||
PENDING = "pending"
|
||||
ESCROWED = "escrowed"
|
||||
RELEASED = "released"
|
||||
REFUNDED = "refunded"
|
||||
FAILED = "failed"
|
||||
|
||||
|
||||
class PaymentMethod(str, Enum):
|
||||
"""Payment methods"""
|
||||
AITBC_TOKEN = "aitbc_token" # Primary method for job payments
|
||||
BITCOIN = "bitcoin" # Only for exchange purchases
|
||||
|
||||
|
||||
class JobPaymentCreate(BaseModel):
|
||||
"""Request to create a payment for a job"""
|
||||
job_id: str
|
||||
amount: float
|
||||
currency: str = "AITBC" # Jobs paid with AITBC tokens
|
||||
payment_method: PaymentMethod = PaymentMethod.AITBC_TOKEN
|
||||
payment_method: str = "aitbc_token" # Primary method for job payments
|
||||
escrow_timeout_seconds: int = 3600 # 1 hour default
|
||||
|
||||
|
||||
@@ -39,8 +23,8 @@ class JobPaymentView(BaseModel):
|
||||
payment_id: str
|
||||
amount: float
|
||||
currency: str
|
||||
status: PaymentStatus
|
||||
payment_method: PaymentMethod
|
||||
status: str
|
||||
payment_method: str
|
||||
escrow_address: Optional[str] = None
|
||||
refund_address: Optional[str] = None
|
||||
created_at: datetime
|
||||
@@ -65,7 +49,7 @@ class PaymentReceipt(BaseModel):
|
||||
job_id: str
|
||||
amount: float
|
||||
currency: str
|
||||
status: PaymentStatus
|
||||
status: str
|
||||
transaction_hash: Optional[str] = None
|
||||
created_at: datetime
|
||||
verified_at: Optional[datetime] = None
|
||||
|
||||
@@ -32,15 +32,8 @@ class JobService:
|
||||
|
||||
# Create payment if amount is specified
|
||||
if req.payment_amount and req.payment_amount > 0:
|
||||
from ..schemas.payments import JobPaymentCreate, PaymentMethod
|
||||
payment_create = JobPaymentCreate(
|
||||
job_id=job.id,
|
||||
amount=req.payment_amount,
|
||||
currency=req.payment_currency,
|
||||
payment_method=PaymentMethod.BITCOIN
|
||||
)
|
||||
# Note: This is async, so we'll handle it in the router
|
||||
job.payment_pending = True
|
||||
# Note: Payment creation is handled in the router
|
||||
pass
|
||||
|
||||
return job
|
||||
|
||||
@@ -81,6 +74,8 @@ class JobService:
|
||||
requested_at=job.requested_at,
|
||||
expires_at=job.expires_at,
|
||||
error=job.error,
|
||||
payment_id=job.payment_id,
|
||||
payment_status=job.payment_status,
|
||||
)
|
||||
|
||||
def to_result(self, job: Job) -> JobResult:
|
||||
|
||||
@@ -5,7 +5,7 @@ from typing import Iterable, Optional
|
||||
|
||||
from sqlmodel import Session, select
|
||||
|
||||
from ..domain import MarketplaceOffer, MarketplaceBid, OfferStatus
|
||||
from ..domain import MarketplaceOffer, MarketplaceBid
|
||||
from ..schemas import (
|
||||
MarketplaceBidRequest,
|
||||
MarketplaceOfferView,
|
||||
@@ -62,7 +62,7 @@ class MarketplaceService:
|
||||
|
||||
def get_stats(self) -> MarketplaceStatsView:
|
||||
offers = self.session.exec(select(MarketplaceOffer)).all()
|
||||
open_offers = [offer for offer in offers if offer.status == OfferStatus.open]
|
||||
open_offers = [offer for offer in offers if offer.status == "open"]
|
||||
|
||||
total_offers = len(offers)
|
||||
open_capacity = sum(offer.capacity for offer in open_offers)
|
||||
|
||||
@@ -6,11 +6,9 @@ import httpx
|
||||
import logging
|
||||
|
||||
from ..domain.payment import JobPayment, PaymentEscrow
|
||||
from ..schemas.payments import (
|
||||
from ..schemas import (
|
||||
JobPaymentCreate,
|
||||
JobPaymentView,
|
||||
PaymentStatus,
|
||||
PaymentMethod,
|
||||
EscrowRelease,
|
||||
RefundRequest
|
||||
)
|
||||
@@ -44,10 +42,10 @@ class PaymentService:
|
||||
self.session.refresh(payment)
|
||||
|
||||
# For AITBC token payments, use token escrow
|
||||
if payment_data.payment_method == PaymentMethod.AITBC_TOKEN:
|
||||
if payment_data.payment_method == "aitbc_token":
|
||||
await self._create_token_escrow(payment)
|
||||
# Bitcoin payments only for exchange purchases
|
||||
elif payment_data.payment_method == PaymentMethod.BITCOIN:
|
||||
elif payment_data.payment_method == "bitcoin":
|
||||
await self._create_bitcoin_escrow(payment)
|
||||
|
||||
return payment
|
||||
@@ -61,7 +59,7 @@ class PaymentService:
|
||||
response = await client.post(
|
||||
f"{self.exchange_base_url}/api/v1/token/escrow/create",
|
||||
json={
|
||||
"amount": payment.amount,
|
||||
"amount": float(payment.amount),
|
||||
"currency": payment.currency,
|
||||
"job_id": payment.job_id,
|
||||
"timeout_seconds": 3600 # 1 hour
|
||||
@@ -71,7 +69,7 @@ class PaymentService:
|
||||
if response.status_code == 200:
|
||||
escrow_data = response.json()
|
||||
payment.escrow_address = escrow_data.get("escrow_id")
|
||||
payment.status = PaymentStatus.ESCROWED
|
||||
payment.status = "escrowed"
|
||||
payment.escrowed_at = datetime.utcnow()
|
||||
payment.updated_at = datetime.utcnow()
|
||||
|
||||
@@ -92,7 +90,7 @@ class PaymentService:
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error creating token escrow: {e}")
|
||||
payment.status = PaymentStatus.FAILED
|
||||
payment.status = "failed"
|
||||
payment.updated_at = datetime.utcnow()
|
||||
self.session.commit()
|
||||
|
||||
@@ -104,7 +102,7 @@ class PaymentService:
|
||||
response = await client.post(
|
||||
f"{self.wallet_base_url}/api/v1/escrow/create",
|
||||
json={
|
||||
"amount": payment.amount,
|
||||
"amount": float(payment.amount),
|
||||
"currency": payment.currency,
|
||||
"timeout_seconds": 3600 # 1 hour
|
||||
}
|
||||
@@ -113,7 +111,7 @@ class PaymentService:
|
||||
if response.status_code == 200:
|
||||
escrow_data = response.json()
|
||||
payment.escrow_address = escrow_data["address"]
|
||||
payment.status = PaymentStatus.ESCROWED
|
||||
payment.status = "escrowed"
|
||||
payment.escrowed_at = datetime.utcnow()
|
||||
payment.updated_at = datetime.utcnow()
|
||||
|
||||
@@ -134,7 +132,7 @@ class PaymentService:
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error creating Bitcoin escrow: {e}")
|
||||
payment.status = PaymentStatus.FAILED
|
||||
payment.status = "failed"
|
||||
payment.updated_at = datetime.utcnow()
|
||||
self.session.commit()
|
||||
|
||||
@@ -145,7 +143,7 @@ class PaymentService:
|
||||
if not payment or payment.job_id != job_id:
|
||||
return False
|
||||
|
||||
if payment.status != PaymentStatus.ESCROWED:
|
||||
if payment.status != "escrowed":
|
||||
return False
|
||||
|
||||
try:
|
||||
@@ -161,7 +159,7 @@ class PaymentService:
|
||||
|
||||
if response.status_code == 200:
|
||||
release_data = response.json()
|
||||
payment.status = PaymentStatus.RELEASED
|
||||
payment.status = "released"
|
||||
payment.released_at = datetime.utcnow()
|
||||
payment.updated_at = datetime.utcnow()
|
||||
payment.transaction_hash = release_data.get("transaction_hash")
|
||||
@@ -195,7 +193,7 @@ class PaymentService:
|
||||
if not payment or payment.job_id != job_id:
|
||||
return False
|
||||
|
||||
if payment.status not in [PaymentStatus.ESCROWED, PaymentStatus.PENDING]:
|
||||
if payment.status not in ["escrowed", "pending"]:
|
||||
return False
|
||||
|
||||
try:
|
||||
@@ -206,14 +204,14 @@ class PaymentService:
|
||||
json={
|
||||
"payment_id": payment_id,
|
||||
"address": payment.refund_address,
|
||||
"amount": payment.amount,
|
||||
"amount": float(payment.amount),
|
||||
"reason": reason
|
||||
}
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
refund_data = response.json()
|
||||
payment.status = PaymentStatus.REFUNDED
|
||||
payment.status = "refunded"
|
||||
payment.refunded_at = datetime.utcnow()
|
||||
payment.updated_at = datetime.utcnow()
|
||||
payment.refund_transaction_hash = refund_data.get("transaction_hash")
|
||||
|
||||
@@ -8,7 +8,7 @@ from sqlalchemy.engine import Engine
|
||||
from sqlmodel import Session, SQLModel, create_engine
|
||||
|
||||
from ..config import settings
|
||||
from ..domain import Job, Miner, MarketplaceOffer, MarketplaceBid
|
||||
from ..domain import Job, Miner, MarketplaceOffer, MarketplaceBid, JobPayment, PaymentEscrow
|
||||
from .models_governance import GovernanceProposal, ProposalVote, TreasuryTransaction, GovernanceParameter
|
||||
|
||||
_engine: Engine | None = None
|
||||
|
||||
@@ -6,6 +6,7 @@ from sqlmodel import SQLModel, Field, Relationship, Column, JSON
|
||||
from typing import Optional, Dict, Any
|
||||
from datetime import datetime
|
||||
from uuid import uuid4
|
||||
from pydantic import ConfigDict
|
||||
|
||||
|
||||
class GovernanceProposal(SQLModel, table=True):
|
||||
@@ -83,10 +84,11 @@ class VotingPowerSnapshot(SQLModel, table=True):
|
||||
snapshot_time: datetime = Field(default_factory=datetime.utcnow, index=True)
|
||||
block_number: Optional[int] = Field(index=True)
|
||||
|
||||
class Config:
|
||||
indexes = [
|
||||
model_config = ConfigDict(
|
||||
indexes=[
|
||||
{"name": "ix_user_snapshot", "fields": ["user_id", "snapshot_time"]},
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class ProtocolUpgrade(SQLModel, table=True):
|
||||
|
||||
Reference in New Issue
Block a user