django-cfg 1.2.22__py3-none-any.whl → 1.2.23__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.
Files changed (67) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/payments/admin/__init__.py +23 -0
  3. django_cfg/apps/payments/admin/api_keys_admin.py +347 -0
  4. django_cfg/apps/payments/admin/balance_admin.py +434 -0
  5. django_cfg/apps/payments/admin/currencies_admin.py +186 -0
  6. django_cfg/apps/payments/admin/filters.py +259 -0
  7. django_cfg/apps/payments/admin/payments_admin.py +142 -0
  8. django_cfg/apps/payments/admin/subscriptions_admin.py +227 -0
  9. django_cfg/apps/payments/admin/tariffs_admin.py +199 -0
  10. django_cfg/apps/payments/config/__init__.py +87 -0
  11. django_cfg/apps/payments/config/module.py +162 -0
  12. django_cfg/apps/payments/config/providers.py +93 -0
  13. django_cfg/apps/payments/config/settings.py +136 -0
  14. django_cfg/apps/payments/config/utils.py +198 -0
  15. django_cfg/apps/payments/decorators.py +291 -0
  16. django_cfg/apps/payments/middleware/api_access.py +261 -0
  17. django_cfg/apps/payments/middleware/rate_limiting.py +216 -0
  18. django_cfg/apps/payments/middleware/usage_tracking.py +296 -0
  19. django_cfg/apps/payments/migrations/0001_initial.py +32 -11
  20. django_cfg/apps/payments/models/__init__.py +18 -0
  21. django_cfg/apps/payments/models/api_keys.py +2 -2
  22. django_cfg/apps/payments/models/balance.py +2 -2
  23. django_cfg/apps/payments/models/base.py +16 -0
  24. django_cfg/apps/payments/models/events.py +2 -2
  25. django_cfg/apps/payments/models/payments.py +2 -2
  26. django_cfg/apps/payments/models/subscriptions.py +2 -2
  27. django_cfg/apps/payments/services/__init__.py +58 -7
  28. django_cfg/apps/payments/services/billing/__init__.py +8 -0
  29. django_cfg/apps/payments/services/cache/__init__.py +15 -0
  30. django_cfg/apps/payments/services/cache/base.py +30 -0
  31. django_cfg/apps/payments/services/cache/simple_cache.py +135 -0
  32. django_cfg/apps/payments/services/core/__init__.py +17 -0
  33. django_cfg/apps/payments/services/core/balance_service.py +449 -0
  34. django_cfg/apps/payments/services/core/payment_service.py +393 -0
  35. django_cfg/apps/payments/services/core/subscription_service.py +616 -0
  36. django_cfg/apps/payments/services/internal_types.py +266 -0
  37. django_cfg/apps/payments/services/middleware/__init__.py +8 -0
  38. django_cfg/apps/payments/services/providers/__init__.py +19 -0
  39. django_cfg/apps/payments/services/providers/base.py +137 -0
  40. django_cfg/apps/payments/services/providers/cryptapi.py +262 -0
  41. django_cfg/apps/payments/services/providers/nowpayments.py +293 -0
  42. django_cfg/apps/payments/services/providers/registry.py +99 -0
  43. django_cfg/apps/payments/services/validators/__init__.py +8 -0
  44. django_cfg/apps/payments/signals/__init__.py +13 -0
  45. django_cfg/apps/payments/signals/api_key_signals.py +150 -0
  46. django_cfg/apps/payments/signals/payment_signals.py +127 -0
  47. django_cfg/apps/payments/signals/subscription_signals.py +196 -0
  48. django_cfg/apps/payments/urls.py +5 -5
  49. django_cfg/apps/payments/utils/__init__.py +42 -0
  50. django_cfg/apps/payments/utils/config_utils.py +243 -0
  51. django_cfg/apps/payments/utils/middleware_utils.py +228 -0
  52. django_cfg/apps/payments/utils/validation_utils.py +94 -0
  53. django_cfg/apps/support/signals.py +16 -4
  54. django_cfg/apps/support/templates/support/chat/ticket_chat.html +1 -1
  55. django_cfg/models/revolution.py +1 -1
  56. django_cfg/modules/base.py +1 -1
  57. django_cfg/modules/django_email.py +42 -4
  58. django_cfg/modules/django_unfold/dashboard.py +20 -0
  59. {django_cfg-1.2.22.dist-info → django_cfg-1.2.23.dist-info}/METADATA +2 -1
  60. {django_cfg-1.2.22.dist-info → django_cfg-1.2.23.dist-info}/RECORD +63 -26
  61. django_cfg/apps/payments/services/base.py +0 -68
  62. django_cfg/apps/payments/services/nowpayments.py +0 -78
  63. django_cfg/apps/payments/services/providers.py +0 -77
  64. django_cfg/apps/payments/services/redis_service.py +0 -215
  65. {django_cfg-1.2.22.dist-info → django_cfg-1.2.23.dist-info}/WHEEL +0 -0
  66. {django_cfg-1.2.22.dist-info → django_cfg-1.2.23.dist-info}/entry_points.txt +0 -0
  67. {django_cfg-1.2.22.dist-info → django_cfg-1.2.23.dist-info}/licenses/LICENSE +0 -0
@@ -1,5 +1,5 @@
1
1
  django_cfg/README.md,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- django_cfg/__init__.py,sha256=Xr1TpxIGCz8PGP-3n7OZqU3lCSjl9yNS4gakIXX7gqo,1631
2
+ django_cfg/__init__.py,sha256=UAyyUuLL3rXe_tPmnLDBZA3gajkyvd4DvOdkV8sJKko,1631
3
3
  django_cfg/apps.py,sha256=k84brkeXJI7EgKZLEpTkM9YFZofKI4PzhFOn1cl9Msc,1656
4
4
  django_cfg/config.py,sha256=ME-JKaVzcdmaGhuc1YTkEWoMKSaUasNf1SBlNz-NfrM,1399
5
5
  django_cfg/urls.py,sha256=bpRFjMonQuk4UCUMxx4ueBX3YDNB7HXKFwEghQ3KR3o,793
