django-cfg 1.3.11__py3-none-any.whl → 1.4.0__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/accounts/admin/inlines.py +11 -5
- django_cfg/apps/accounts/admin/user_admin.py +39 -16
- django_cfg/apps/accounts/serializers/profile.py +1 -1
- django_cfg/apps/accounts/services/otp_service.py +18 -11
- django_cfg/apps/accounts/signals.py +15 -24
- django_cfg/apps/accounts/utils/notifications.py +217 -358
- django_cfg/apps/accounts/views/otp.py +2 -2
- django_cfg/apps/accounts/views/webhook.py +1 -1
- django_cfg/apps/agents/core/django_agent.py +1 -1
- django_cfg/apps/api/commands/views.py +66 -83
- django_cfg/apps/api/health/drf_views.py +269 -0
- django_cfg/apps/api/health/serializers.py +45 -0
- django_cfg/apps/api/health/urls.py +6 -1
- django_cfg/apps/knowbase/admin/actions/__init__.py +13 -0
- django_cfg/apps/knowbase/admin/actions/visibility_actions.py +56 -0
- django_cfg/apps/knowbase/admin/document_admin.py +136 -270
- django_cfg/apps/knowbase/admin/helpers/__init__.py +17 -0
- django_cfg/apps/knowbase/admin/helpers/configs.py +72 -0
- django_cfg/apps/knowbase/admin/helpers/display_helpers.py +156 -0
- django_cfg/apps/knowbase/admin/helpers/statistics.py +108 -0
- django_cfg/apps/knowbase/config/constance_fields.py +1 -1
- django_cfg/apps/knowbase/config/settings.py +2 -2
- django_cfg/apps/knowbase/mixins/__init__.py +19 -2
- django_cfg/apps/knowbase/mixins/config/__init__.py +14 -0
- django_cfg/apps/knowbase/mixins/config/defaults.py +75 -0
- django_cfg/apps/knowbase/mixins/config/meta_config.py +120 -0
- django_cfg/apps/knowbase/mixins/creator.py +10 -10
- django_cfg/apps/knowbase/mixins/external_data_mixin.py +105 -403
- django_cfg/apps/knowbase/mixins/generators/__init__.py +16 -0
- django_cfg/apps/knowbase/mixins/generators/content_generator.py +218 -0
- django_cfg/apps/knowbase/mixins/generators/field_analyzer.py +76 -0
- django_cfg/apps/knowbase/mixins/generators/metadata_generator.py +124 -0
- django_cfg/apps/knowbase/mixins/service.py +2 -2
- django_cfg/apps/knowbase/services/archive/__init__.py +1 -0
- django_cfg/apps/knowbase/services/archive/analyzers/__init__.py +17 -0
- django_cfg/apps/knowbase/services/archive/analyzers/complexity_analyzer.py +33 -0
- django_cfg/apps/knowbase/services/archive/analyzers/purpose_detector.py +36 -0
- django_cfg/apps/knowbase/services/archive/analyzers/quality_analyzer.py +39 -0
- django_cfg/apps/knowbase/services/archive/analyzers/tag_generator.py +103 -0
- django_cfg/apps/knowbase/services/archive/chunking/__init__.py +19 -0
- django_cfg/apps/knowbase/services/archive/chunking/base.py +81 -0
- django_cfg/apps/knowbase/services/archive/chunking/json_chunker.py +62 -0
- django_cfg/apps/knowbase/services/archive/chunking/markdown_chunker.py +107 -0
- django_cfg/apps/knowbase/services/archive/chunking/python_chunker.py +248 -0
- django_cfg/apps/knowbase/services/archive/chunking/text_chunker.py +70 -0
- django_cfg/apps/knowbase/services/archive/chunking_service.py +110 -729
- django_cfg/apps/knowbase/services/archive/context/__init__.py +14 -0
- django_cfg/apps/knowbase/services/archive/context/builders.py +220 -0
- django_cfg/apps/knowbase/services/archive/context/models.py +38 -0
- django_cfg/apps/knowbase/services/embedding/models.py +18 -14
- django_cfg/apps/knowbase/services/embedding/processors.py +6 -3
- django_cfg/apps/knowbase/tasks/document_processing.py +11 -3
- django_cfg/apps/leads/tests.py +1 -1
- django_cfg/apps/payments/admin/api_keys_admin.py +1 -1
- django_cfg/apps/payments/admin/balance_admin.py +1 -1
- django_cfg/apps/payments/admin/currencies_admin.py +1 -1
- django_cfg/apps/payments/admin/payments_admin.py +1 -1
- django_cfg/apps/payments/admin/subscriptions_admin.py +1 -1
- django_cfg/apps/payments/admin_interface/templates/payments/base.html +59 -126
- django_cfg/apps/payments/admin_interface/views/api/payments.py +1 -1
- django_cfg/apps/payments/admin_interface/views/api/stats.py +1 -1
- django_cfg/apps/payments/admin_interface/views/api/users.py +1 -1
- django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +1 -1
- django_cfg/apps/payments/admin_interface/views/api/webhook_public.py +1 -1
- django_cfg/apps/payments/admin_interface/views/base.py +29 -2
- django_cfg/apps/payments/apps.py +1 -1
- django_cfg/apps/payments/config/django_cfg_integration.py +2 -2
- django_cfg/apps/payments/config/helpers.py +3 -2
- django_cfg/apps/payments/management/commands/cleanup_expired_data.py +1 -1
- django_cfg/apps/payments/management/commands/currency_stats.py +1 -1
- django_cfg/apps/payments/management/commands/manage_currencies.py +1 -1
- django_cfg/apps/payments/management/commands/manage_providers.py +1 -1
- django_cfg/apps/payments/management/commands/process_pending_payments.py +1 -1
- django_cfg/apps/payments/management/commands/test_providers.py +1 -1
- django_cfg/apps/payments/middleware/api_access.py +1 -1
- django_cfg/apps/payments/middleware/rate_limiting.py +1 -1
- django_cfg/apps/payments/middleware/usage_tracking.py +1 -1
- django_cfg/apps/payments/models/balance.py +2 -2
- django_cfg/apps/payments/models/managers/api_key_managers.py +1 -1
- django_cfg/apps/payments/models/managers/balance_managers.py +1 -1
- django_cfg/apps/payments/models/managers/currency_managers.py +1 -1
- django_cfg/apps/payments/models/managers/payment_managers.py +1 -1
- django_cfg/apps/payments/models/managers/subscription_managers.py +1 -1
- django_cfg/apps/payments/models/payments.py +2 -2
- django_cfg/apps/payments/services/cache_service/__init__.py +1 -1
- django_cfg/apps/payments/services/cache_service/simple_cache.py +10 -5
- django_cfg/apps/payments/services/core/base.py +1 -1
- django_cfg/apps/payments/services/core/currency/__init__.py +13 -0
- django_cfg/apps/payments/services/core/currency/currency_converter.py +57 -0
- django_cfg/apps/payments/services/core/currency/currency_validator.py +61 -0
- django_cfg/apps/payments/services/core/operations/__init__.py +15 -0
- django_cfg/apps/payments/services/core/operations/payment_canceller.py +100 -0
- django_cfg/apps/payments/services/core/operations/payment_creator.py +196 -0
- django_cfg/apps/payments/services/core/operations/status_checker.py +100 -0
- django_cfg/apps/payments/services/core/payment_service.py +124 -612
- django_cfg/apps/payments/services/core/providers/__init__.py +13 -0
- django_cfg/apps/payments/services/core/providers/provider_client.py +132 -0
- django_cfg/apps/payments/services/core/providers/status_mapper.py +89 -0
- django_cfg/apps/payments/services/core/utils/__init__.py +13 -0
- django_cfg/apps/payments/services/core/utils/data_converter.py +48 -0
- django_cfg/apps/payments/services/core/utils/statistics_calculator.py +69 -0
- django_cfg/apps/payments/services/providers/base.py +1 -1
- django_cfg/apps/payments/services/providers/nowpayments/__init__.py +3 -3
- django_cfg/apps/payments/services/providers/nowpayments/parsers/__init__.py +9 -0
- django_cfg/apps/payments/services/providers/nowpayments/parsers/data/__init__.py +23 -0
- django_cfg/apps/payments/services/providers/nowpayments/parsers/data/constants.py +23 -0
- django_cfg/apps/payments/services/providers/nowpayments/parsers/data/currency_names.py +244 -0
- django_cfg/apps/payments/services/providers/nowpayments/parsers/data/patterns.py +511 -0
- django_cfg/apps/payments/services/providers/nowpayments/parsers/parser.py +168 -0
- django_cfg/apps/payments/services/providers/nowpayments/provider.py +1 -1
- django_cfg/apps/payments/services/providers/nowpayments/sync.py +1 -1
- django_cfg/apps/payments/services/providers/registry.py +1 -1
- django_cfg/apps/payments/services/providers/sync_service.py +1 -1
- django_cfg/apps/payments/signals/__init__.py +1 -1
- django_cfg/apps/payments/signals/api_key_signals.py +1 -1
- django_cfg/apps/payments/signals/balance_signals.py +1 -1
- django_cfg/apps/payments/signals/payment_signals.py +1 -1
- django_cfg/apps/payments/signals/subscription_signals.py +1 -1
- django_cfg/apps/payments/views/api/api_keys.py +1 -1
- django_cfg/apps/payments/views/api/balances.py +1 -1
- django_cfg/apps/payments/views/api/base.py +1 -1
- django_cfg/apps/payments/views/api/currencies.py +1 -1
- django_cfg/apps/payments/views/api/payments.py +1 -1
- django_cfg/apps/payments/views/api/subscriptions.py +1 -1
- django_cfg/apps/payments/views/api/webhooks.py +1 -1
- django_cfg/apps/payments/views/serializers/api_keys.py +1 -1
- django_cfg/apps/payments/views/serializers/balances.py +1 -1
- django_cfg/apps/payments/views/serializers/currencies.py +1 -1
- django_cfg/apps/payments/views/serializers/payments.py +1 -1
- django_cfg/apps/payments/views/serializers/subscriptions.py +1 -1
- django_cfg/apps/payments/views/serializers/webhooks.py +1 -1
- django_cfg/apps/support/admin/support_admin.py +21 -13
- django_cfg/apps/support/templates/support/chat/access_denied.html +21 -27
- django_cfg/apps/support/templates/support/chat/ticket_chat.html +183 -254
- django_cfg/apps/support/utils/support_email_service.py +1 -1
- django_cfg/apps/tasks/templates/tasks/layout/base.html +20 -115
- django_cfg/apps/tasks/utils/simulator.py +1 -1
- django_cfg/apps/tasks/views/dashboard.py +33 -3
- django_cfg/apps/urls.py +5 -1
- django_cfg/cli/README.md +57 -471
- django_cfg/cli/commands/create_project.py +140 -529
- django_cfg/cli/main.py +13 -10
- django_cfg/core/__init__.py +63 -6
- django_cfg/core/base/__init__.py +5 -0
- django_cfg/core/base/config_model.py +652 -0
- django_cfg/core/builders/__init__.py +11 -0
- django_cfg/core/builders/apps_builder.py +258 -0
- django_cfg/core/builders/middleware_builder.py +115 -0
- django_cfg/core/builders/security_builder.py +96 -0
- django_cfg/core/config.py +20 -892
- django_cfg/core/constants.py +69 -0
- django_cfg/core/environment/__init__.py +9 -0
- django_cfg/core/exceptions.py +45 -298
- django_cfg/core/generation/__init__.py +51 -0
- django_cfg/core/generation/core_generators/__init__.py +0 -0
- django_cfg/core/generation/core_generators/settings.py +90 -0
- django_cfg/core/generation/core_generators/static.py +82 -0
- django_cfg/core/generation/core_generators/templates.py +141 -0
- django_cfg/core/generation/data_generators/__init__.py +15 -0
- django_cfg/core/generation/data_generators/cache.py +132 -0
- django_cfg/core/generation/data_generators/database.py +117 -0
- django_cfg/core/generation/generation.py +92 -0
- django_cfg/core/generation/integration_generators/__init__.py +21 -0
- django_cfg/core/generation/integration_generators/api.py +237 -0
- django_cfg/core/generation/integration_generators/sessions.py +65 -0
- django_cfg/core/generation/integration_generators/tailwind.py +54 -0
- django_cfg/core/generation/integration_generators/tasks.py +92 -0
- django_cfg/core/generation/integration_generators/third_party.py +144 -0
- django_cfg/core/generation/orchestrator.py +285 -0
- django_cfg/core/generation/protocols.py +30 -0
- django_cfg/core/generation/security_generators/__init__.py +0 -0
- django_cfg/core/generation/utility_generators/__init__.py +24 -0
- django_cfg/core/generation/utility_generators/email.py +58 -0
- django_cfg/core/generation/utility_generators/i18n.py +66 -0
- django_cfg/core/generation/utility_generators/limits.py +58 -0
- django_cfg/core/generation/utility_generators/logging.py +66 -0
- django_cfg/core/generation/utility_generators/security.py +101 -0
- django_cfg/core/generation/utils/__init__.py +0 -0
- django_cfg/core/generation/utils/helpers.py +32 -0
- django_cfg/core/integration/__init__.py +18 -25
- django_cfg/core/integration/display/startup.py +146 -133
- django_cfg/core/integration/url_integration.py +13 -2
- django_cfg/core/services/__init__.py +5 -0
- django_cfg/core/services/config_service.py +121 -0
- django_cfg/core/state/__init__.py +9 -0
- django_cfg/core/state/registry.py +84 -0
- django_cfg/core/types/__init__.py +15 -0
- django_cfg/core/types/aliases.py +15 -0
- django_cfg/core/types/enums.py +49 -0
- django_cfg/dashboard/DEBUG_README.md +105 -0
- django_cfg/dashboard/REFACTORING_SUMMARY.md +237 -0
- django_cfg/dashboard/__init__.py +24 -0
- django_cfg/dashboard/components.py +308 -0
- django_cfg/dashboard/debug.py +176 -0
- django_cfg/dashboard/management/__init__.py +0 -0
- django_cfg/dashboard/management/commands/__init__.py +0 -0
- django_cfg/dashboard/management/commands/debug_dashboard.py +109 -0
- django_cfg/dashboard/sections/__init__.py +1 -0
- django_cfg/dashboard/sections/base.py +128 -0
- django_cfg/dashboard/sections/commands.py +32 -0
- django_cfg/dashboard/sections/overview.py +394 -0
- django_cfg/dashboard/sections/stats.py +48 -0
- django_cfg/dashboard/sections/system.py +73 -0
- django_cfg/management/commands/check_settings.py +6 -2
- django_cfg/management/commands/clear_constance.py +6 -1
- django_cfg/management/commands/create_token.py +5 -4
- django_cfg/management/commands/generate.py +5 -0
- django_cfg/management/commands/list_urls.py +7 -2
- django_cfg/management/commands/migrate_all.py +6 -2
- django_cfg/management/commands/migrator.py +6 -1
- django_cfg/management/commands/rundramatiq.py +6 -1
- django_cfg/management/commands/rundramatiq_simulator.py +11 -4
- django_cfg/management/commands/runserver_ngrok.py +9 -7
- django_cfg/management/commands/script.py +25 -21
- django_cfg/management/commands/show_config.py +6 -1
- django_cfg/management/commands/show_urls.py +8 -3
- django_cfg/management/commands/superuser.py +5 -4
- django_cfg/management/commands/task_clear.py +8 -3
- django_cfg/management/commands/task_status.py +8 -3
- django_cfg/management/commands/test_email.py +6 -1
- django_cfg/management/commands/test_telegram.py +6 -1
- django_cfg/management/commands/test_twilio.py +6 -1
- django_cfg/management/commands/tree.py +7 -4
- django_cfg/models/__init__.py +88 -3
- django_cfg/models/api/__init__.py +27 -0
- django_cfg/models/{api.py → api/config.py} +1 -1
- django_cfg/models/api/drf/__init__.py +21 -0
- django_cfg/models/api/drf/config.py +101 -0
- django_cfg/models/api/drf/redoc.py +31 -0
- django_cfg/models/api/drf/spectacular.py +129 -0
- django_cfg/models/api/drf/swagger.py +59 -0
- django_cfg/models/{api_keys.py → api/keys.py} +16 -6
- django_cfg/models/{limits.py → api/limits.py} +0 -1
- django_cfg/models/base/__init__.py +14 -0
- django_cfg/models/django/__init__.py +16 -0
- django_cfg/models/{constance.py → django/constance.py} +1 -1
- django_cfg/models/{environment.py → django/environment.py} +1 -1
- django_cfg/models/infrastructure/__init__.py +17 -0
- django_cfg/models/{cache.py → infrastructure/cache.py} +3 -2
- django_cfg/models/infrastructure/database/__init__.py +22 -0
- django_cfg/models/infrastructure/database/config.py +265 -0
- django_cfg/models/infrastructure/database/converters.py +91 -0
- django_cfg/models/infrastructure/database/parsers.py +96 -0
- django_cfg/models/infrastructure/database/routing.py +85 -0
- django_cfg/models/infrastructure/database/validators.py +170 -0
- django_cfg/models/{logging.py → infrastructure/logging.py} +1 -1
- django_cfg/models/{security.py → infrastructure/security.py} +2 -2
- django_cfg/models/ngrok/__init__.py +11 -0
- django_cfg/models/ngrok/auth.py +37 -0
- django_cfg/models/ngrok/config.py +77 -0
- django_cfg/models/ngrok/tunnel.py +35 -0
- django_cfg/models/payments/__init__.py +20 -0
- django_cfg/models/payments/api_keys.py +57 -0
- django_cfg/models/{payments.py → payments/config.py} +56 -154
- django_cfg/models/payments/providers/__init__.py +15 -0
- django_cfg/models/payments/providers/base.py +25 -0
- django_cfg/models/payments/providers/nowpayments.py +48 -0
- django_cfg/models/services/__init__.py +18 -0
- django_cfg/models/services/base.py +65 -0
- django_cfg/models/{email.py → services/email.py} +1 -1
- django_cfg/models/services/telegram.py +172 -0
- django_cfg/models/tasks/__init__.py +51 -0
- django_cfg/models/tasks/backends.py +250 -0
- django_cfg/models/tasks/config.py +314 -0
- django_cfg/models/tasks/utils.py +174 -0
- django_cfg/modules/base.py +18 -3
- django_cfg/modules/django_admin/decorators/actions.py +1 -1
- django_cfg/modules/django_admin/decorators/display.py +1 -1
- django_cfg/modules/django_admin/mixins/standalone_actions_mixin.py +1 -1
- django_cfg/modules/django_cfg_rpc_client/README.md +346 -0
- django_cfg/modules/django_cfg_rpc_client/__init__.py +51 -0
- django_cfg/modules/django_cfg_rpc_client/client.py +540 -0
- django_cfg/modules/django_cfg_rpc_client/config.py +207 -0
- django_cfg/modules/django_cfg_rpc_client/dashboard/README.md +517 -0
- django_cfg/modules/django_cfg_rpc_client/dashboard/UNFOLD_INTEGRATION.md +439 -0
- django_cfg/modules/django_cfg_rpc_client/dashboard/__init__.py +11 -0
- django_cfg/modules/django_cfg_rpc_client/dashboard/apps.py +22 -0
- django_cfg/modules/django_cfg_rpc_client/dashboard/monitor.py +435 -0
- django_cfg/modules/django_cfg_rpc_client/dashboard/static/django_cfg_rpc_dashboard/js/dashboard.js +373 -0
- django_cfg/modules/django_cfg_rpc_client/dashboard/templates/django_cfg_rpc_dashboard/base.html +76 -0
- django_cfg/modules/django_cfg_rpc_client/dashboard/templates/django_cfg_rpc_dashboard/dashboard.html +200 -0
- django_cfg/modules/django_cfg_rpc_client/dashboard/urls.py +22 -0
- django_cfg/modules/django_cfg_rpc_client/dashboard/urls_admin.py +9 -0
- django_cfg/modules/django_cfg_rpc_client/dashboard/views.py +251 -0
- django_cfg/modules/django_cfg_rpc_client/exceptions.py +201 -0
- django_cfg/modules/django_drf_theme/CHANGELOG.md +210 -0
- django_cfg/modules/django_drf_theme/EXAMPLE.md +465 -0
- django_cfg/modules/django_drf_theme/IMPLEMENTATION.md +232 -0
- django_cfg/modules/django_drf_theme/README.md +207 -0
- django_cfg/modules/django_drf_theme/TAILWIND_CDN_GUIDE.md +274 -0
- django_cfg/modules/django_drf_theme/__init__.py +23 -0
- django_cfg/modules/django_drf_theme/apps.py +15 -0
- django_cfg/modules/django_drf_theme/renderers.py +58 -0
- django_cfg/modules/django_drf_theme/templates/rest_framework/tailwind/api.html +375 -0
- django_cfg/modules/django_drf_theme/templates/rest_framework/tailwind/base.html +938 -0
- django_cfg/modules/django_drf_theme/templates/rest_framework/tailwind/forms/filter_form.html +132 -0
- django_cfg/modules/django_drf_theme/templates/rest_framework/tailwind/forms/raw_data_form.html +123 -0
- django_cfg/modules/django_drf_theme/templatetags/__init__.py +1 -0
- django_cfg/modules/django_drf_theme/templatetags/tailwind_tags.py +57 -0
- django_cfg/modules/django_email/__init__.py +14 -0
- django_cfg/modules/{django_email.py → django_email/service.py} +78 -113
- django_cfg/modules/django_email/utils.py +40 -0
- django_cfg/modules/django_health/__init__.py +9 -0
- django_cfg/modules/{django_health.py → django_health/service.py} +23 -21
- django_cfg/modules/django_llm/llm/client.py +155 -550
- django_cfg/modules/django_llm/llm/embeddings/__init__.py +13 -0
- django_cfg/modules/django_llm/llm/embeddings/mock_embedder.py +106 -0
- django_cfg/modules/django_llm/llm/embeddings/openai_embedder.py +79 -0
- django_cfg/modules/django_llm/llm/models_api/__init__.py +9 -0
- django_cfg/modules/django_llm/llm/models_api/models_query.py +163 -0
- django_cfg/modules/django_llm/llm/providers/__init__.py +15 -0
- django_cfg/modules/django_llm/llm/providers/config_builder.py +103 -0
- django_cfg/modules/django_llm/llm/providers/provider_manager.py +148 -0
- django_cfg/modules/django_llm/llm/providers/provider_selector.py +60 -0
- django_cfg/modules/django_llm/llm/requests/__init__.py +15 -0
- django_cfg/modules/django_llm/llm/requests/cache_manager.py +170 -0
- django_cfg/modules/django_llm/llm/requests/chat_handler.py +199 -0
- django_cfg/modules/django_llm/llm/requests/embedding_handler.py +113 -0
- django_cfg/modules/django_llm/llm/responses/__init__.py +9 -0
- django_cfg/modules/django_llm/llm/responses/response_builder.py +131 -0
- django_cfg/modules/django_llm/llm/stats/__init__.py +9 -0
- django_cfg/modules/django_llm/llm/stats/stats_manager.py +107 -0
- django_cfg/modules/django_llm/translator/detectors/__init__.py +13 -0
- django_cfg/modules/django_llm/translator/detectors/language_detector.py +90 -0
- django_cfg/modules/django_llm/translator/detectors/script_detector.py +153 -0
- django_cfg/modules/django_llm/translator/stats/__init__.py +11 -0
- django_cfg/modules/django_llm/translator/stats/stats_tracker.py +85 -0
- django_cfg/modules/django_llm/translator/translator.py +150 -603
- django_cfg/modules/django_llm/translator/translators/__init__.py +15 -0
- django_cfg/modules/django_llm/translator/translators/json_translator.py +316 -0
- django_cfg/modules/django_llm/translator/translators/text_translator.py +139 -0
- django_cfg/modules/django_llm/translator/utils/__init__.py +13 -0
- django_cfg/modules/django_llm/translator/utils/prompt_builder.py +110 -0
- django_cfg/modules/django_llm/translator/utils/text_utils.py +114 -0
- django_cfg/modules/django_logging/FIXES_SUMMARY.md +276 -0
- django_cfg/modules/django_logging/LOGGING_GUIDE.md +504 -0
- django_cfg/modules/django_logging/__init__.py +14 -0
- django_cfg/modules/{django_logger.py → django_logging/django_logger.py} +13 -13
- django_cfg/modules/{logger.py → django_logging/logger.py} +14 -4
- django_cfg/modules/django_ngrok/__init__.py +39 -0
- django_cfg/modules/{django_ngrok.py → django_ngrok/service.py} +14 -42
- django_cfg/modules/django_rpc_old/POETRY.md +344 -0
- django_cfg/modules/django_rpc_old/README.md +397 -0
- django_cfg/modules/django_rpc_old/TESTING.md +358 -0
- django_cfg/modules/django_rpc_old/__init__.py +39 -0
- django_cfg/modules/django_rpc_old/client.py +531 -0
- django_cfg/modules/django_rpc_old/config.py +279 -0
- django_cfg/modules/django_rpc_old/exceptions.py +172 -0
- django_cfg/modules/django_tailwind/README.md +478 -0
- django_cfg/modules/django_tailwind/__init__.py +7 -0
- django_cfg/modules/django_tailwind/apps.py +10 -0
- django_cfg/modules/django_tailwind/templates/django_tailwind/app.html +5 -0
- django_cfg/modules/django_tailwind/templates/django_tailwind/base.html +117 -0
- django_cfg/modules/django_tailwind/templates/django_tailwind/components/navbar.html +124 -0
- django_cfg/modules/django_tailwind/templates/django_tailwind/components/theme_toggle.html +54 -0
- django_cfg/modules/django_tailwind/templates/django_tailwind/components/user_menu.html +116 -0
- django_cfg/modules/django_tailwind/templates/django_tailwind/simple.html +46 -0
- django_cfg/modules/django_tailwind/templatetags/__init__.py +1 -0
- django_cfg/modules/django_tailwind/templatetags/tailwind_info.py +185 -0
- django_cfg/modules/django_tasks/__init__.py +29 -0
- django_cfg/modules/django_tasks/factory.py +127 -0
- django_cfg/modules/{django_tasks.py → django_tasks/service.py} +45 -274
- django_cfg/modules/django_tasks/settings.py +107 -0
- django_cfg/modules/django_telegram/__init__.py +29 -0
- django_cfg/modules/{django_telegram.py → django_telegram/service.py} +45 -113
- django_cfg/modules/django_telegram/utils.py +62 -0
- django_cfg/modules/django_twilio/__init__.py +54 -107
- django_cfg/modules/django_twilio/_imports.py +30 -0
- django_cfg/modules/django_twilio/base.py +192 -0
- django_cfg/modules/django_twilio/email_otp.py +227 -0
- django_cfg/modules/django_twilio/sendgrid_service.py +1 -1
- django_cfg/modules/django_twilio/simple_service.py +1 -2
- django_cfg/modules/django_twilio/sms.py +94 -0
- django_cfg/modules/django_twilio/twilio_service.py +2 -3
- django_cfg/modules/django_twilio/unified.py +310 -0
- django_cfg/modules/django_twilio/utils.py +190 -0
- django_cfg/modules/django_twilio/whatsapp.py +137 -0
- django_cfg/modules/django_unfold/callbacks/base.py +198 -7
- django_cfg/modules/django_unfold/callbacks/main.py +102 -10
- django_cfg/modules/django_unfold/dashboard.py +65 -43
- django_cfg/modules/django_unfold/models/config.py +13 -12
- django_cfg/modules/django_unfold/models/navigation.py +8 -3
- django_cfg/modules/django_unfold/models/tabs.py +2 -2
- django_cfg/modules/django_unfold/templates/unfold/helpers/app_list.html +102 -0
- django_cfg/registry/core.py +24 -26
- django_cfg/registry/modules.py +5 -2
- django_cfg/registry/services.py +20 -3
- django_cfg/registry/third_party.py +8 -8
- django_cfg/static/admin/css/dashboard.css +260 -0
- django_cfg/static/admin/js/commands.js +171 -0
- django_cfg/static/admin/js/dashboard.js +126 -0
- django_cfg/templates/admin/components/management_commands.js +375 -0
- django_cfg/templates/admin/components/progress_bar.html +18 -23
- django_cfg/templates/admin/index.html +48 -20
- django_cfg/templates/admin/index_new.html +106 -0
- django_cfg/templates/admin/layouts/base_dashboard.html +60 -0
- django_cfg/templates/admin/layouts/dashboard_with_tabs.html +1 -20
- django_cfg/templates/admin/sections/commands_section.html +626 -0
- django_cfg/templates/admin/sections/overview_section.html +112 -0
- django_cfg/templates/admin/sections/stats_section.html +35 -0
- django_cfg/templates/admin/sections/system_section.html +99 -0
- django_cfg/templates/admin/snippets/components/CHARTS_GUIDE.md +322 -0
- django_cfg/templates/admin/snippets/components/activity_tracker.html +85 -47
- django_cfg/templates/admin/snippets/components/charts_section.html +154 -64
- django_cfg/templates/admin/snippets/components/django_commands.html +3 -3
- django_cfg/templates/admin/snippets/components/recent_activity_improved.html +25 -0
- django_cfg/templates/admin/snippets/components/recent_users_table.html +1 -1
- django_cfg/templates/admin/snippets/components/system_metrics.html +179 -93
- django_cfg/templates/admin/snippets/zones/zones_table.html +2 -2
- django_cfg/templatetags/django_cfg.py +7 -1
- django_cfg/utils/smart_defaults.py +4 -4
- django_cfg-1.4.0.dist-info/METADATA +920 -0
- {django_cfg-1.3.11.dist-info → django_cfg-1.4.0.dist-info}/RECORD +425 -196
- django_cfg/apps/accounts/utils/auth_email_service.py +0 -84
- django_cfg/apps/payments/services/providers/nowpayments/parsers.py +0 -879
- django_cfg/core/generation.py +0 -621
- django_cfg/management/commands/validate_config.py +0 -189
- django_cfg/models/database.py +0 -480
- django_cfg/models/drf.py +0 -272
- django_cfg/models/ngrok.py +0 -122
- django_cfg/models/services.py +0 -440
- django_cfg/models/tasks.py +0 -550
- django_cfg/modules/django_twilio/service.py +0 -942
- django_cfg/template_archive/django_sample.zip +0 -0
- django_cfg/templates/rest_framework/api.html +0 -12
- django_cfg/utils/toolkit.py +0 -703
- django_cfg-1.3.11.dist-info/METADATA +0 -1029
- /django_cfg/apps/accounts/management/commands/{test_otp.py → otp_test.py} +0 -0
- /django_cfg/core/{environment.py → environment/detector.py} +0 -0
- /django_cfg/models/{cors.py → api/cors.py} +0 -0
- /django_cfg/models/{jwt.py → api/jwt.py} +0 -0
- /django_cfg/models/{base.py → base/config.py} +0 -0
- /django_cfg/models/{cfg.py → base/module.py} +0 -0
- /django_cfg/models/{revolution.py → django/revolution.py} +0 -0
- /django_cfg/modules/{dramatiq_setup.py → django_tasks/dramatiq_setup.py} +0 -0
- {django_cfg-1.3.11.dist-info → django_cfg-1.4.0.dist-info}/WHEEL +0 -0
- {django_cfg-1.3.11.dist-info → django_cfg-1.4.0.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.3.11.dist-info → django_cfg-1.4.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,47 +1,81 @@
|
|
1
1
|
"""
|
2
|
-
Payment service for the Universal Payment System v2.0.
|
2
|
+
Payment service orchestrator for the Universal Payment System v2.0.
|
3
3
|
|
4
|
-
|
4
|
+
Coordinates payment operations using specialized components.
|
5
5
|
"""
|
6
6
|
|
7
|
-
from typing import Optional
|
8
|
-
from decimal import Decimal
|
7
|
+
from typing import Optional
|
9
8
|
from django.contrib.auth import get_user_model
|
10
|
-
from django.db import models
|
11
|
-
from django.utils import timezone
|
12
9
|
|
13
|
-
from django_cfg.modules.django_currency import convert_currency, get_exchange_rate
|
14
10
|
from .base import BaseService
|
15
11
|
from ..types import (
|
16
12
|
PaymentCreateRequest, PaymentStatusRequest, PaymentResult,
|
17
|
-
|
13
|
+
ServiceOperationResult
|
18
14
|
)
|
19
|
-
from ...models
|
20
|
-
from
|
21
|
-
|
15
|
+
from ...models import UniversalPayment
|
16
|
+
from ..providers import get_provider_registry
|
17
|
+
|
18
|
+
# Import specialized components
|
19
|
+
from .currency import CurrencyValidator, CurrencyConverter
|
20
|
+
from .providers import ProviderClient, StatusMapper
|
21
|
+
from .utils import DataConverter, StatisticsCalculator
|
22
|
+
from .operations import PaymentCreator, PaymentCanceller, StatusChecker
|
22
23
|
|
23
24
|
User = get_user_model()
|
24
25
|
|
26
|
+
|
25
27
|
class PaymentService(BaseService):
|
26
28
|
"""
|
27
|
-
Payment service
|
28
|
-
|
29
|
-
|
29
|
+
Payment service orchestrator.
|
30
|
+
|
31
|
+
Coordinates payment operations using specialized components.
|
32
|
+
Delegates to:
|
33
|
+
- PaymentCreator: Payment creation logic
|
34
|
+
- StatusChecker: Status checking and retrieval
|
35
|
+
- PaymentCanceller: Payment cancellation
|
36
|
+
- CurrencyValidator/Converter: Currency operations
|
37
|
+
- ProviderClient: Provider communication
|
38
|
+
- DataConverter: Data transformations
|
39
|
+
- StatisticsCalculator: Statistics and metrics
|
30
40
|
"""
|
31
|
-
|
41
|
+
|
32
42
|
def __init__(self):
|
33
|
-
"""Initialize
|
43
|
+
"""Initialize service with components."""
|
34
44
|
super().__init__()
|
35
|
-
|
45
|
+
|
46
|
+
# Initialize provider registry
|
36
47
|
self.provider_registry = get_provider_registry()
|
37
|
-
|
48
|
+
|
49
|
+
# Initialize currency components
|
50
|
+
self.currency_validator = CurrencyValidator(self)
|
51
|
+
self.currency_converter = CurrencyConverter(self)
|
52
|
+
|
53
|
+
# Initialize provider components
|
54
|
+
self.provider_client = ProviderClient(self.provider_registry, self)
|
55
|
+
self.status_mapper = StatusMapper()
|
56
|
+
|
57
|
+
# Initialize utilities
|
58
|
+
self.data_converter = DataConverter()
|
59
|
+
self.stats_calculator = StatisticsCalculator(self)
|
60
|
+
|
61
|
+
# Initialize operations
|
62
|
+
self.payment_creator = PaymentCreator(
|
63
|
+
self.provider_registry,
|
64
|
+
self.currency_validator,
|
65
|
+
self
|
66
|
+
)
|
67
|
+
self.payment_canceller = PaymentCanceller(self)
|
68
|
+
self.status_checker = StatusChecker(self.provider_client, self)
|
69
|
+
|
38
70
|
def create_payment(self, request: PaymentCreateRequest) -> PaymentResult:
|
39
71
|
"""
|
40
|
-
Create new payment
|
41
|
-
|
72
|
+
Create new payment.
|
73
|
+
|
74
|
+
Delegates to PaymentCreator.
|
75
|
+
|
42
76
|
Args:
|
43
77
|
request: Payment creation request with validation
|
44
|
-
|
78
|
+
|
45
79
|
Returns:
|
46
80
|
PaymentResult: Result with payment data or error
|
47
81
|
"""
|
@@ -49,8 +83,7 @@ class PaymentService(BaseService):
|
|
49
83
|
# Validate request
|
50
84
|
if isinstance(request, dict):
|
51
85
|
request = PaymentCreateRequest(**request)
|
52
|
-
|
53
|
-
|
86
|
+
|
54
87
|
# Get user
|
55
88
|
try:
|
56
89
|
user = User.objects.get(id=request.user_id)
|
@@ -60,463 +93,80 @@ class PaymentService(BaseService):
|
|
60
93
|
message=f"User {request.user_id} not found",
|
61
94
|
error_code="user_not_found"
|
62
95
|
)
|
63
|
-
|
64
|
-
#
|
65
|
-
|
66
|
-
|
67
|
-
return PaymentResult(
|
68
|
-
success=False,
|
69
|
-
message=currency_result.message,
|
70
|
-
error_code=currency_result.error_code
|
71
|
-
)
|
72
|
-
|
73
|
-
# Get provider for payment creation
|
74
|
-
provider = self.provider_registry.get_provider(request.provider)
|
75
|
-
if not provider:
|
76
|
-
return PaymentResult(
|
77
|
-
success=False,
|
78
|
-
message=f"Provider {request.provider} not available",
|
79
|
-
error_code="provider_not_available"
|
80
|
-
)
|
81
|
-
|
82
|
-
# Create payment in database first
|
83
|
-
def create_payment_transaction():
|
84
|
-
currency = currency_result.data['currency']
|
85
|
-
payment = UniversalPayment.objects.create(
|
86
|
-
user=user,
|
87
|
-
amount_usd=request.amount_usd,
|
88
|
-
currency=currency,
|
89
|
-
network=currency.native_networks.first(), # Use first native network
|
90
|
-
provider=request.provider,
|
91
|
-
status=UniversalPayment.PaymentStatus.PENDING,
|
92
|
-
status_changed_at=timezone.now(), # Track initial status setting
|
93
|
-
callback_url=request.callback_url,
|
94
|
-
cancel_url=request.cancel_url,
|
95
|
-
description=request.description,
|
96
|
-
expires_at=timezone.now() + timezone.timedelta(hours=1) # 1 hour expiry
|
97
|
-
)
|
98
|
-
return payment
|
99
|
-
|
100
|
-
payment = self._execute_with_transaction(create_payment_transaction)
|
101
|
-
|
102
|
-
|
103
|
-
# Create payment with provider
|
104
|
-
from ..providers.models import PaymentRequest as ProviderPaymentRequest
|
105
|
-
|
106
|
-
# Use provider_currency_code from metadata if available, otherwise use original currency_code
|
107
|
-
provider_currency_code = request.metadata.get('provider_currency_code', request.currency_code)
|
108
|
-
|
109
|
-
provider_request = ProviderPaymentRequest(
|
110
|
-
amount_usd=request.amount_usd,
|
111
|
-
currency_code=provider_currency_code, # Use provider-specific currency code
|
112
|
-
order_id=str(payment.id),
|
113
|
-
callback_url=request.callback_url,
|
114
|
-
cancel_url=request.cancel_url,
|
115
|
-
description=request.description,
|
116
|
-
metadata=request.metadata
|
117
|
-
)
|
118
|
-
|
119
|
-
provider_response = provider.create_payment(provider_request)
|
120
|
-
|
121
|
-
|
122
|
-
# Update payment with provider response
|
123
|
-
if provider_response.success:
|
124
|
-
def update_payment_transaction():
|
125
|
-
payment.provider_payment_id = provider_response.provider_payment_id
|
126
|
-
payment.pay_amount = provider_response.amount # Fix: use pay_amount instead of crypto_amount
|
127
|
-
payment.payment_url = provider_response.payment_url
|
128
|
-
payment.qr_code_url = provider_response.qr_code_url
|
129
|
-
payment.pay_address = provider_response.wallet_address # Fix: use pay_address instead of wallet_address
|
130
|
-
if provider_response.expires_at:
|
131
|
-
payment.expires_at = provider_response.expires_at
|
132
|
-
payment.save()
|
133
|
-
return payment
|
134
|
-
|
135
|
-
payment = self._execute_with_transaction(update_payment_transaction)
|
136
|
-
|
137
|
-
# Convert to PaymentData using our helper method
|
138
|
-
payment_data = self._convert_payment_to_data(payment)
|
139
|
-
|
140
|
-
self._log_operation(
|
141
|
-
"create_payment",
|
142
|
-
True,
|
143
|
-
payment_id=str(payment.id),
|
144
|
-
user_id=request.user_id,
|
145
|
-
amount_usd=request.amount_usd
|
146
|
-
)
|
147
|
-
|
148
|
-
return PaymentResult(
|
149
|
-
success=True,
|
150
|
-
message="Payment created successfully",
|
151
|
-
payment_id=str(payment.id),
|
152
|
-
status=payment.status,
|
153
|
-
amount_usd=payment.amount_usd,
|
154
|
-
crypto_amount=payment.pay_amount,
|
155
|
-
currency_code=payment.currency.code,
|
156
|
-
payment_url=payment.payment_url,
|
157
|
-
expires_at=payment.expires_at,
|
158
|
-
data={'payment': payment_data.model_dump()}
|
159
|
-
)
|
160
|
-
|
161
|
-
else:
|
162
|
-
# Mark payment as failed if provider creation failed
|
163
|
-
self.logger.error("❌ PAYMENT SERVICE: Provider creation failed", extra={
|
164
|
-
'payment_id': str(payment.id),
|
165
|
-
'provider_error': getattr(provider_response, 'error_message', 'Unknown error')
|
166
|
-
})
|
167
|
-
payment.mark_failed(
|
168
|
-
reason=provider_response.error_message,
|
169
|
-
error_code="provider_creation_failed"
|
170
|
-
)
|
171
|
-
|
172
|
-
# Return error result when provider fails
|
173
|
-
self._log_operation(
|
174
|
-
"create_payment",
|
175
|
-
False,
|
176
|
-
payment_id=str(payment.id),
|
177
|
-
user_id=request.user_id,
|
178
|
-
amount_usd=request.amount_usd,
|
179
|
-
error=provider_response.error_message
|
180
|
-
)
|
181
|
-
|
182
|
-
return PaymentResult(
|
183
|
-
success=False,
|
184
|
-
message=provider_response.error_message or "Payment creation failed",
|
185
|
-
error_code="provider_creation_failed",
|
186
|
-
payment_id=str(payment.id),
|
187
|
-
status=payment.status,
|
188
|
-
data={'error_details': getattr(provider_response, 'raw_response', {})}
|
189
|
-
)
|
190
|
-
|
96
|
+
|
97
|
+
# Delegate to creator
|
98
|
+
return self.payment_creator.create_payment(request, user)
|
99
|
+
|
191
100
|
except Exception as e:
|
192
101
|
return PaymentResult(**self._handle_exception(
|
193
102
|
"create_payment", e,
|
194
103
|
user_id=request.user_id if hasattr(request, 'user_id') else None
|
195
104
|
).model_dump())
|
196
|
-
|
105
|
+
|
197
106
|
def get_payment_status(self, request: PaymentStatusRequest) -> PaymentResult:
|
198
107
|
"""
|
199
|
-
Get payment status
|
200
|
-
|
108
|
+
Get payment status.
|
109
|
+
|
110
|
+
Delegates to StatusChecker.
|
111
|
+
|
201
112
|
Args:
|
202
113
|
request: Payment status request
|
203
|
-
|
114
|
+
|
204
115
|
Returns:
|
205
116
|
PaymentResult: Current payment status
|
206
117
|
"""
|
207
|
-
|
208
|
-
|
209
|
-
if isinstance(request, dict):
|
210
|
-
request = PaymentStatusRequest(**request)
|
211
|
-
|
212
|
-
self.logger.debug("Getting payment status", extra={
|
213
|
-
'payment_id': request.payment_id,
|
214
|
-
'force_provider_check': request.force_provider_check
|
215
|
-
})
|
216
|
-
|
217
|
-
# Get payment
|
218
|
-
try:
|
219
|
-
payment = UniversalPayment.objects.get(id=request.payment_id)
|
220
|
-
except UniversalPayment.DoesNotExist:
|
221
|
-
return PaymentResult(
|
222
|
-
success=False,
|
223
|
-
message=f"Payment {request.payment_id} not found",
|
224
|
-
error_code="payment_not_found"
|
225
|
-
)
|
226
|
-
|
227
|
-
# Check user authorization if provided
|
228
|
-
if request.user_id and payment.user_id != request.user_id:
|
229
|
-
return PaymentResult(
|
230
|
-
success=False,
|
231
|
-
message="Access denied to payment",
|
232
|
-
error_code="access_denied"
|
233
|
-
)
|
234
|
-
|
235
|
-
# Force provider check if requested
|
236
|
-
if request.force_provider_check:
|
237
|
-
provider_result = self._check_provider_status(payment)
|
238
|
-
if provider_result.success and provider_result.data.get('status_changed'):
|
239
|
-
# Reload payment if status was updated
|
240
|
-
payment.refresh_from_db()
|
241
|
-
|
242
|
-
# Convert to PaymentData using from_attributes
|
243
|
-
payment_data = self._convert_payment_to_data(payment)
|
244
|
-
|
245
|
-
return PaymentResult(
|
246
|
-
success=True,
|
247
|
-
message="Payment status retrieved",
|
248
|
-
payment_id=str(payment.id),
|
249
|
-
status=payment.status,
|
250
|
-
amount_usd=payment.amount_usd,
|
251
|
-
crypto_amount=payment.pay_amount,
|
252
|
-
currency_code=payment.currency.code,
|
253
|
-
provider_payment_id=payment.provider_payment_id,
|
254
|
-
payment_url=payment.payment_url,
|
255
|
-
qr_code_url=getattr(payment, 'qr_code_url', None),
|
256
|
-
wallet_address=payment.pay_address,
|
257
|
-
expires_at=payment.expires_at,
|
258
|
-
data={'payment': payment_data.model_dump()}
|
259
|
-
)
|
260
|
-
|
261
|
-
except Exception as e:
|
262
|
-
return PaymentResult(**self._handle_exception(
|
263
|
-
"get_payment_status", e,
|
264
|
-
payment_id=request.payment_id if hasattr(request, 'payment_id') else None
|
265
|
-
).model_dump())
|
266
|
-
|
118
|
+
return self.status_checker.get_payment_status(request)
|
119
|
+
|
267
120
|
def cancel_payment(self, payment_id: str, reason: str = None) -> PaymentResult:
|
268
121
|
"""
|
269
|
-
Cancel payment
|
270
|
-
|
122
|
+
Cancel payment.
|
123
|
+
|
124
|
+
Delegates to PaymentCanceller.
|
125
|
+
|
271
126
|
Args:
|
272
127
|
payment_id: Payment ID to cancel
|
273
128
|
reason: Cancellation reason
|
274
|
-
|
129
|
+
|
275
130
|
Returns:
|
276
131
|
PaymentResult: Cancellation result
|
277
132
|
"""
|
278
|
-
|
279
|
-
|
280
|
-
'payment_id': payment_id,
|
281
|
-
'reason': reason
|
282
|
-
})
|
283
|
-
|
284
|
-
# Get payment
|
285
|
-
try:
|
286
|
-
payment = UniversalPayment.objects.get(id=payment_id)
|
287
|
-
except UniversalPayment.DoesNotExist:
|
288
|
-
return PaymentResult(
|
289
|
-
success=False,
|
290
|
-
message=f"Payment {payment_id} not found",
|
291
|
-
error_code="payment_not_found"
|
292
|
-
)
|
293
|
-
|
294
|
-
# Check if payment can be cancelled
|
295
|
-
if not payment.can_be_cancelled():
|
296
|
-
return PaymentResult(
|
297
|
-
success=False,
|
298
|
-
message=f"Payment {payment_id} cannot be cancelled (status: {payment.status})",
|
299
|
-
error_code="cannot_cancel"
|
300
|
-
)
|
301
|
-
|
302
|
-
# Cancel using manager
|
303
|
-
def cancel_payment_transaction():
|
304
|
-
return payment.cancel(reason)
|
305
|
-
|
306
|
-
success = self._execute_with_transaction(cancel_payment_transaction)
|
307
|
-
|
308
|
-
if success:
|
309
|
-
payment.refresh_from_db()
|
310
|
-
payment_data = self._convert_payment_to_data(payment)
|
311
|
-
|
312
|
-
self._log_operation(
|
313
|
-
"cancel_payment",
|
314
|
-
True,
|
315
|
-
payment_id=payment_id,
|
316
|
-
reason=reason
|
317
|
-
)
|
318
|
-
|
319
|
-
return PaymentResult(
|
320
|
-
success=True,
|
321
|
-
message="Payment cancelled successfully",
|
322
|
-
payment_id=str(payment.id),
|
323
|
-
status=payment.status,
|
324
|
-
data={'payment': payment_data.model_dump()}
|
325
|
-
)
|
326
|
-
else:
|
327
|
-
return PaymentResult(
|
328
|
-
success=False,
|
329
|
-
message="Failed to cancel payment",
|
330
|
-
error_code="cancel_failed"
|
331
|
-
)
|
332
|
-
|
333
|
-
except Exception as e:
|
334
|
-
return PaymentResult(**self._handle_exception(
|
335
|
-
"cancel_payment", e,
|
336
|
-
payment_id=payment_id
|
337
|
-
).model_dump())
|
338
|
-
|
339
|
-
def _validate_currency(self, currency_code: str) -> ServiceOperationResult:
|
340
|
-
"""Validate currency is supported."""
|
341
|
-
try:
|
342
|
-
currency = Currency.objects.get(code=currency_code, is_active=True)
|
343
|
-
|
344
|
-
# Check if currency is supported by any provider
|
345
|
-
provider_currency = ProviderCurrency.objects.filter(
|
346
|
-
currency=currency,
|
347
|
-
is_enabled=True
|
348
|
-
).first()
|
349
|
-
|
350
|
-
if not provider_currency:
|
351
|
-
return self._create_error_result(
|
352
|
-
f"Currency {currency_code} not supported by any provider",
|
353
|
-
"currency_not_supported"
|
354
|
-
)
|
355
|
-
|
356
|
-
return self._create_success_result(
|
357
|
-
"Currency is valid",
|
358
|
-
{'currency': currency} # Wrap in dict for Pydantic
|
359
|
-
)
|
360
|
-
|
361
|
-
except Currency.DoesNotExist:
|
362
|
-
return self._create_error_result(
|
363
|
-
f"Currency {currency_code} not found",
|
364
|
-
"currency_not_found"
|
365
|
-
)
|
366
|
-
|
367
|
-
def _convert_usd_to_crypto(self, amount_usd: float, currency_code: str) -> ServiceOperationResult:
|
368
|
-
"""Convert USD amount to cryptocurrency."""
|
369
|
-
try:
|
370
|
-
# Use django_currency module for conversion
|
371
|
-
crypto_amount = convert_currency(amount_usd, 'USD', currency_code)
|
372
|
-
|
373
|
-
return self._create_success_result(
|
374
|
-
"Currency converted successfully",
|
375
|
-
{
|
376
|
-
'amount_usd': amount_usd,
|
377
|
-
'crypto_amount': Decimal(str(crypto_amount)),
|
378
|
-
'currency_code': currency_code,
|
379
|
-
'exchange_rate': get_exchange_rate('USD', currency_code)
|
380
|
-
}
|
381
|
-
)
|
382
|
-
|
383
|
-
except Exception as e:
|
384
|
-
return self._create_error_result(
|
385
|
-
f"Currency conversion failed: {e}",
|
386
|
-
"conversion_failed"
|
387
|
-
)
|
388
|
-
|
389
|
-
def _check_provider_status(self, payment: UniversalPayment) -> ServiceOperationResult:
|
390
|
-
"""Check payment status with provider and update database if needed."""
|
391
|
-
try:
|
392
|
-
if not payment.provider_payment_id:
|
393
|
-
return self._create_success_result(
|
394
|
-
"No provider payment ID, skipping provider check",
|
395
|
-
{'status_changed': False}
|
396
|
-
)
|
397
|
-
|
398
|
-
# Get provider instance
|
399
|
-
provider = self.provider_registry.get_provider(payment.provider)
|
400
|
-
if not provider:
|
401
|
-
return self._create_error_result(
|
402
|
-
f"Provider {payment.provider} not available",
|
403
|
-
"provider_not_available"
|
404
|
-
)
|
405
|
-
|
406
|
-
# Check status with provider
|
407
|
-
provider_response = provider.get_payment_status(payment.provider_payment_id)
|
408
|
-
|
409
|
-
if not provider_response.success:
|
410
|
-
self.logger.warning(f"Provider status check failed for payment {payment.id}", extra={
|
411
|
-
'payment_id': str(payment.id),
|
412
|
-
'provider_payment_id': payment.provider_payment_id,
|
413
|
-
'error': provider_response.error_message
|
414
|
-
})
|
415
|
-
return self._create_error_result(
|
416
|
-
f"Provider status check failed: {provider_response.error_message}",
|
417
|
-
"provider_check_failed"
|
418
|
-
)
|
419
|
-
|
420
|
-
# Map provider status to our status
|
421
|
-
original_status = payment.status
|
422
|
-
new_status = self._map_provider_status(provider_response.status, payment.provider)
|
423
|
-
|
424
|
-
status_changed = False
|
425
|
-
|
426
|
-
# Update payment if status changed
|
427
|
-
if new_status and new_status != original_status:
|
428
|
-
def update_payment_transaction():
|
429
|
-
nonlocal status_changed
|
430
|
-
|
431
|
-
# Update status
|
432
|
-
payment.status = new_status
|
433
|
-
payment.status_changed_at = timezone.now() # Track when status changed
|
434
|
-
|
435
|
-
# Update other fields from provider response
|
436
|
-
if provider_response.transaction_hash and not payment.transaction_hash:
|
437
|
-
payment.transaction_hash = provider_response.transaction_hash
|
438
|
-
|
439
|
-
if provider_response.confirmations_count is not None:
|
440
|
-
payment.confirmations_count = provider_response.confirmations_count
|
441
|
-
|
442
|
-
# Mark as completed if status is completed/confirmed
|
443
|
-
if new_status in ['completed', 'confirmed'] and not payment.completed_at:
|
444
|
-
payment.completed_at = timezone.now()
|
445
|
-
|
446
|
-
payment.save()
|
447
|
-
status_changed = True
|
448
|
-
|
449
|
-
return True
|
450
|
-
|
451
|
-
# Execute in transaction
|
452
|
-
self._execute_with_transaction(update_payment_transaction)
|
453
|
-
|
454
|
-
self.logger.info(f"Payment status updated from provider", extra={
|
455
|
-
'payment_id': str(payment.id),
|
456
|
-
'provider_payment_id': payment.provider_payment_id,
|
457
|
-
'status_change': f"{original_status} -> {new_status}",
|
458
|
-
'provider': payment.provider
|
459
|
-
})
|
460
|
-
|
461
|
-
return self._create_success_result(
|
462
|
-
f"Provider status checked: {original_status} -> {new_status}" if status_changed else "No status change",
|
463
|
-
{
|
464
|
-
'status_changed': status_changed,
|
465
|
-
'original_status': original_status,
|
466
|
-
'new_status': new_status,
|
467
|
-
'provider_response': provider_response.raw_response
|
468
|
-
}
|
469
|
-
)
|
470
|
-
|
471
|
-
except Exception as e:
|
472
|
-
self.logger.error(f"Provider status check failed", extra={
|
473
|
-
'payment_id': str(payment.id),
|
474
|
-
'error': str(e)
|
475
|
-
})
|
476
|
-
return self._create_error_result(
|
477
|
-
f"Provider check failed: {e}",
|
478
|
-
"provider_check_failed"
|
479
|
-
)
|
480
|
-
|
481
|
-
def _map_provider_status(self, provider_status: str, provider: str) -> Optional[str]:
|
482
|
-
"""Map provider-specific status to universal status."""
|
483
|
-
if provider == 'nowpayments':
|
484
|
-
status_mapping = {
|
485
|
-
'waiting': 'pending',
|
486
|
-
'confirming': 'confirming',
|
487
|
-
'confirmed': 'confirmed',
|
488
|
-
'finished': 'completed',
|
489
|
-
'failed': 'failed',
|
490
|
-
'refunded': 'refunded',
|
491
|
-
'expired': 'expired'
|
492
|
-
}
|
493
|
-
return status_mapping.get(provider_status.lower())
|
494
|
-
|
495
|
-
# Default mapping for unknown providers
|
496
|
-
return provider_status.lower() if provider_status else None
|
497
|
-
|
133
|
+
return self.payment_canceller.cancel_payment(payment_id, reason)
|
134
|
+
|
498
135
|
def get_user_payments(
|
499
|
-
self,
|
500
|
-
user_id: int,
|
136
|
+
self,
|
137
|
+
user_id: int,
|
501
138
|
status: Optional[str] = None,
|
502
139
|
limit: int = 50,
|
503
140
|
offset: int = 0
|
504
141
|
) -> ServiceOperationResult:
|
505
|
-
"""
|
142
|
+
"""
|
143
|
+
Get user payments with pagination.
|
144
|
+
|
145
|
+
Simple query method - kept in main service.
|
146
|
+
|
147
|
+
Args:
|
148
|
+
user_id: User ID
|
149
|
+
status: Optional status filter
|
150
|
+
limit: Page size
|
151
|
+
offset: Page offset
|
152
|
+
|
153
|
+
Returns:
|
154
|
+
ServiceOperationResult with payments list
|
155
|
+
"""
|
506
156
|
try:
|
507
157
|
queryset = UniversalPayment.objects.filter(user_id=user_id)
|
508
|
-
|
158
|
+
|
509
159
|
if status:
|
510
160
|
queryset = queryset.filter(status=status)
|
511
|
-
|
161
|
+
|
512
162
|
total_count = queryset.count()
|
513
163
|
payments = queryset.order_by('-created_at')[offset:offset + limit]
|
514
|
-
|
164
|
+
|
515
165
|
payment_data = []
|
516
166
|
for payment in payments:
|
517
|
-
payment_obj = self.
|
167
|
+
payment_obj = self.data_converter.payment_to_data(payment)
|
518
168
|
payment_data.append(payment_obj.model_dump())
|
519
|
-
|
169
|
+
|
520
170
|
return self._create_success_result(
|
521
171
|
f"Retrieved {len(payment_data)} payments",
|
522
172
|
{
|
@@ -527,180 +177,42 @@ class PaymentService(BaseService):
|
|
527
177
|
'has_more': offset + limit < total_count
|
528
178
|
}
|
529
179
|
)
|
530
|
-
|
180
|
+
|
531
181
|
except Exception as e:
|
532
182
|
return self._handle_exception(
|
533
183
|
"get_user_payments", e,
|
534
184
|
user_id=user_id
|
535
185
|
)
|
536
|
-
|
537
|
-
def _convert_payment_to_data(self, payment: UniversalPayment) -> PaymentData:
|
538
|
-
"""Convert Django UniversalPayment to PaymentData."""
|
539
|
-
return PaymentData(
|
540
|
-
id=str(payment.id),
|
541
|
-
user_id=payment.user_id,
|
542
|
-
amount_usd=float(payment.amount_usd),
|
543
|
-
crypto_amount=payment.pay_amount,
|
544
|
-
currency_code=payment.currency.code,
|
545
|
-
provider=payment.provider,
|
546
|
-
status=payment.status,
|
547
|
-
provider_payment_id=payment.provider_payment_id,
|
548
|
-
payment_url=payment.payment_url,
|
549
|
-
qr_code_url=getattr(payment, 'qr_code_url', None),
|
550
|
-
wallet_address=payment.pay_address,
|
551
|
-
callback_url=payment.callback_url,
|
552
|
-
cancel_url=payment.cancel_url,
|
553
|
-
description=payment.description,
|
554
|
-
metadata={},
|
555
|
-
created_at=payment.created_at,
|
556
|
-
updated_at=payment.updated_at,
|
557
|
-
expires_at=payment.expires_at,
|
558
|
-
completed_at=getattr(payment, 'completed_at', None)
|
559
|
-
)
|
560
|
-
|
186
|
+
|
561
187
|
def get_payment_stats(self, days: int = 30) -> ServiceOperationResult:
|
562
|
-
"""Get payment statistics."""
|
563
|
-
try:
|
564
|
-
from datetime import timedelta
|
565
|
-
|
566
|
-
since = timezone.now() - timedelta(days=days)
|
567
|
-
|
568
|
-
stats = UniversalPayment.objects.filter(
|
569
|
-
created_at__gte=since
|
570
|
-
).aggregate(
|
571
|
-
total_payments=models.Count('id'),
|
572
|
-
total_amount_usd=models.Sum('amount_usd'),
|
573
|
-
completed_payments=models.Count(
|
574
|
-
'id',
|
575
|
-
filter=models.Q(status=UniversalPayment.PaymentStatus.COMPLETED)
|
576
|
-
),
|
577
|
-
failed_payments=models.Count(
|
578
|
-
'id',
|
579
|
-
filter=models.Q(status=UniversalPayment.PaymentStatus.FAILED)
|
580
|
-
)
|
581
|
-
)
|
582
|
-
|
583
|
-
# Calculate success rate
|
584
|
-
total = stats['total_payments'] or 0
|
585
|
-
completed = stats['completed_payments'] or 0
|
586
|
-
success_rate = (completed / total * 100) if total > 0 else 0
|
587
|
-
|
588
|
-
stats['success_rate'] = round(success_rate, 2)
|
589
|
-
stats['period_days'] = days
|
590
|
-
|
591
|
-
return self._create_success_result(
|
592
|
-
f"Payment statistics for {days} days",
|
593
|
-
stats
|
594
|
-
)
|
595
|
-
|
596
|
-
except Exception as e:
|
597
|
-
return self._handle_exception("get_payment_stats", e)
|
598
|
-
|
599
|
-
def _check_provider_status(self, payment: 'UniversalPayment') -> ServiceOperationResult:
|
600
188
|
"""
|
601
|
-
|
602
|
-
|
189
|
+
Get payment statistics.
|
190
|
+
|
191
|
+
Delegates to StatisticsCalculator.
|
192
|
+
|
603
193
|
Args:
|
604
|
-
|
605
|
-
|
194
|
+
days: Number of days to analyze
|
195
|
+
|
606
196
|
Returns:
|
607
|
-
ServiceOperationResult
|
197
|
+
ServiceOperationResult with statistics
|
198
|
+
"""
|
199
|
+
return self.stats_calculator.get_payment_stats(days)
|
200
|
+
|
201
|
+
# Private helper methods for backward compatibility
|
202
|
+
|
203
|
+
def _get_user(self, user_id: int):
|
204
|
+
"""
|
205
|
+
Get user by ID.
|
206
|
+
|
207
|
+
Helper method for internal use.
|
208
|
+
|
209
|
+
Args:
|
210
|
+
user_id: User ID
|
211
|
+
|
212
|
+
Returns:
|
213
|
+
User instance or None
|
608
214
|
"""
|
609
215
|
try:
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
self.logger.debug("Checking provider status", extra={
|
614
|
-
'payment_id': str(payment.id),
|
615
|
-
'current_status': payment.status,
|
616
|
-
'provider': payment.provider
|
617
|
-
})
|
618
|
-
|
619
|
-
# Get provider instance
|
620
|
-
registry = ProviderRegistry()
|
621
|
-
provider = registry.get_provider(payment.provider)
|
622
|
-
|
623
|
-
if not provider:
|
624
|
-
return self._create_error_result(
|
625
|
-
f"Provider {payment.provider} not found",
|
626
|
-
"provider_not_found"
|
627
|
-
)
|
628
|
-
|
629
|
-
# Get status from provider
|
630
|
-
provider_response = provider.get_payment_status(payment.provider_payment_id)
|
631
|
-
|
632
|
-
if not provider_response.success:
|
633
|
-
self.logger.warning("Provider status check failed", extra={
|
634
|
-
'payment_id': str(payment.id),
|
635
|
-
'provider': payment.provider,
|
636
|
-
'error': provider_response.error_message
|
637
|
-
})
|
638
|
-
return self._create_error_result(
|
639
|
-
f"Provider status check failed: {provider_response.error_message}",
|
640
|
-
"provider_check_failed"
|
641
|
-
)
|
642
|
-
|
643
|
-
# Map provider status to universal status
|
644
|
-
provider_status = provider_response.data.get('status', '').lower()
|
645
|
-
status_mapping = {
|
646
|
-
'waiting': UniversalPayment.PaymentStatus.PENDING,
|
647
|
-
'confirming': UniversalPayment.PaymentStatus.PENDING,
|
648
|
-
'confirmed': UniversalPayment.PaymentStatus.COMPLETED,
|
649
|
-
'sending': UniversalPayment.PaymentStatus.PENDING,
|
650
|
-
'partially_paid': UniversalPayment.PaymentStatus.PENDING,
|
651
|
-
'finished': UniversalPayment.PaymentStatus.COMPLETED,
|
652
|
-
'failed': UniversalPayment.PaymentStatus.FAILED,
|
653
|
-
'refunded': UniversalPayment.PaymentStatus.FAILED,
|
654
|
-
'expired': UniversalPayment.PaymentStatus.EXPIRED,
|
655
|
-
}
|
656
|
-
|
657
|
-
new_status = status_mapping.get(provider_status, payment.status)
|
658
|
-
status_changed = new_status != payment.status
|
659
|
-
|
660
|
-
# Update payment if status changed
|
661
|
-
if status_changed:
|
662
|
-
with transaction.atomic():
|
663
|
-
# Prepare extra fields from provider response
|
664
|
-
provider_data = provider_response.data
|
665
|
-
|
666
|
-
extra_fields = PaymentStatusUpdateFields(
|
667
|
-
transaction_hash=provider_data.get('transaction_hash'),
|
668
|
-
confirmations_count=provider_data.get('confirmations_count')
|
669
|
-
)
|
670
|
-
|
671
|
-
# Use manager method for consistent status updates
|
672
|
-
from ...models import UniversalPayment
|
673
|
-
success = UniversalPayment.objects.update_payment_status(
|
674
|
-
payment, new_status, extra_fields
|
675
|
-
)
|
676
|
-
|
677
|
-
if not success:
|
678
|
-
return self._create_error_result(
|
679
|
-
"Failed to update payment status",
|
680
|
-
"status_update_failed"
|
681
|
-
)
|
682
|
-
|
683
|
-
self.logger.info("Payment status updated", extra={
|
684
|
-
'payment_id': str(payment.id),
|
685
|
-
'old_status': payment.status,
|
686
|
-
'new_status': new_status,
|
687
|
-
'provider_status': provider_status
|
688
|
-
})
|
689
|
-
|
690
|
-
return self._create_success_result(
|
691
|
-
"Provider status checked",
|
692
|
-
{
|
693
|
-
'status_changed': status_changed,
|
694
|
-
'old_status': payment.status if not status_changed else None,
|
695
|
-
'new_status': new_status,
|
696
|
-
'provider_status': provider_status,
|
697
|
-
'provider_response': provider_response.data
|
698
|
-
}
|
699
|
-
)
|
700
|
-
|
701
|
-
except Exception as e:
|
702
|
-
self.logger.error("Error checking provider status", extra={
|
703
|
-
'payment_id': str(payment.id),
|
704
|
-
'error': str(e)
|
705
|
-
})
|
706
|
-
return self._handle_exception("_check_provider_status", e)
|
216
|
+
return User.objects.get(id=user_id)
|
217
|
+
except User.DoesNotExist:
|
218
|
+
return None
|