django-cfg 1.2.29__py3-none-any.whl → 1.2.31__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- django_cfg/__init__.py +1 -1
- django_cfg/apps/payments/admin/__init__.py +3 -2
- django_cfg/apps/payments/admin/balance_admin.py +18 -18
- django_cfg/apps/payments/admin/currencies_admin.py +319 -131
- django_cfg/apps/payments/admin/payments_admin.py +15 -4
- django_cfg/apps/payments/config/module.py +2 -2
- django_cfg/apps/payments/config/utils.py +2 -2
- django_cfg/apps/payments/decorators.py +2 -2
- django_cfg/apps/payments/management/commands/README.md +95 -127
- django_cfg/apps/payments/management/commands/currency_stats.py +5 -24
- django_cfg/apps/payments/management/commands/manage_currencies.py +229 -0
- django_cfg/apps/payments/management/commands/manage_providers.py +235 -0
- django_cfg/apps/payments/managers/__init__.py +3 -2
- django_cfg/apps/payments/managers/balance_manager.py +2 -2
- django_cfg/apps/payments/managers/currency_manager.py +272 -49
- django_cfg/apps/payments/managers/payment_manager.py +161 -13
- django_cfg/apps/payments/middleware/api_access.py +2 -2
- django_cfg/apps/payments/middleware/rate_limiting.py +8 -18
- django_cfg/apps/payments/middleware/usage_tracking.py +20 -17
- django_cfg/apps/payments/migrations/0002_network_providercurrency_and_more.py +241 -0
- django_cfg/apps/payments/migrations/0003_add_usd_rate_cache.py +30 -0
- django_cfg/apps/payments/models/__init__.py +3 -2
- django_cfg/apps/payments/models/currencies.py +187 -71
- django_cfg/apps/payments/models/payments.py +3 -2
- django_cfg/apps/payments/serializers/__init__.py +3 -2
- django_cfg/apps/payments/serializers/currencies.py +20 -12
- django_cfg/apps/payments/services/cache/simple_cache.py +2 -2
- django_cfg/apps/payments/services/core/balance_service.py +2 -2
- django_cfg/apps/payments/services/core/fallback_service.py +2 -2
- django_cfg/apps/payments/services/core/payment_service.py +3 -6
- django_cfg/apps/payments/services/core/subscription_service.py +4 -7
- django_cfg/apps/payments/services/internal_types.py +171 -7
- django_cfg/apps/payments/services/monitoring/api_schemas.py +58 -204
- django_cfg/apps/payments/services/monitoring/provider_health.py +2 -2
- django_cfg/apps/payments/services/providers/base.py +144 -43
- django_cfg/apps/payments/services/providers/cryptapi/__init__.py +4 -0
- django_cfg/apps/payments/services/providers/cryptapi/config.py +8 -0
- django_cfg/apps/payments/services/providers/cryptapi/models.py +192 -0
- django_cfg/apps/payments/services/providers/cryptapi/provider.py +439 -0
- django_cfg/apps/payments/services/providers/cryptomus/__init__.py +4 -0
- django_cfg/apps/payments/services/providers/cryptomus/models.py +176 -0
- django_cfg/apps/payments/services/providers/cryptomus/provider.py +429 -0
- django_cfg/apps/payments/services/providers/cryptomus/provider_v2.py +564 -0
- django_cfg/apps/payments/services/providers/models/__init__.py +34 -0
- django_cfg/apps/payments/services/providers/models/currencies.py +190 -0
- django_cfg/apps/payments/services/providers/nowpayments/__init__.py +4 -0
- django_cfg/apps/payments/services/providers/nowpayments/models.py +196 -0
- django_cfg/apps/payments/services/providers/nowpayments/provider.py +380 -0
- django_cfg/apps/payments/services/providers/registry.py +294 -11
- django_cfg/apps/payments/services/providers/stripe/__init__.py +4 -0
- django_cfg/apps/payments/services/providers/stripe/models.py +184 -0
- django_cfg/apps/payments/services/providers/stripe/provider.py +109 -0
- django_cfg/apps/payments/services/security/error_handler.py +6 -8
- django_cfg/apps/payments/services/security/payment_notifications.py +2 -2
- django_cfg/apps/payments/services/security/webhook_validator.py +3 -4
- django_cfg/apps/payments/signals/api_key_signals.py +2 -2
- django_cfg/apps/payments/signals/payment_signals.py +11 -5
- django_cfg/apps/payments/signals/subscription_signals.py +2 -2
- django_cfg/apps/payments/tasks/webhook_processing.py +2 -2
- django_cfg/apps/payments/templates/admin/payments/currency/change_list.html +50 -0
- django_cfg/apps/payments/templates/payments/base.html +4 -4
- django_cfg/apps/payments/templates/payments/components/payment_card.html +6 -6
- django_cfg/apps/payments/templates/payments/components/payment_qr_code.html +4 -4
- django_cfg/apps/payments/templates/payments/components/progress_bar.html +14 -7
- django_cfg/apps/payments/templates/payments/components/provider_stats.html +2 -2
- django_cfg/apps/payments/templates/payments/components/status_badge.html +8 -1
- django_cfg/apps/payments/templates/payments/components/status_overview.html +34 -30
- django_cfg/apps/payments/templates/payments/dashboard.html +202 -290
- django_cfg/apps/payments/templates/payments/dashboard_simple_test.html +35 -0
- django_cfg/apps/payments/templates/payments/payment_create.html +579 -0
- django_cfg/apps/payments/templates/payments/payment_detail.html +373 -0
- django_cfg/apps/payments/templates/payments/payment_list.html +354 -0
- django_cfg/apps/payments/templates/payments/stats.html +261 -0
- django_cfg/apps/payments/templates/payments/test.html +213 -0
- django_cfg/apps/payments/urls.py +3 -1
- django_cfg/apps/payments/{urls_templates.py → urls_admin.py} +6 -0
- django_cfg/apps/payments/utils/__init__.py +1 -3
- django_cfg/apps/payments/utils/billing_utils.py +2 -2
- django_cfg/apps/payments/utils/config_utils.py +2 -8
- django_cfg/apps/payments/utils/validation_utils.py +2 -2
- django_cfg/apps/payments/views/__init__.py +3 -2
- django_cfg/apps/payments/views/currency_views.py +31 -20
- django_cfg/apps/payments/views/payment_views.py +2 -2
- django_cfg/apps/payments/views/templates/ajax.py +141 -2
- django_cfg/apps/payments/views/templates/base.py +21 -13
- django_cfg/apps/payments/views/templates/payment_detail.py +1 -1
- django_cfg/apps/payments/views/templates/payment_management.py +34 -40
- django_cfg/apps/payments/views/templates/stats.py +8 -4
- django_cfg/apps/payments/views/webhook_views.py +2 -2
- django_cfg/apps/payments/viewsets.py +3 -2
- django_cfg/apps/tasks/urls.py +0 -2
- django_cfg/apps/tasks/urls_admin.py +14 -0
- django_cfg/apps/urls.py +4 -4
- django_cfg/core/config.py +35 -0
- django_cfg/models/payments.py +2 -8
- django_cfg/modules/django_currency/__init__.py +16 -11
- django_cfg/modules/django_currency/clients/__init__.py +4 -4
- django_cfg/modules/django_currency/clients/coinpaprika_client.py +289 -0
- django_cfg/modules/django_currency/clients/yahoo_client.py +157 -0
- django_cfg/modules/django_currency/core/__init__.py +1 -7
- django_cfg/modules/django_currency/core/converter.py +18 -23
- django_cfg/modules/django_currency/core/models.py +122 -11
- django_cfg/modules/django_currency/database/__init__.py +4 -4
- django_cfg/modules/django_currency/database/database_loader.py +190 -309
- django_cfg/modules/django_unfold/dashboard.py +7 -2
- django_cfg/template_archive/django_sample.zip +0 -0
- django_cfg/templates/admin/components/action_grid.html +9 -9
- django_cfg/templates/admin/components/metric_card.html +5 -5
- django_cfg/templates/admin/components/status_badge.html +2 -2
- django_cfg/templates/admin/layouts/dashboard_with_tabs.html +152 -24
- django_cfg/templates/admin/snippets/components/quick_actions.html +3 -3
- django_cfg/templates/admin/snippets/components/system_health.html +1 -1
- django_cfg/templates/admin/snippets/tabs/overview_tab.html +49 -52
- {django_cfg-1.2.29.dist-info → django_cfg-1.2.31.dist-info}/METADATA +2 -4
- {django_cfg-1.2.29.dist-info → django_cfg-1.2.31.dist-info}/RECORD +118 -96
- django_cfg/apps/payments/management/commands/populate_currencies.py +0 -246
- django_cfg/apps/payments/management/commands/update_currencies.py +0 -336
- django_cfg/apps/payments/services/providers/cryptapi.py +0 -273
- django_cfg/apps/payments/services/providers/cryptomus.py +0 -311
- django_cfg/apps/payments/services/providers/nowpayments.py +0 -293
- django_cfg/apps/payments/services/validators/__init__.py +0 -8
- django_cfg/modules/django_currency/clients/coingecko_client.py +0 -257
- django_cfg/modules/django_currency/clients/yfinance_client.py +0 -246
- {django_cfg-1.2.29.dist-info → django_cfg-1.2.31.dist-info}/WHEEL +0 -0
- {django_cfg-1.2.29.dist-info → django_cfg-1.2.31.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.2.29.dist-info → django_cfg-1.2.31.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,190 @@
|
|
1
|
+
"""
|
2
|
+
Provider-specific currency and network models.
|
3
|
+
|
4
|
+
These models are ONLY for provider-specific currency data,
|
5
|
+
NOT for universal service communication (those are in internal_types.py).
|
6
|
+
"""
|
7
|
+
|
8
|
+
from pydantic import BaseModel, Field, ConfigDict, field_validator
|
9
|
+
from decimal import Decimal
|
10
|
+
from typing import List, Dict, Optional, Any
|
11
|
+
from enum import Enum
|
12
|
+
|
13
|
+
|
14
|
+
class CurrencyType(str, Enum):
|
15
|
+
"""Currency type enumeration."""
|
16
|
+
FIAT = "fiat"
|
17
|
+
CRYPTO = "crypto"
|
18
|
+
|
19
|
+
|
20
|
+
class NetworkType(str, Enum):
|
21
|
+
"""Network type enumeration."""
|
22
|
+
MAINNET = "mainnet"
|
23
|
+
TESTNET = "testnet"
|
24
|
+
LAYER2 = "layer2"
|
25
|
+
SIDECHAIN = "sidechain"
|
26
|
+
|
27
|
+
|
28
|
+
class CurrencyInfo(BaseModel):
|
29
|
+
"""Information about a currency from provider API."""
|
30
|
+
model_config = ConfigDict(validate_assignment=True, extra="forbid")
|
31
|
+
|
32
|
+
code: str = Field(min_length=2, max_length=10, description="Currency code (BTC, ETH, USD)")
|
33
|
+
name: str = Field(min_length=1, max_length=100, description="Full currency name")
|
34
|
+
symbol: Optional[str] = Field(None, max_length=10, description="Currency symbol")
|
35
|
+
currency_type: CurrencyType = Field(description="Type of currency")
|
36
|
+
|
37
|
+
# Provider-specific data
|
38
|
+
min_amount: Optional[Decimal] = Field(None, ge=0, description="Minimum payment amount")
|
39
|
+
max_amount: Optional[Decimal] = Field(None, ge=0, description="Maximum payment amount")
|
40
|
+
precision: Optional[int] = Field(None, ge=0, le=18, description="Decimal precision")
|
41
|
+
|
42
|
+
# Exchange rate info
|
43
|
+
usd_rate: Optional[Decimal] = Field(None, gt=0, description="Rate to USD")
|
44
|
+
rate_updated_at: Optional[str] = Field(None, description="Rate update timestamp")
|
45
|
+
|
46
|
+
# Provider metadata
|
47
|
+
provider_metadata: Dict[str, Any] = Field(default_factory=dict, description="Provider-specific data")
|
48
|
+
|
49
|
+
@field_validator('code')
|
50
|
+
@classmethod
|
51
|
+
def code_must_be_uppercase(cls, v):
|
52
|
+
return v.upper() if v else v
|
53
|
+
|
54
|
+
@field_validator('max_amount')
|
55
|
+
@classmethod
|
56
|
+
def max_amount_must_be_greater_than_min(cls, v, info):
|
57
|
+
if v is not None and 'min_amount' in info.data and info.data['min_amount'] is not None:
|
58
|
+
if v <= info.data['min_amount']:
|
59
|
+
raise ValueError('max_amount must be greater than min_amount')
|
60
|
+
return v
|
61
|
+
|
62
|
+
|
63
|
+
class NetworkInfo(BaseModel):
|
64
|
+
"""Information about a blockchain network from provider API."""
|
65
|
+
model_config = ConfigDict(validate_assignment=True, extra="forbid")
|
66
|
+
|
67
|
+
code: str = Field(min_length=1, max_length=20, description="Network code")
|
68
|
+
name: str = Field(min_length=1, max_length=50, description="Network display name")
|
69
|
+
network_type: Optional[NetworkType] = Field(None, description="Type of network")
|
70
|
+
|
71
|
+
# Network-specific settings
|
72
|
+
confirmation_blocks: int = Field(default=1, ge=0, description="Required confirmations")
|
73
|
+
min_amount: Optional[Decimal] = Field(None, ge=0, description="Minimum amount for this network")
|
74
|
+
max_amount: Optional[Decimal] = Field(None, ge=0, description="Maximum amount for this network")
|
75
|
+
|
76
|
+
# Fee information
|
77
|
+
base_fee: Optional[Decimal] = Field(None, ge=0, description="Base network fee")
|
78
|
+
fee_percentage: Optional[Decimal] = Field(None, ge=0, le=100, description="Fee percentage")
|
79
|
+
|
80
|
+
# Network status
|
81
|
+
is_active: bool = Field(default=True, description="Whether network is active")
|
82
|
+
is_maintenance: bool = Field(default=False, description="Whether network is in maintenance")
|
83
|
+
|
84
|
+
# Provider metadata
|
85
|
+
provider_metadata: Dict[str, Any] = Field(default_factory=dict, description="Provider-specific data")
|
86
|
+
|
87
|
+
@field_validator('code')
|
88
|
+
@classmethod
|
89
|
+
def code_must_be_lowercase(cls, v):
|
90
|
+
return v.lower() if v else v
|
91
|
+
|
92
|
+
|
93
|
+
class ProviderCurrencyResponse(BaseModel):
|
94
|
+
"""Response from provider API for supported currencies."""
|
95
|
+
model_config = ConfigDict(validate_assignment=True, extra="forbid")
|
96
|
+
|
97
|
+
success: bool = Field(description="Whether request was successful")
|
98
|
+
currencies: List[CurrencyInfo] = Field(default_factory=list, description="List of supported currencies")
|
99
|
+
total_count: Optional[int] = Field(None, ge=0, description="Total number of currencies")
|
100
|
+
|
101
|
+
# Error information
|
102
|
+
error_code: Optional[str] = Field(None, description="Provider error code")
|
103
|
+
error_message: Optional[str] = Field(None, description="Error message if failed")
|
104
|
+
|
105
|
+
# Request metadata
|
106
|
+
provider_name: Optional[str] = Field(None, description="Provider name")
|
107
|
+
request_timestamp: Optional[str] = Field(None, description="Request timestamp")
|
108
|
+
cache_ttl: Optional[int] = Field(None, ge=0, description="Cache TTL in seconds")
|
109
|
+
|
110
|
+
@field_validator('currencies')
|
111
|
+
@classmethod
|
112
|
+
def validate_currency_codes_unique(cls, v):
|
113
|
+
codes = [currency.code for currency in v]
|
114
|
+
if len(codes) != len(set(codes)):
|
115
|
+
raise ValueError('Currency codes must be unique')
|
116
|
+
return v
|
117
|
+
|
118
|
+
|
119
|
+
class ProviderNetworkResponse(BaseModel):
|
120
|
+
"""Response from provider API for supported networks."""
|
121
|
+
model_config = ConfigDict(validate_assignment=True, extra="forbid")
|
122
|
+
|
123
|
+
success: bool = Field(description="Whether request was successful")
|
124
|
+
networks: Dict[str, List[NetworkInfo]] = Field(
|
125
|
+
default_factory=dict,
|
126
|
+
description="Networks grouped by currency code"
|
127
|
+
)
|
128
|
+
|
129
|
+
# Error information
|
130
|
+
error_code: Optional[str] = Field(None, description="Provider error code")
|
131
|
+
error_message: Optional[str] = Field(None, description="Error message if failed")
|
132
|
+
|
133
|
+
# Request metadata
|
134
|
+
provider_name: Optional[str] = Field(None, description="Provider name")
|
135
|
+
request_timestamp: Optional[str] = Field(None, description="Request timestamp")
|
136
|
+
cache_ttl: Optional[int] = Field(None, ge=0, description="Cache TTL in seconds")
|
137
|
+
|
138
|
+
@field_validator('networks')
|
139
|
+
@classmethod
|
140
|
+
def validate_network_structure(cls, v):
|
141
|
+
for currency_code, networks in v.items():
|
142
|
+
if not currency_code.isupper():
|
143
|
+
raise ValueError(f'Currency code {currency_code} must be uppercase')
|
144
|
+
|
145
|
+
network_codes = [network.code for network in networks]
|
146
|
+
if len(network_codes) != len(set(network_codes)):
|
147
|
+
raise ValueError(f'Network codes must be unique for currency {currency_code}')
|
148
|
+
return v
|
149
|
+
|
150
|
+
|
151
|
+
class CurrencyNetworkMapping(BaseModel):
|
152
|
+
"""Mapping of currencies to their supported networks."""
|
153
|
+
model_config = ConfigDict(validate_assignment=True, extra="forbid")
|
154
|
+
|
155
|
+
provider_name: str = Field(description="Provider name")
|
156
|
+
mapping: Dict[str, List[str]] = Field(
|
157
|
+
default_factory=dict,
|
158
|
+
description="Currency code -> List of network codes"
|
159
|
+
)
|
160
|
+
last_updated: Optional[str] = Field(None, description="Last update timestamp")
|
161
|
+
|
162
|
+
# Cache information
|
163
|
+
cache_key: Optional[str] = Field(None, description="Cache key for this mapping")
|
164
|
+
ttl_seconds: Optional[int] = Field(None, ge=0, description="TTL for caching")
|
165
|
+
|
166
|
+
@field_validator('mapping')
|
167
|
+
@classmethod
|
168
|
+
def validate_mapping_structure(cls, v):
|
169
|
+
for currency_code, network_codes in v.items():
|
170
|
+
if not currency_code.isupper():
|
171
|
+
raise ValueError(f'Currency code {currency_code} must be uppercase')
|
172
|
+
|
173
|
+
if len(network_codes) != len(set(network_codes)):
|
174
|
+
raise ValueError(f'Network codes must be unique for currency {currency_code}')
|
175
|
+
return v
|
176
|
+
|
177
|
+
def get_networks_for_currency(self, currency_code: str) -> List[str]:
|
178
|
+
"""Get supported networks for a specific currency."""
|
179
|
+
return self.mapping.get(currency_code.upper(), [])
|
180
|
+
|
181
|
+
def get_all_currencies(self) -> List[str]:
|
182
|
+
"""Get all supported currency codes."""
|
183
|
+
return list(self.mapping.keys())
|
184
|
+
|
185
|
+
def get_all_networks(self) -> List[str]:
|
186
|
+
"""Get all unique network codes."""
|
187
|
+
all_networks = []
|
188
|
+
for networks in self.mapping.values():
|
189
|
+
all_networks.extend(networks)
|
190
|
+
return list(set(all_networks))
|
@@ -0,0 +1,196 @@
|
|
1
|
+
from pydantic import BaseModel, Field, ConfigDict, field_validator
|
2
|
+
from typing import Optional, List
|
3
|
+
from decimal import Decimal
|
4
|
+
|
5
|
+
from ...internal_types import ProviderConfig
|
6
|
+
|
7
|
+
|
8
|
+
class NowPaymentsConfig(ProviderConfig):
|
9
|
+
"""NowPayments provider configuration with Pydantic v2."""
|
10
|
+
|
11
|
+
ipn_secret: Optional[str] = Field(default=None, description="IPN secret for webhook validation")
|
12
|
+
callback_url: Optional[str] = Field(default=None, description="Webhook callback URL")
|
13
|
+
success_url: Optional[str] = Field(default=None, description="Payment success redirect URL")
|
14
|
+
cancel_url: Optional[str] = Field(default=None, description="Payment cancel redirect URL")
|
15
|
+
|
16
|
+
|
17
|
+
|
18
|
+
|
19
|
+
class NowPaymentsCurrency(BaseModel):
|
20
|
+
"""NowPayments full currency model from /v1/full-currencies."""
|
21
|
+
model_config = ConfigDict(validate_assignment=True, extra="allow")
|
22
|
+
|
23
|
+
id: int = Field(..., description="Currency ID")
|
24
|
+
code: str = Field(..., description="Currency code (e.g., BTC, USDTERC20)")
|
25
|
+
name: str = Field(..., description="Full currency name")
|
26
|
+
enable: bool = Field(..., description="Currency availability")
|
27
|
+
wallet_regex: Optional[str] = Field(None, description="Wallet address regex")
|
28
|
+
priority: int = Field(..., description="Currency priority")
|
29
|
+
extra_id_exists: bool = Field(..., description="Whether extra ID is required")
|
30
|
+
extra_id_regex: Optional[str] = Field(None, description="Extra ID regex")
|
31
|
+
logo_url: str = Field(..., description="Currency logo URL")
|
32
|
+
track: bool = Field(..., description="Track transactions")
|
33
|
+
cg_id: Optional[str] = Field(None, description="CoinGecko ID")
|
34
|
+
is_maxlimit: bool = Field(..., description="Has max limit")
|
35
|
+
network: Optional[str] = Field(None, description="Blockchain network")
|
36
|
+
smart_contract: Optional[str] = Field(None, description="Smart contract address")
|
37
|
+
network_precision: Optional[str] = Field(None, description="Network precision")
|
38
|
+
explorer_link_hash: Optional[str] = Field(None, description="Explorer link")
|
39
|
+
precision: int = Field(..., description="Currency precision")
|
40
|
+
ticker: Optional[str] = Field(None, description="Ticker symbol")
|
41
|
+
is_defi: bool = Field(..., description="Is DeFi token")
|
42
|
+
is_popular: bool = Field(..., description="Is popular currency")
|
43
|
+
is_stable: bool = Field(..., description="Is stablecoin")
|
44
|
+
available_for_to_conversion: bool = Field(..., description="Available for conversion")
|
45
|
+
trust_wallet_id: Optional[str] = Field(None, description="Trust Wallet ID")
|
46
|
+
created_at: str = Field(..., description="Creation timestamp")
|
47
|
+
updated_at: str = Field(..., description="Update timestamp")
|
48
|
+
available_for_payment: bool = Field(..., description="Available for payment")
|
49
|
+
available_for_payout: bool = Field(..., description="Available for payout")
|
50
|
+
extra_id_optional: bool = Field(..., description="Extra ID is optional")
|
51
|
+
|
52
|
+
|
53
|
+
class NowPaymentsNetwork(BaseModel):
|
54
|
+
"""NowPayments specific network model."""
|
55
|
+
model_config = ConfigDict(validate_assignment=True, extra="forbid")
|
56
|
+
|
57
|
+
code: str = Field(..., description="Network code")
|
58
|
+
name: str = Field(..., description="Network display name")
|
59
|
+
currency: str = Field(..., description="Currency this network belongs to")
|
60
|
+
confirmations: int = Field(1, description="Required confirmations")
|
61
|
+
|
62
|
+
|
63
|
+
class NowPaymentsPaymentRequest(BaseModel):
|
64
|
+
"""NowPayments payment creation request."""
|
65
|
+
model_config = ConfigDict(validate_assignment=True, extra="forbid")
|
66
|
+
|
67
|
+
price_amount: float = Field(..., description="Payment amount")
|
68
|
+
price_currency: str = Field(..., description="Price currency (usually USD)")
|
69
|
+
pay_currency: str = Field(..., description="Payment currency (crypto)")
|
70
|
+
order_id: str = Field(..., description="Unique order identifier")
|
71
|
+
order_description: Optional[str] = Field(None, description="Order description")
|
72
|
+
success_url: Optional[str] = Field(None, description="Success redirect URL")
|
73
|
+
cancel_url: Optional[str] = Field(None, description="Cancel redirect URL")
|
74
|
+
ipn_callback_url: Optional[str] = Field(None, description="IPN callback URL")
|
75
|
+
|
76
|
+
|
77
|
+
class NowPaymentsPaymentResponse(BaseModel):
|
78
|
+
"""NowPayments payment creation response."""
|
79
|
+
model_config = ConfigDict(validate_assignment=True, extra="forbid")
|
80
|
+
|
81
|
+
payment_id: str = Field(..., description="Payment ID")
|
82
|
+
payment_status: str = Field(..., description="Payment status")
|
83
|
+
pay_address: str = Field(..., description="Payment address")
|
84
|
+
price_amount: float = Field(..., description="Price amount")
|
85
|
+
price_currency: str = Field(..., description="Price currency")
|
86
|
+
pay_amount: float = Field(..., description="Payment amount")
|
87
|
+
pay_currency: str = Field(..., description="Payment currency")
|
88
|
+
order_id: str = Field(..., description="Order ID")
|
89
|
+
order_description: Optional[str] = Field(None, description="Order description")
|
90
|
+
invoice_url: Optional[str] = Field(None, description="Payment page URL")
|
91
|
+
success_url: Optional[str] = Field(None, description="Success URL")
|
92
|
+
cancel_url: Optional[str] = Field(None, description="Cancel URL")
|
93
|
+
created_at: str = Field(..., description="Creation timestamp")
|
94
|
+
updated_at: str = Field(..., description="Update timestamp")
|
95
|
+
|
96
|
+
|
97
|
+
class NowPaymentsWebhook(BaseModel):
|
98
|
+
"""NowPayments webhook/IPN data."""
|
99
|
+
model_config = ConfigDict(validate_assignment=True, extra="forbid")
|
100
|
+
|
101
|
+
payment_id: str = Field(..., description="Payment ID")
|
102
|
+
payment_status: str = Field(..., description="Payment status")
|
103
|
+
pay_address: str = Field(..., description="Payment address")
|
104
|
+
price_amount: float = Field(..., description="Price amount")
|
105
|
+
price_currency: str = Field(..., description="Price currency")
|
106
|
+
pay_amount: float = Field(..., description="Payment amount")
|
107
|
+
pay_currency: str = Field(..., description="Payment currency")
|
108
|
+
order_id: str = Field(..., description="Order ID")
|
109
|
+
order_description: Optional[str] = Field(None, description="Order description")
|
110
|
+
outcome_amount: Optional[float] = Field(None, description="Outcome amount")
|
111
|
+
outcome_currency: Optional[str] = Field(None, description="Outcome currency")
|
112
|
+
actually_paid: Optional[float] = Field(None, description="Actually paid amount")
|
113
|
+
txid: Optional[str] = Field(None, description="Transaction ID")
|
114
|
+
created_at: str = Field(..., description="Creation timestamp")
|
115
|
+
updated_at: str = Field(..., description="Update timestamp")
|
116
|
+
|
117
|
+
|
118
|
+
class NowPaymentsStatusResponse(BaseModel):
|
119
|
+
"""NowPayments payment status response."""
|
120
|
+
model_config = ConfigDict(validate_assignment=True, extra="forbid")
|
121
|
+
|
122
|
+
payment_id: str = Field(..., description="Payment ID")
|
123
|
+
payment_status: str = Field(..., description="Payment status")
|
124
|
+
pay_address: str = Field(..., description="Payment address")
|
125
|
+
price_amount: float = Field(..., description="Price amount")
|
126
|
+
price_currency: str = Field(..., description="Price currency")
|
127
|
+
pay_amount: float = Field(..., description="Payment amount")
|
128
|
+
pay_currency: str = Field(..., description="Payment currency")
|
129
|
+
order_id: str = Field(..., description="Order ID")
|
130
|
+
order_description: Optional[str] = Field(None, description="Order description")
|
131
|
+
outcome_amount: Optional[float] = Field(None, description="Outcome amount")
|
132
|
+
outcome_currency: Optional[str] = Field(None, description="Outcome currency")
|
133
|
+
actually_paid: Optional[float] = Field(None, description="Actually paid amount")
|
134
|
+
created_at: str = Field(..., description="Creation timestamp")
|
135
|
+
updated_at: str = Field(..., description="Update timestamp")
|
136
|
+
|
137
|
+
|
138
|
+
class NowPaymentsCurrenciesResponse(BaseModel):
|
139
|
+
"""NowPayments supported currencies response."""
|
140
|
+
model_config = ConfigDict(validate_assignment=True, extra="forbid")
|
141
|
+
|
142
|
+
currencies: List[str] = Field(..., description="List of supported currency codes")
|
143
|
+
|
144
|
+
|
145
|
+
class NowPaymentsFullCurrenciesResponse(BaseModel):
|
146
|
+
"""NowPayments full currencies response from /v1/full-currencies."""
|
147
|
+
model_config = ConfigDict(validate_assignment=True, extra="forbid")
|
148
|
+
|
149
|
+
currencies: List[NowPaymentsCurrency] = Field(..., description="List of full currency data")
|
150
|
+
|
151
|
+
|
152
|
+
class NowPaymentsMinAmountResponse(BaseModel):
|
153
|
+
"""NowPayments minimum amount response."""
|
154
|
+
model_config = ConfigDict(validate_assignment=True, extra="forbid")
|
155
|
+
|
156
|
+
currency_from: str = Field(..., description="Source currency")
|
157
|
+
currency_to: str = Field(..., description="Target currency")
|
158
|
+
min_amount: float = Field(..., description="Minimum payment amount")
|
159
|
+
|
160
|
+
|
161
|
+
class NowPaymentsEstimateResponse(BaseModel):
|
162
|
+
"""NowPayments payment estimate response."""
|
163
|
+
model_config = ConfigDict(validate_assignment=True, extra="forbid")
|
164
|
+
|
165
|
+
currency_from: str = Field(..., description="Source currency")
|
166
|
+
currency_to: str = Field(..., description="Target currency")
|
167
|
+
amount_from: float = Field(..., description="Source amount")
|
168
|
+
estimated_amount: float = Field(..., description="Estimated target amount")
|
169
|
+
fee_amount: Optional[float] = Field(None, description="Fee amount")
|
170
|
+
|
171
|
+
|
172
|
+
class NowPaymentsStatusInfo(BaseModel):
|
173
|
+
"""NowPayments API status response."""
|
174
|
+
model_config = ConfigDict(validate_assignment=True, extra="forbid")
|
175
|
+
|
176
|
+
message: str = Field(..., description="Status message")
|
177
|
+
uptime: Optional[float] = Field(None, description="API uptime percentage")
|
178
|
+
|
179
|
+
|
180
|
+
# =============================================================================
|
181
|
+
# MONITORING & HEALTH CHECK MODELS
|
182
|
+
# =============================================================================
|
183
|
+
|
184
|
+
class NowPaymentsStatusResponse(BaseModel):
|
185
|
+
"""NowPayments /v1/status response schema for health checks."""
|
186
|
+
model_config = ConfigDict(validate_assignment=True, extra="forbid")
|
187
|
+
|
188
|
+
message: str = Field(..., description="Status message")
|
189
|
+
|
190
|
+
@field_validator('message')
|
191
|
+
@classmethod
|
192
|
+
def validate_message_ok(cls, v):
|
193
|
+
"""Validate that message is OK."""
|
194
|
+
if v.upper() != 'OK':
|
195
|
+
raise ValueError(f"Expected message 'OK', got '{v}'")
|
196
|
+
return v
|