@@ -269,7 +269,21 @@ django_cfg/apps/newsletter/views/subscriptions.py,sha256=marHLbRT6a6e8tMjKVtKG_0
269
269
  django_cfg/apps/newsletter/views/tracking.py,sha256=EJlAltpASzc_0nslVPJt-6mnIqI_QCEmz3MQEGPKzmU,2481
270
270
  django_cfg/apps/payments/__init__.py,sha256=Qb8viLJBKCclV9uuoXjxWXeDevXlbdJ76mV8g5hK7tk,237
271
271
  django_cfg/apps/payments/apps.py,sha256=FZgbLQFMeOJPs_892bM6X-3Edt3YPD2CP50HMekH7Sk,551
272
- django_cfg/apps/payments/urls.py,sha256=aJ4hC3quZvn1c_JJHVWd305DLFAHhtkBHGS-i7XfYCw,3508
272
+ django_cfg/apps/payments/decorators.py,sha256=A8MsH7hzw8i7Cl3Eh5Lcqd9Dbn_nubbb6BAI3eZMEgk,10960
273
+ django_cfg/apps/payments/urls.py,sha256=8Tr5Sr3FcTgUB4PRYYlb8MOkugAPgKT46Jd0uVyNruk,3473
274
+ django_cfg/apps/payments/admin/__init__.py,sha256=BUTbE4RfbqX3EBF-9oscrh7wQ2tuwCqofGYqlCz4QM4,666
275
+ django_cfg/apps/payments/admin/api_keys_admin.py,sha256=nMghMkKcDYvjZ4POVwpS59E6Dc7IVP9dHv-1OltpEes,11436
276
+ django_cfg/apps/payments/admin/balance_admin.py,sha256=Dwap1VZGwNJ4ZbELMDbiVIxQPmI80MWnsTudyZOvsxo,15386
277
+ django_cfg/apps/payments/admin/currencies_admin.py,sha256=mMcttxDJlbQNJBhbrUhjJJYY2n9uHg1-7Hn8DCH9pVU,5810
278
+ django_cfg/apps/payments/admin/filters.py,sha256=MCYFqs1t3XL0U4WZv4ONUbwqv0vLOo7sN7eDuSaVFd0,8504
279
+ django_cfg/apps/payments/admin/payments_admin.py,sha256=fYKiTgEn1mHNZ2bEamTdf0alyZXYi_GgzjuFB81Ib98,4356
280
+ django_cfg/apps/payments/admin/subscriptions_admin.py,sha256=mJRSyDd761f0LA64AnG19dzTUmiivqq9wZg4roqKYIo,6913
281
+ django_cfg/apps/payments/admin/tariffs_admin.py,sha256=6UiQyRRIKrDdmpi2IZOpDpLeJLHrebOGNc3OizlikLI,6422
282
+ django_cfg/apps/payments/config/__init__.py,sha256=eEfe8z-RKGLmNQaxXdngcUAVnlbq3VKzo9qKMZwh45A,1873
283
+ django_cfg/apps/payments/config/module.py,sha256=jm8CFJoFoYhUrd1tt7j0uZgFA7oMOTbLQM21UVZNpQw,6547
284
+ django_cfg/apps/payments/config/providers.py,sha256=NuLCdgKbQyMkyWvKKLsmMO4hwkjqT1ddIynsTBoAWAk,3845
285
+ django_cfg/apps/payments/config/settings.py,sha256=S0V_DH4EzU21wAGgyodSLGKcSGs3s14GcHAihFgdGMg,6569
286
+ django_cfg/apps/payments/config/utils.py,sha256=H-4IdmlbpYAknaGWVvuivT_5lyvWY8q2C50Wb8qnkD8,6549
273
287
  django_cfg/apps/payments/managers/__init__.py,sha256=uYiETCw-K5GXQyAwpcOa2Qxzof8sbtTNc_SghyW45YU,672
274
288
  django_cfg/apps/payments/managers/api_key_manager.py,sha256=xhViRKCLC_eR68hankMZQTcbzMgiRJdyr8KYKk01_dQ,928
275
289
  django_cfg/apps/payments/managers/balance_manager.py,sha256=Sm_EETlHO7LtlrWIqF65vFPS4FpPSNYo5oTgQwTFAZ4,13279
@@ -278,16 +292,19 @@ django_cfg/apps/payments/managers/payment_manager.py,sha256=udiTFIIVXsjqmlwzFwcT
278
292
  django_cfg/apps/payments/managers/subscription_manager.py,sha256=LHrw5INc1nSqfpw76bn7W2poEA-29FXfF5uhaxmq7l8,968
279
293
  django_cfg/apps/payments/managers/tariff_manager.py,sha256=Kor_0ApvROcH0UKlp1-9rYChtHWDeOFgUc1GG3IepxY,799
280
294
  django_cfg/apps/payments/middleware/__init__.py,sha256=ryn3emWBq15-kE8eJxX19gd-eTrk7JlpXqV2Cg8N_Wg,294
281
- django_cfg/apps/payments/migrations/0001_initial.py,sha256=u4as4J5YycFDAs4acQz9girjg2Hp9uTn8Hy7WZEUI6U,39911
295
+ django_cfg/apps/payments/middleware/api_access.py,sha256=JcAg0ntqjR_r1LTUmJ9DTp1F8Uv62_Hza5mo9Rn6hDw,9406
296
+ django_cfg/apps/payments/middleware/rate_limiting.py,sha256=MhpHQkTjoHdH-tsJNlZATQQ9d9R9PDTwrdw1gUt_ydo,7407
297
+ django_cfg/apps/payments/middleware/usage_tracking.py,sha256=i8KT5E0dN6zhQwY8A-wFWtpkc9FJpfTQ4qTnRV8onmw,11176
298
+ django_cfg/apps/payments/migrations/0001_initial.py,sha256=uLAGM9KXBHLuLt4NN9yY8qcelxZ0gxUQYwv2zfp9nus,40538
282
299
  django_cfg/apps/payments/migrations/__init__.py,sha256=OHlzxEGDJYKZz82orZXnh77xy-Okv6FcG3EKMfg9LzU,21
283
- django_cfg/apps/payments/models/__init__.py,sha256=UV1xEm1osEkWA5XmvBJSS5YU2fddqkvSum6-_15IBH0,882
284
- django_cfg/apps/payments/models/api_keys.py,sha256=6V9hOSPQT27qEWaygWrtmQPqCeZpgHZv1KAfsgXnS-Q,2476
285
- django_cfg/apps/payments/models/balance.py,sha256=wn63yjOVIbLbirAjKLAZm03ooXeOe3yOqDGVr_hiLZY,7026
286
- django_cfg/apps/payments/models/base.py,sha256=T8mQKFXlpLc05xZDmSdOfrOAc8s_mGnKqoSgEhaY9Kk,349
300
+ django_cfg/apps/payments/models/__init__.py,sha256=iYRu56c8oZCG5n52aWJqsKEKX44k0_2BiSFkN8jZ46U,1444
301
+ django_cfg/apps/payments/models/api_keys.py,sha256=HCtZuvpW7cYRpsSXkS3U3lk_smMQpgLRf7PHaM58MkQ,2484
302
+ django_cfg/apps/payments/models/balance.py,sha256=F7unVOEVZMUGNvPkvGTWsytxfvQKdV8eQSnEBPZkuDc,7052
303
+ django_cfg/apps/payments/models/base.py,sha256=iypC2MCPrASunrJepuY0y7fASVLDMDSqOwAWp-ovTDk,793
287
304
  django_cfg/apps/payments/models/currencies.py,sha256=lz6axNRmfoQa3od-EV78vbZQv3vh2FznxHzGBi20qIU,4106
288
- django_cfg/apps/payments/models/events.py,sha256=xIrAj8nyBq0NTc0YA-SolJSxG9Mr3ut4gyARsY-QMRM,2364
289
- django_cfg/apps/payments/models/payments.py,sha256=7CLgRFWPg8K5ajjXjG5_xy8wpACzTAMxOzOufbyIaAI,9540
290
- django_cfg/apps/payments/models/subscriptions.py,sha256=RuINi31j2DIhI_V_-HNvhdq8qa-woAGrUDeWOJOq3so,8167
305
+ django_cfg/apps/payments/models/events.py,sha256=flKdY3SRAx5UZyHw6wLh5TflHTyy2lQpdi5aa1NqJ1o,2372
306
+ django_cfg/apps/payments/models/payments.py,sha256=z_dgYWZBd09wBn5ZEp0jf-3osjJASb4UlWgdELL-f1g,9548
307
+ django_cfg/apps/payments/models/subscriptions.py,sha256=femsIzWxSZZdu1Ee_MgCYnnpMnXshejwb1_JPhbo8k8,8193
291
308
  django_cfg/apps/payments/models/tariffs.py,sha256=nqM3RPtu4itAHRvT6jAgZgQEdEIN1KgKnEljkaHGFDc,2749
292
309
  django_cfg/apps/payments/serializers/__init__.py,sha256=4l7Tu1NXztXtqceVbC3OxtWJir4n3_dFpVEgRgQ9J4k,1416
293
310
  django_cfg/apps/payments/serializers/api_keys.py,sha256=5qG3PEwIVfoNSC6XpbOBbC3l4umgld3kPJC5l_vsYV0,1396
@@ -296,11 +313,31 @@ django_cfg/apps/payments/serializers/currencies.py,sha256=SskSl3rjZBxxLnY_zsaJiF
296
313
  django_cfg/apps/payments/serializers/payments.py,sha256=BDhcD9DCea50Cz1PumLNVntgH40dOR55NzCXv4I0YOQ,2258
297
314
  django_cfg/apps/payments/serializers/subscriptions.py,sha256=16OileC0yxM90ERK3Uo5Jqe9DeyNf3tBwK-ZqDtEbPw,2795
298
315
  django_cfg/apps/payments/serializers/tariffs.py,sha256=54N5tFpc5NBIMyXuN7Re6pDNoQLJc2yZBIb5uVRhn-I,1698
299
- django_cfg/apps/payments/services/__init__.py,sha256=GigyUFB3pT1qpN1uV1-LZUXVQFPMNkbjh6i6sV4iZsw,280
300
- django_cfg/apps/payments/services/base.py,sha256=2xX42CnTDObNNc0atXwEWpozI1fAbh7Y4uOgBAoHGzY,2302
301
- django_cfg/apps/payments/services/nowpayments.py,sha256=K31_KFM9KvkKidbbgYBgnIwg7I49dFBTSkD9upyYkO8,2878
302
- django_cfg/apps/payments/services/providers.py,sha256=7t335fOtBwwqMa_HLpvuJ-dW3Zyi8AjkKApSurhidTY,2337
303
- django_cfg/apps/payments/services/redis_service.py,sha256=xarjV3ndkuXXTHy0N9yuLfEvcg0it4pNNQ-yghNwIqo,8193
316
+ django_cfg/apps/payments/services/__init__.py,sha256=rBXpwd9kR-zcrjDoBPivUjWpw3vPgAVNUGmpATX1T7Y,1786
317
+ django_cfg/apps/payments/services/internal_types.py,sha256=RgKpbGDIGhtFA4fQGwfI3OCiwaC2mOC8bEJP5ecZjdg,7862
318
+ django_cfg/apps/payments/services/billing/__init__.py,sha256=YHdL1RWo-VAotOdZaykArOd0NTGI0fuQ0B7nBRKn6Lg,149
319
+ django_cfg/apps/payments/services/cache/__init__.py,sha256=CA8cUptfUD5LwbAB9kPMDofdkJBD_o7Z3HHG4vXMoyA,318
320
+ django_cfg/apps/payments/services/cache/base.py,sha256=McWSpr6eNEXbhG2PBkQd_YkL-he-scoWNUVmKI4FohY,696
321
+ django_cfg/apps/payments/services/cache/simple_cache.py,sha256=N2i0yGOR5Y1SPl2hqsXcGyehCpwXdN9SQVrphO1yat0,4508
322
+ django_cfg/apps/payments/services/core/__init__.py,sha256=I7x8Ryz4htQ1LwwtWZRK-MeI_SYboCQ7cKtTxmoqfpk,382
323
+ django_cfg/apps/payments/services/core/balance_service.py,sha256=E1r620C25CzcNdkV8INBR8FbEtl02sPSboYtzLZMU9I,16400
324
+ django_cfg/apps/payments/services/core/payment_service.py,sha256=CaMGobTU7t7o-i0D_TbfPH15XJ1eilbOFJuBWzAM64g,14634
325
+ django_cfg/apps/payments/services/core/subscription_service.py,sha256=ORUHYaz9BQXKwyeRSuLSaKp20UDtRxAkl9PmdRwqxt8,22559
326
+ django_cfg/apps/payments/services/middleware/__init__.py,sha256=SAiK2KW4aFVdfjlNrV3Bv8F0qg_epqwbZXOV0WoS6yA,154
327
+ django_cfg/apps/payments/services/providers/__init__.py,sha256=A0R28JDaB-sI--wTJ-WegbpKXpVmDizTVTKqOYWvSPM,444
328
+ django_cfg/apps/payments/services/providers/base.py,sha256=kjWPb7EutZHaByDW3py8srAcnvZdE4oqj6Tp9xS9ADk,3807
329
+ django_cfg/apps/payments/services/providers/cryptapi.py,sha256=BrAhUHfoQJgmHpUq9I3hWloMLBzyRCqwNG6pMfs5Qlg,10149
330
+ django_cfg/apps/payments/services/providers/nowpayments.py,sha256=xsylxVaN5L5tYfv1j_OJzX_PSEBEq9pmBeRFG80rcCA,11490
331
+ django_cfg/apps/payments/services/providers/registry.py,sha256=uS0C9JutkDlhYjlSs_EiK7Qj3gJCEXFt-dgZNqwVVkw,3687
332
+ django_cfg/apps/payments/services/validators/__init__.py,sha256=_NgbT79oslNvBPPYRpSFnPkvnZuQ48Kk1dzSZIE2ZAU,140
333
+ django_cfg/apps/payments/signals/__init__.py,sha256=L3danrGQ-9tEfaAbkXOILPb2Bw_T4XRN4PodjMl1hvk,342
334
+ django_cfg/apps/payments/signals/api_key_signals.py,sha256=1Sj334_o9vTChrUjiH9KSM789T89S4xHywAncGmT4dE,5601
335
+ django_cfg/apps/payments/signals/payment_signals.py,sha256=XbfW0KLYuqEAl8O8PEkcD8dHxgeLPP6YGUFV5vrNvzQ,4823
336
+ django_cfg/apps/payments/signals/subscription_signals.py,sha256=FDXlR02fpS6ILzMJQjoK4Ln1v7Kb8vp_Ej9v_lqnVE0,7492
337
+ django_cfg/apps/payments/utils/__init__.py,sha256=uE3H5xO0cxMyxfM39qwmLWIL0QVzArCy0Ecr-poeL-U,1063
338
+ django_cfg/apps/payments/utils/config_utils.py,sha256=NvH8thOnlBK5jhgCFzPw6kbYOWIVhiE-YRfWv_U1SQk,8477
339
+ django_cfg/apps/payments/utils/middleware_utils.py,sha256=MTS9MY_8-VqpBVv8uQYH89rfeG8dTnUru38vVbbE-1o,6682
340
+ django_cfg/apps/payments/utils/validation_utils.py,sha256=xSxpjz5aAajSOIxDhaXHdB-Uf98MOi3uDKVx0jHmREo,2704
304
341
  django_cfg/apps/payments/views/__init__.py,sha256=00HRO1sDErjnFq2dedFAjBIyIhZZyZmv4DK98lHmx2Q,1613
305
342
  django_cfg/apps/payments/views/api_key_views.py,sha256=fG68roqOF9FShde68Qp_CBf0KJuf1chwK41RU0VqUJk,5542
306
343
  django_cfg/apps/payments/views/balance_views.py,sha256=rA_OJFizP_UBBr8OdWLQZQ51qlPFYoeJCYUzNXSw4S0,2509
@@ -314,7 +351,7 @@ django_cfg/apps/support/admin_filters.py,sha256=ZpKtetRxppRAMwIr-pwDbXAyh7qouDfT
314
351
  django_cfg/apps/support/apps.py,sha256=t-MzsKjXx6ZXACkG72Wa8GSmRYOERxfhrZUwUwjqCak,304
315
352
  django_cfg/apps/support/models.py,sha256=a_m4CWpfR7A2KlVRUYOGPdpNyNPJflD3Mq7EkiwzICA,2465
316
353
  django_cfg/apps/support/serializers.py,sha256=N-PP8d1H00mHSWT6x5DwDYrJoR3PS3AKrUW80L4lncs,1385
317
- django_cfg/apps/support/signals.py,sha256=z465Nr7xin7S82YA7v-E5bNlimpv3ewN7kLoJzlGxtQ,2816
354
+ django_cfg/apps/support/signals.py,sha256=W7lA3o1eoAwZQd92fX0GaiIXp9ptQRYtwn1Siz1FV9g,3706
318
355
  django_cfg/apps/support/urls.py,sha256=t3r4wGRUqQS-BDCQgSXwSF9pbucyvr7lo7_pJ1uZ52Q,899
319
356
  django_cfg/apps/support/admin/__init__.py,sha256=vVoUWfKj6qBHhwr6zG2ZH8ZpUegYNJvzuAxdFoHEo_0,153
320
357
  django_cfg/apps/support/admin/filters.py,sha256=ZpKtetRxppRAMwIr-pwDbXAyh7qouDfTCEZoo1YJfFs,2179
@@ -326,7 +363,7 @@ django_cfg/apps/support/migrations/0001_initial.py,sha256=AUUXqMiRbv_bRhogBWKWdr
326
363
  django_cfg/apps/support/migrations/0002_alter_message_ticket.py,sha256=MxdjTW7hJ1B0yQloKR3sVOgujOzSA6eZp5YCmCuXht0,571
327
364
  django_cfg/apps/support/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
328
365
  django_cfg/apps/support/templates/support/chat/access_denied.html,sha256=QdVXR-61aUHmh9Vlag_zXGcS_BG7a807LesIbduqnag,1094
329
- django_cfg/apps/support/templates/support/chat/ticket_chat.html,sha256=b2UvCQp122-CRl567J_sLCvcp_zZi21VG4DOy7dmjM8,14471
366
+ django_cfg/apps/support/templates/support/chat/ticket_chat.html,sha256=Xz5Z0ADP9aZpiE_cJdcRP9UJpJhY32h1h1sLfI94zgU,14483
330
367
  django_cfg/apps/support/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
331
368
  django_cfg/apps/support/utils/support_email_service.py,sha256=0l4dXqAG6oN2UR7W3JzuRhlomaWa41IT7ceVcanluKs,5570
332
369
  django_cfg/apps/support/views/__init__.py,sha256=E-Iqiy888SncfBTiSyXyGCjaNrMa5Klvapzb3ItYPXc,448
@@ -414,13 +451,13 @@ django_cfg/models/jwt.py,sha256=3R_dpLmVZIcH4zdtwA4qKnuCB8RZQACrgsbbgWY2q4c,9025
414
451
  django_cfg/models/limits.py,sha256=nUfvyPykwKR38ZOlIFqlNGRmfV8RO5hiUrhDH6FCHJw,7449
415
452
  django_cfg/models/logging.py,sha256=4vZF-G9rPmXMxxtUx_ad7Esvgbe8a_5Dl692Yg0fL4A,10636
416
453
  django_cfg/models/ngrok.py,sha256=MVgcKWx0DRSW0QcwhiSx2vVwTSG49vbVrzPkZqDK-zw,3575
417
- django_cfg/models/revolution.py,sha256=7Rj-EYvNK4EEttMqtE95ejLWzvucz8rFelWNBNTKk3s,8130
454
+ django_cfg/models/revolution.py,sha256=16fQ09iovrmlepNcTr2J0hZ-ShdMnMSQ2rTXa60yrus,8134
418
455
  django_cfg/models/security.py,sha256=Xv19ZVOIenB_-f0wB6fm-Ap4j9kA43bSFaT2XenpSqc,4685
419
456
  django_cfg/models/services.py,sha256=fj9JjrJFrlL4DMnMbx_D8JiiZpz4E5uBqmhquAxau7c,13159
420
457
  django_cfg/models/tasks.py,sha256=2T3apcUFf8zevYJmapbFrh6bWv5PTJPeXR6Bc5vlj4c,15524
421
458
  django_cfg/modules/__init__.py,sha256=Ip9WMpzImEwIAywpFwU056_v0O9oIGG7nCT1YSArxkw,316
422
- django_cfg/modules/base.py,sha256=_agj4rt9CrEfrDVL29A1vgFYIwZXXg6Wf7aCNFFdXo0,5732
423
- django_cfg/modules/django_email.py,sha256=2XXlIKzD6Jao3CT4_zIE2eaM9Cc9ROA1tjp2bJ9z5Lo,16592
459
+ django_cfg/modules/base.py,sha256=ZFXqGPpZGvsVYp9Cc1aCpvMzGrEE9V2z9g50so2P5Nk,5736
460
+ django_cfg/modules/django_email.py,sha256=Kk2B1Y73xlbpNKCDkU2uBT01iVANjKpmVlKbRScVCs4,18059
424
461
  django_cfg/modules/django_health.py,sha256=7QzuQ6WyjWYj6lecd4auwRvEyrMUL7N6hiAp-tLyoY4,8923
425
462
  django_cfg/modules/django_logger.py,sha256=3oP9jev0lOcFUJ1tYcpbFnK524zIGA2xIOrrAiTwpb8,6331
426
463
  django_cfg/modules/django_ngrok.py,sha256=LjlAIJprbFhFYwDG93acas8XaB6iFQGu4fWvZiDNhyQ,10482
@@ -461,7 +498,7 @@ django_cfg/modules/django_twilio/templates/guide.md,sha256=nZfwx-sgWyK5NApm93zOe
461
498
  django_cfg/modules/django_twilio/templates/sendgrid_otp_email.html,sha256=sXR6_D9hmOFfk9CrfPizpLddVhkRirBWpZd_ioEsxVk,6671
462
499
  django_cfg/modules/django_twilio/templates/sendgrid_test_data.json,sha256=fh1VyuSiDELHsS_CIz9gp7tlsMAEjaDOoqbAPSZ3yyo,339
463
500
  django_cfg/modules/django_unfold/__init__.py,sha256=Z91x1iGmkzlRbEb2L9OCFmYDKNAV9C4G3i15j5S0esc,1898
464
- django_cfg/modules/django_unfold/dashboard.py,sha256=DHQruZRtSLD786v2R3le4mmw_5YG5rI32ZL4qjBoAkQ,13426
501
+ django_cfg/modules/django_unfold/dashboard.py,sha256=UITRlmqAGpptKGFMdTwh9rcIZqKyu9V1TkyKkGNHlKo,14892
465
502
  django_cfg/modules/django_unfold/models.py,sha256=bY6QSSaH_-r9vOTkSQjxeIkl5RaED7XkxXkT8-W5stk,4014
466
503
  django_cfg/modules/django_unfold/system_monitor.py,sha256=cznZqldRJqiSLSJbs4U7R2rX8ClzoIpqdfXdXqI2iQw,6955
467
504
  django_cfg/modules/django_unfold/tailwind.py,sha256=X9o1K3QL0VwUISgJ26sLb6zkdK-00qiDuekqTw-fydc,10846
@@ -544,8 +581,8 @@ django_cfg/utils/path_resolution.py,sha256=C9As6p4Q9l3VeoVkFDRPQWGrzAWf8O8UxLVka
544
581
  django_cfg/utils/smart_defaults.py,sha256=MxbUZwn_xbh48li7uLI6W4D9WCD2P2WO48dv85Fra5E,23057
545
582
  django_cfg/utils/toolkit.py,sha256=Td8_iXNaftonF_xdZP4Y3uO65nuA_4_zditn5Q_Pfcw,23310
546
583
  django_cfg/utils/version_check.py,sha256=jI4v3YMdQriUEeb_TvRl511sDghy6I75iKRDUaNpucs,4800
547
- django_cfg-1.2.22.dist-info/METADATA,sha256=VGD-7Y0LSzV1UdNyMYvkZ2w8WIO14V_uk1T0Xe0WbIg,38434
548
- django_cfg-1.2.22.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
549
- django_cfg-1.2.22.dist-info/entry_points.txt,sha256=Ucmde4Z2wEzgb4AggxxZ0zaYDb9HpyE5blM3uJ0_VNg,56
550
- django_cfg-1.2.22.dist-info/licenses/LICENSE,sha256=xHuytiUkSZCRG3N11nk1X6q1_EGQtv6aL5O9cqNRhKE,1071
551
- django_cfg-1.2.22.dist-info/RECORD,,
584
+ django_cfg-1.2.23.dist-info/METADATA,sha256=2qgEiOin5w8jT2vBv1cxqAPjKWYo0W3D3OEaXTCfqg0,38435
585
+ django_cfg-1.2.23.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
586
+ django_cfg-1.2.23.dist-info/entry_points.txt,sha256=Ucmde4Z2wEzgb4AggxxZ0zaYDb9HpyE5blM3uJ0_VNg,56
587
+ django_cfg-1.2.23.dist-info/licenses/LICENSE,sha256=xHuytiUkSZCRG3N11nk1X6q1_EGQtv6aL5O9cqNRhKE,1071
588
+ django_cfg-1.2.23.dist-info/RECORD,,
@@ -1,68 +0,0 @@
1
- """
2
- Base payment service classes.
3
- """
4
-
5
- from abc import ABC, abstractmethod
6
- from typing import Dict, Any, Optional
7
- from decimal import Decimal
8
-
9
-
10
- class PaymentProvider(ABC):
11
- """Abstract base class for payment providers."""
12
-
13
- def __init__(self, config: Dict[str, Any]):
14
- """Initialize provider with config."""
15
- self.config = config
16
- self.name = self.__class__.__name__.lower().replace('provider', '')
17
-
18
- @abstractmethod
19
- def create_payment(self, amount: Decimal, currency: str, **kwargs) -> Dict[str, Any]:
20
- """Create a payment request."""
21
- pass
22
-
23
- @abstractmethod
24
- def check_payment_status(self, payment_id: str) -> Dict[str, Any]:
25
- """Check payment status."""
26
- pass
27
-
28
- @abstractmethod
29
- def process_webhook(self, payload: Dict[str, Any]) -> Dict[str, Any]:
30
- """Process webhook payload."""
31
- pass
32
-
33
- @abstractmethod
34
- def get_supported_currencies(self) -> list[str]:
35
- """Get list of supported currencies."""
36
- pass
37
-
38
-
39
- class PaymentService:
40
- """Main payment service with provider management."""
41
-
42
- def __init__(self):
43
- """Initialize payment service."""
44
- self.providers: Dict[str, PaymentProvider] = {}
45
-
46
- def register_provider(self, provider: PaymentProvider) -> None:
47
- """Register a payment provider."""
48
- self.providers[provider.name] = provider
49
-
50
- def get_provider(self, name: str) -> Optional[PaymentProvider]:
51
- """Get provider by name."""
52
- return self.providers.get(name)
53
-
54
- def create_payment(self, provider_name: str, amount: Decimal, currency: str, **kwargs) -> Dict[str, Any]:
55
- """Create payment using specified provider."""
56
- provider = self.get_provider(provider_name)
57
- if not provider:
58
- raise ValueError(f"Provider {provider_name} not found")
59
-
60
- return provider.create_payment(amount, currency, **kwargs)
61
-
62
- def process_webhook(self, provider_name: str, payload: Dict[str, Any]) -> Dict[str, Any]:
63
- """Process webhook for specified provider."""
64
- provider = self.get_provider(provider_name)
65
- if not provider:
66
- raise ValueError(f"Provider {provider_name} not found")
67
-
68
- return provider.process_webhook(payload)
@@ -1,78 +0,0 @@
1
- """
2
- NowPayments provider implementation.
3
- """
4
-
5
- from typing import Dict, Any
6
- from decimal import Decimal
7
- import requests
8
- from .base import PaymentProvider
9
-
10
-
11
- class NowPaymentsProvider(PaymentProvider):
12
- """NowPayments crypto payment provider."""
13
-
14
- def __init__(self, config: Dict[str, Any]):
15
- """Initialize NowPayments provider."""
16
- super().__init__(config)
17
- self.api_key = config.get('api_key')
18
- self.base_url = config.get('base_url', 'https://api.nowpayments.io/v1')
19
- self.headers = {'x-api-key': self.api_key}
20
-
21
- def create_payment(self, amount: Decimal, currency: str, **kwargs) -> Dict[str, Any]:
22
- """Create payment via NowPayments API."""
23
- payload = {
24
- 'price_amount': float(amount),
25
- 'price_currency': 'USD',
26
- 'pay_currency': currency.upper(),
27
- 'order_id': kwargs.get('order_id'),
28
- 'order_description': kwargs.get('description', 'Payment'),
29
- 'ipn_callback_url': kwargs.get('callback_url'),
30
- 'success_url': kwargs.get('success_url'),
31
- 'cancel_url': kwargs.get('cancel_url'),
32
- }
33
-
34
- response = requests.post(
35
- f"{self.base_url}/payment",
36
- json=payload,
37
- headers=self.headers,
38
- timeout=30
39
- )
40
- response.raise_for_status()
41
- return response.json()
42
-
43
- def check_payment_status(self, payment_id: str) -> Dict[str, Any]:
44
- """Check payment status via NowPayments API."""
45
- response = requests.get(
46
- f"{self.base_url}/payment/{payment_id}",
47
- headers=self.headers,
48
- timeout=30
49
- )
50
- response.raise_for_status()
51
- return response.json()
52
-
53
- def process_webhook(self, payload: Dict[str, Any]) -> Dict[str, Any]:
54
- """Process NowPayments webhook."""
55
- # Extract important fields from webhook
56
- return {
57
- 'payment_id': payload.get('payment_id'),
58
- 'payment_status': payload.get('payment_status'),
59
- 'pay_address': payload.get('pay_address'),
60
- 'pay_amount': payload.get('pay_amount'),
61
- 'pay_currency': payload.get('pay_currency'),
62
- 'price_amount': payload.get('price_amount'),
63
- 'price_currency': payload.get('price_currency'),
64
- 'order_id': payload.get('order_id'),
65
- 'outcome_amount': payload.get('outcome_amount'),
66
- 'outcome_currency': payload.get('outcome_currency'),
67
- }
68
-
69
- def get_supported_currencies(self) -> list[str]:
70
- """Get supported cryptocurrencies from NowPayments."""
71
- response = requests.get(
72
- f"{self.base_url}/currencies",
73
- headers=self.headers,
74
- timeout=30
75
- )
76
- response.raise_for_status()
77
- data = response.json()
78
- return data.get('currencies', [])
@@ -1,77 +0,0 @@
1
- """
2
- Payment provider integrations using Pydantic for external data validation.
3
- Only for provider responses and internal service communication.
4
- """
5
-
6
- from pydantic import BaseModel, Field, ConfigDict, field_validator
7
- from decimal import Decimal
8
- from datetime import datetime, timezone
9
- from typing import Optional, Dict, Any
10
-
11
-
12
- class NowPaymentsWebhook(BaseModel):
13
- """NowPayments webhook data validation."""
14
- model_config = ConfigDict(validate_assignment=True, extra="forbid")
15
-
16
- payment_id: str
17
- payment_status: str
18
- pay_address: str
19
- pay_amount: Decimal
20
- pay_currency: str
21
- order_id: str
22
- order_description: Optional[str] = None
23
- ipn_callback_url: Optional[str] = None
24
- created_at: Optional[datetime] = None
25
- updated_at: Optional[datetime] = None
26
-
27
- @field_validator('pay_amount')
28
- @classmethod
29
- def validate_amount(cls, v: Decimal) -> Decimal:
30
- if v <= 0:
31
- raise ValueError("Payment amount must be positive")
32
- return v
33
-
34
-
35
- class NowPaymentsCreateResponse(BaseModel):
36
- """NowPayments payment creation response."""
37
- model_config = ConfigDict(validate_assignment=True, extra="forbid")
38
-
39
- payment_id: str
40
- payment_status: str
41
- pay_address: str
42
- pay_amount: Decimal
43
- pay_currency: str
44
- order_id: str
45
- order_description: Optional[str] = None
46
- ipn_callback_url: Optional[str] = None
47
- created_at: datetime
48
- updated_at: datetime
49
-
50
-
51
- class NowPaymentsStatusResponse(BaseModel):
52
- """NowPayments payment status response."""
53
- model_config = ConfigDict(validate_assignment=True, extra="forbid")
54
-
55
- payment_id: str
56
- payment_status: str
57
- pay_address: str
58
- pay_amount: Decimal
59
- actually_paid: Optional[Decimal] = None
60
- pay_currency: str
61
- order_id: str
62
- outcome_amount: Optional[Decimal] = None
63
- outcome_currency: Optional[str] = None
64
-
65
-
66
- class ProviderWebhookData(BaseModel):
67
- """Generic webhook data for any provider."""
68
- model_config = ConfigDict(validate_assignment=True, extra="forbid")
69
-
70
- provider: str
71
- payment_id: str
72
- status: str
73
- amount: Optional[Decimal] = None
74
- currency: Optional[str] = None
75
- raw_data: Dict[str, Any] = Field(default_factory=dict)
76
- signature: Optional[str] = None
77
- received_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
@@ -1,215 +0,0 @@
1
- """
2
- Redis service for universal payments with circuit breaker.
3
- """
4
-
5
- import logging
6
- import redis
7
- import json
8
- import time
9
- from typing import Optional, Any, Dict, List
10
- from django_cfg.modules import BaseCfgModule
11
- from django.core.cache import cache
12
- from django.db import transaction
13
-
14
- logger = logging.getLogger(__name__)
15
-
16
-
17
- class RedisCircuitBreaker:
18
- """Circuit breaker for Redis operations with database fallback."""
19
-
20
- def __init__(self, failure_threshold: int = 5, recovery_timeout: int = 60):
21
- self.failure_threshold = failure_threshold
22
- self.recovery_timeout = recovery_timeout
23
- self.failure_count = 0
24
- self.last_failure_time = None
25
- self.state = "CLOSED" # CLOSED, OPEN, HALF_OPEN
26
-
27
- def call(self, redis_func, fallback_func, *args, **kwargs):
28
- """Execute function with circuit breaker pattern."""
29
- if self.state == "OPEN":
30
- if self._should_attempt_reset():
31
- self.state = "HALF_OPEN"
32
- else:
33
- logger.warning("Circuit breaker OPEN, using fallback")
34
- return fallback_func(*args, **kwargs)
35
-
36
- try:
37
- result = redis_func(*args, **kwargs)
38
- self._on_success()
39
- return result
40
- except Exception as e:
41
- self._on_failure()
42
- logger.warning(f"Redis operation failed, using fallback: {e}")
43
- return fallback_func(*args, **kwargs)
44
-
45
- def _on_success(self):
46
- """Handle successful operation."""
47
- self.failure_count = 0
48
- self.state = "CLOSED"
49
-
50
- def _on_failure(self):
51
- """Handle failed operation."""
52
- self.failure_count += 1
53
- self.last_failure_time = time.time()
54
- if self.failure_count >= self.failure_threshold:
55
- self.state = "OPEN"
56
-
57
- def _should_attempt_reset(self):
58
- """Check if circuit breaker should attempt reset."""
59
- import time
60
- return (time.time() - self.last_failure_time) > self.recovery_timeout
61
-
62
-
63
- class RedisService(BaseCfgModule):
64
- """Redis service with automatic configuration and circuit breaker."""
65
-
66
- def __init__(self):
67
- super().__init__()
68
- self._client = None
69
- self._circuit_breaker = RedisCircuitBreaker()
70
-
71
- @property
72
- def client(self) -> Optional[redis.Redis]:
73
- """Get Redis client with lazy initialization."""
74
- if self._client is None:
75
- self._client = self._create_client()
76
- return self._client
77
-
78
- def _create_client(self) -> Optional[redis.Redis]:
79
- """Create Redis client from configuration."""
80
- try:
81
- config = self.get_config()
82
- if not config:
83
- logger.warning("No config available, Redis disabled")
84
- return None
85
-
86
- # Get Redis config from main config
87
- redis_config = getattr(config, 'cache', None)
88
- if not redis_config:
89
- logger.warning("No Redis config found, using default")
90
- redis_url = "redis://localhost:6379/0"
91
- else:
92
- redis_url = getattr(redis_config, 'redis_url', "redis://localhost:6379/0")
93
-
94
- return redis.Redis.from_url(redis_url, decode_responses=True)
95
-
96
- except Exception as e:
97
- logger.error(f"Failed to create Redis client: {e}")
98
- return None
99
-
100
- def get_cache(self, key: str) -> Any:
101
- """Get value from cache with circuit breaker."""
102
- def redis_get():
103
- if not self.client:
104
- raise redis.ConnectionError("Redis client not available")
105
- data = self.client.get(key)
106
- return json.loads(data) if data else None
107
-
108
- def fallback_get():
109
- # Fallback to Django cache (database)
110
- return cache.get(key)
111
-
112
- return self._circuit_breaker.call(redis_get, fallback_get)
113
-
114
- def set_cache(self, key: str, value: Any, ttl: int = 300) -> bool:
115
- """Set value in cache with circuit breaker."""
116
- def redis_set():
117
- if not self.client:
118
- raise redis.ConnectionError("Redis client not available")
119
- data = json.dumps(value) if value is not None else ""
120
- return self.client.setex(key, ttl, data)
121
-
122
- def fallback_set():
123
- # Fallback to Django cache (database)
124
- cache.set(key, value, ttl)
125
- return True
126
-
127
- return self._circuit_breaker.call(redis_set, fallback_set)
128
-
129
- def delete_cache(self, key: str) -> bool:
130
- """Delete key from cache."""
131
- def redis_delete():
132
- if not self.client:
133
- raise redis.ConnectionError("Redis client not available")
134
- return self.client.delete(key)
135
-
136
- def fallback_delete():
137
- cache.delete(key)
138
- return True
139
-
140
- return self._circuit_breaker.call(redis_delete, fallback_delete)
141
-
142
- def increment(self, key: str, amount: int = 1, ttl: Optional[int] = None) -> int:
143
- """Increment counter with circuit breaker."""
144
- def redis_incr():
145
- if not self.client:
146
- raise redis.ConnectionError("Redis client not available")
147
- result = self.client.incr(key, amount)
148
- if ttl:
149
- self.client.expire(key, ttl)
150
- return result
151
-
152
- def fallback_incr():
153
- # Simple fallback - just return amount (no persistence)
154
- logger.warning(f"Redis unavailable, counter increment for {key} not persisted")
155
- return amount
156
-
157
- return self._circuit_breaker.call(redis_incr, fallback_incr)
158
-
159
- def get_user_access_cache(self, user_id: int, endpoint_group: str) -> Optional[Dict]:
160
- """Get cached user access info."""
161
- key = f"access:{user_id}:{endpoint_group}"
162
- return self.get_cache(key)
163
-
164
- def set_user_access_cache(self, user_id: int, endpoint_group: str, access_info: Dict, ttl: int = 60) -> bool:
165
- """Set cached user access info."""
166
- key = f"access:{user_id}:{endpoint_group}"
167
- return self.set_cache(key, access_info, ttl)
168
-
169
- def track_usage(self, user_id: int, endpoint_group: str, response_time_ms: Optional[int] = None) -> None:
170
- """Track API usage with rate limiting."""
171
- # Increment request counter
172
- usage_key = f"usage:{user_id}:{endpoint_group}"
173
- self.increment(usage_key, ttl=3600) # 1 hour window
174
-
175
- # Track response time if provided
176
- if response_time_ms:
177
- rt_key = f"response_time:{user_id}:{endpoint_group}"
178
- def redis_track_rt():
179
- if not self.client:
180
- raise redis.ConnectionError("Redis client not available")
181
- self.client.lpush(rt_key, response_time_ms)
182
- self.client.ltrim(rt_key, 0, 99) # Keep last 100
183
- self.client.expire(rt_key, 3600)
184
-
185
- def fallback_track_rt():
186
- pass # Skip response time tracking in fallback
187
-
188
- self._circuit_breaker.call(redis_track_rt, fallback_track_rt)
189
-
190
- def check_rate_limit(self, user_id: int, endpoint_group: str, limit: int, window: int = 3600) -> Dict:
191
- """Check rate limit for user."""
192
- rate_key = f"rate:{user_id}:{endpoint_group}:{window}"
193
-
194
- def redis_check():
195
- if not self.client:
196
- raise redis.ConnectionError("Redis client not available")
197
- current = self.client.get(rate_key) or 0
198
- return {
199
- 'allowed': int(current) < limit,
200
- 'current': int(current),
201
- 'limit': limit,
202
- 'window': window
203
- }
204
-
205
- def fallback_check():
206
- # Fallback - always allow (no rate limiting)
207
- logger.warning(f"Redis unavailable, rate limiting disabled for user {user_id}")
208
- return {
209
- 'allowed': True,
210
- 'current': 0,
211
- 'limit': limit,
212
- 'window': window
213
- }
214
-
215
- return self._circuit_breaker.call(redis_check, fallback_check)