paytechuz 0.2.20__tar.gz → 0.2.22__tar.gz

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.

Potentially problematic release.


This version of paytechuz might be problematic. Click here for more details.

Files changed (88) hide show
  1. paytechuz-0.2.22/PKG-INFO +334 -0
  2. {paytechuz-0.2.20 → paytechuz-0.2.22}/pyproject.toml +1 -1
  3. {paytechuz-0.2.20 → paytechuz-0.2.22}/setup.py +1 -1
  4. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/integrations/django/webhooks.py +28 -22
  5. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/paytechuz/integrations/fastapi/routes.py +32 -27
  6. paytechuz-0.2.22/src/paytechuz.egg-info/PKG-INFO +334 -0
  7. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/paytechuz.egg-info/SOURCES.txt +0 -1
  8. paytechuz-0.2.22/src/paytechuz.egg-info/top_level.txt +1 -0
  9. paytechuz-0.2.20/PKG-INFO +0 -348
  10. paytechuz-0.2.20/src/paytechuz.egg-info/PKG-INFO +0 -348
  11. paytechuz-0.2.20/src/paytechuz.egg-info/requires.txt +0 -19
  12. paytechuz-0.2.20/src/paytechuz.egg-info/top_level.txt +0 -4
  13. {paytechuz-0.2.20 → paytechuz-0.2.22}/MANIFEST.in +0 -0
  14. {paytechuz-0.2.20 → paytechuz-0.2.22}/README.md +0 -0
  15. {paytechuz-0.2.20 → paytechuz-0.2.22}/examples/paytechuz_fastapi/__init__.py +0 -0
  16. {paytechuz-0.2.20 → paytechuz-0.2.22}/examples/paytechuz_fastapi/app/__init__.py +0 -0
  17. {paytechuz-0.2.20 → paytechuz-0.2.22}/examples/paytechuz_fastapi/app/database.py +0 -0
  18. {paytechuz-0.2.20 → paytechuz-0.2.22}/examples/paytechuz_fastapi/app/models.py +0 -0
  19. {paytechuz-0.2.20 → paytechuz-0.2.22}/examples/paytechuz_fastapi/app/typing.py +0 -0
  20. {paytechuz-0.2.20 → paytechuz-0.2.22}/examples/paytechuz_fastapi/app/webhook_handlers.py +0 -0
  21. {paytechuz-0.2.20 → paytechuz-0.2.22}/examples/paytechuz_fastapi/main.py +0 -0
  22. {paytechuz-0.2.20 → paytechuz-0.2.22}/examples/paytechuz_fastapi/payments.db +0 -0
  23. {paytechuz-0.2.20 → paytechuz-0.2.22}/examples/paytechuz_fastapi/run.sh +0 -0
  24. {paytechuz-0.2.20 → paytechuz-0.2.22}/setup.cfg +0 -0
  25. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/__init__.py +0 -0
  26. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/core/__init__.py +0 -0
  27. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/core/base.py +0 -0
  28. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/core/constants.py +0 -0
  29. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/core/exceptions.py +0 -0
  30. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/core/http.py +0 -0
  31. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/core/payme/errors.py +0 -0
  32. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/core/utils.py +0 -0
  33. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/gateways/__init__.py +0 -0
  34. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/gateways/click/__init__.py +0 -0
  35. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/gateways/click/client.py +0 -0
  36. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/gateways/click/merchant.py +0 -0
  37. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/gateways/click/webhook.py +0 -0
  38. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/gateways/payme/__init__.py +0 -0
  39. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/gateways/payme/cards.py +0 -0
  40. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/gateways/payme/client.py +0 -0
  41. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/gateways/payme/receipts.py +0 -0
  42. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/gateways/payme/webhook.py +0 -0
  43. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/integrations/__init__.py +0 -0
  44. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/integrations/django/__init__.py +0 -0
  45. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/integrations/django/admin.py +0 -0
  46. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/integrations/django/apps.py +0 -0
  47. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/integrations/django/migrations/0001_initial.py +0 -0
  48. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/integrations/django/migrations/__init__.py +0 -0
  49. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/integrations/django/models.py +0 -0
  50. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/integrations/django/signals.py +0 -0
  51. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/integrations/django/views.py +0 -0
  52. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/integrations/fastapi/__init__.py +0 -0
  53. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/integrations/fastapi/models.py +0 -0
  54. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/integrations/fastapi/routes.py +0 -0
  55. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/integrations/fastapi/schemas.py +0 -0
  56. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/paytechuz/__init__.py +0 -0
  57. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/paytechuz/core/__init__.py +0 -0
  58. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/paytechuz/core/base.py +0 -0
  59. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/paytechuz/core/constants.py +0 -0
  60. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/paytechuz/core/exceptions.py +0 -0
  61. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/paytechuz/core/http.py +0 -0
  62. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/paytechuz/core/payme/errors.py +0 -0
  63. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/paytechuz/core/utils.py +0 -0
  64. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/paytechuz/gateways/__init__.py +0 -0
  65. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/paytechuz/gateways/click/__init__.py +0 -0
  66. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/paytechuz/gateways/click/client.py +0 -0
  67. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/paytechuz/gateways/click/merchant.py +0 -0
  68. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/paytechuz/gateways/click/webhook.py +0 -0
  69. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/paytechuz/gateways/payme/__init__.py +0 -0
  70. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/paytechuz/gateways/payme/cards.py +0 -0
  71. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/paytechuz/gateways/payme/client.py +0 -0
  72. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/paytechuz/gateways/payme/receipts.py +0 -0
  73. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/paytechuz/gateways/payme/webhook.py +0 -0
  74. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/paytechuz/integrations/__init__.py +0 -0
  75. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/paytechuz/integrations/django/__init__.py +0 -0
  76. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/paytechuz/integrations/django/admin.py +0 -0
  77. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/paytechuz/integrations/django/apps.py +0 -0
  78. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/paytechuz/integrations/django/migrations/0001_initial.py +0 -0
  79. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/paytechuz/integrations/django/migrations/__init__.py +0 -0
  80. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/paytechuz/integrations/django/models.py +0 -0
  81. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/paytechuz/integrations/django/signals.py +0 -0
  82. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/paytechuz/integrations/django/views.py +0 -0
  83. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/paytechuz/integrations/django/webhooks.py +0 -0
  84. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/paytechuz/integrations/fastapi/__init__.py +0 -0
  85. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/paytechuz/integrations/fastapi/models.py +0 -0
  86. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/paytechuz/integrations/fastapi/schemas.py +0 -0
  87. {paytechuz-0.2.20 → paytechuz-0.2.22}/src/paytechuz.egg-info/dependency_links.txt +0 -0
  88. {paytechuz-0.2.20 → paytechuz-0.2.22}/tests/test_click_gateway.py +0 -0
@@ -0,0 +1,334 @@
1
+ Metadata-Version: 2.4
2
+ Name: paytechuz
3
+ Version: 0.2.22
4
+ Summary: Unified Python package for Uzbekistan payment gateways
5
+ Home-page: https://github.com/Muhammadali-Akbarov/paytechuz
6
+ Author: Muhammadali Akbarov
7
+ Author-email: muhammadali17abc@gmail.com
8
+ License: MIT
9
+ Requires-Python: >=3.6
10
+ Description-Content-Type: text/markdown
11
+ Dynamic: author
12
+ Dynamic: author-email
13
+ Dynamic: home-page
14
+ Dynamic: requires-python
15
+
16
+ # paytechuz
17
+
18
+ [![PyPI version](https://badge.fury.io/py/paytechuz.svg)](https://badge.fury.io/py/paytechuz)
19
+ [![Python Versions](https://img.shields.io/pypi/pyversions/paytechuz.svg)](https://pypi.org/project/paytechuz/)
20
+ [![Documentation](https://img.shields.io/badge/docs-pay--tech.uz-blue.svg)](https://pay-tech.uz)
21
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
22
+
23
+ PayTechUZ is a unified payment library for integrating with popular payment systems in Uzbekistan. It provides a simple and consistent interface for working with Payme and Click payment gateways.
24
+
25
+ 📖 **[Complete Documentation](https://pay-tech.uz)** | 🚀 **[Quick Start Guide](https://pay-tech.uz/quickstart)**
26
+
27
+ ## Features
28
+
29
+ - 🔄 **API**: Consistent interface for multiple payment providers
30
+ - 🛡️ **Secure**: Built-in security features for payment processing
31
+ - 🔌 **Framework Integration**: Native support for Django and FastAPI
32
+ - 🌐 **Webhook Handling**: Easy-to-use webhook handlers for payment notifications
33
+ - 📊 **Transaction Management**: Automatic transaction tracking and management
34
+ - 🧩 **Extensible**: Easy to add new payment providers
35
+ ## Installation
36
+
37
+ ### Basic Installation
38
+
39
+ ```bash
40
+ pip install paytechuz
41
+ ```
42
+
43
+ ### Framework-Specific Installation
44
+
45
+ ```bash
46
+ # For Django
47
+ pip install paytechuz[django]
48
+
49
+ # For FastAPI
50
+ pip install paytechuz[fastapi]
51
+ ```
52
+
53
+ ## Quick Start
54
+
55
+ > 💡 **Need help?** Check out our [complete documentation](https://pay-tech.uz) for detailed guides and examples.
56
+
57
+ ### Generate Payment Links
58
+
59
+ ```python
60
+ from paytechuz.gateways.payme import PaymeGateway
61
+ from paytechuz.gateways.click import ClickGateway
62
+
63
+ # Initialize Payme gateway
64
+ payme = PaymeGateway(
65
+ payme_id="your_payme_id",
66
+ payme_key="your_payme_key",
67
+ is_test_mode=True # Set to False in production environment
68
+ )
69
+
70
+ # Initialize Click gateway
71
+ click = ClickGateway(
72
+ service_id="your_service_id",
73
+ merchant_id="your_merchant_id",
74
+ merchant_user_id="your_merchant_user_id",
75
+ secret_key="your_secret_key",
76
+ is_test_mode=True # Set to False in production environment
77
+ )
78
+
79
+ # Generate payment links
80
+ payme_link = payme.create_payment(
81
+ id="order_123",
82
+ amount=150000, # amount in UZS
83
+ return_url="https://example.com/return"
84
+ )
85
+
86
+ click_link = click.create_payment(
87
+ id="order_123",
88
+ amount=150000, # amount in UZS
89
+ description="Test payment",
90
+ return_url="https://example.com/return"
91
+ )
92
+ ```
93
+
94
+ ### Django Integration
95
+
96
+ 1. Create Order model:
97
+
98
+ ```python
99
+ # models.py
100
+ from django.db import models
101
+ from django.utils import timezone
102
+
103
+ class Order(models.Model):
104
+ STATUS_CHOICES = (
105
+ ('pending', 'Pending'),
106
+ ('paid', 'Paid'),
107
+ ('cancelled', 'Cancelled'),
108
+ ('delivered', 'Delivered'),
109
+ )
110
+
111
+ product_name = models.CharField(max_length=255)
112
+ amount = models.DecimalField(max_digits=12, decimal_places=2)
113
+ status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending')
114
+ created_at = models.DateTimeField(default=timezone.now)
115
+
116
+ def __str__(self):
117
+ return f"{self.id} - {self.product_name} ({self.amount})"
118
+ ```
119
+
120
+ 2. Add to `INSTALLED_APPS` and configure settings:
121
+
122
+ ```python
123
+ # settings.py
124
+ INSTALLED_APPS = [
125
+ # ...
126
+ 'paytechuz.integrations.django',
127
+ ]
128
+
129
+ PAYTECHUZ = {
130
+ 'PAYME': {
131
+ 'PAYME_ID': 'your_payme_id',
132
+ 'PAYME_KEY': 'your_payme_key',
133
+ 'ACCOUNT_MODEL': 'your_app.models.Order', # For example: 'orders.models.Order'
134
+ 'ACCOUNT_FIELD': 'id',
135
+ 'AMOUNT_FIELD': 'amount',
136
+ 'ONE_TIME_PAYMENT': True,
137
+ 'IS_TEST_MODE': True, # Set to False in production
138
+ },
139
+ 'CLICK': {
140
+ 'SERVICE_ID': 'your_service_id',
141
+ 'MERCHANT_ID': 'your_merchant_id',
142
+ 'MERCHANT_USER_ID': 'your_merchant_user_id',
143
+ 'SECRET_KEY': 'your_secret_key',
144
+ 'ACCOUNT_MODEL': 'your_app.models.Order',
145
+ 'COMMISSION_PERCENT': 0.0,
146
+ 'IS_TEST_MODE': True, # Set to False in production
147
+ }
148
+ }
149
+ ```
150
+
151
+ 3. Create webhook handlers:
152
+
153
+ ```python
154
+ # views.py
155
+ from paytechuz.integrations.django.views import BasePaymeWebhookView, BaseClickWebhookView
156
+ from .models import Order
157
+
158
+ class PaymeWebhookView(BasePaymeWebhookView):
159
+ def successfully_payment(self, params, transaction):
160
+ order = Order.objects.get(id=transaction.account_id)
161
+ order.status = 'paid'
162
+ order.save()
163
+
164
+ def cancelled_payment(self, params, transaction):
165
+ order = Order.objects.get(id=transaction.account_id)
166
+ order.status = 'cancelled'
167
+ order.save()
168
+
169
+ class ClickWebhookView(BaseClickWebhookView):
170
+ def successfully_payment(self, params, transaction):
171
+ order = Order.objects.get(id=transaction.account_id)
172
+ order.status = 'paid'
173
+ order.save()
174
+
175
+ def cancelled_payment(self, params, transaction):
176
+ order = Order.objects.get(id=transaction.account_id)
177
+ order.status = 'cancelled'
178
+ order.save()
179
+ ```
180
+
181
+ 4. Add webhook URLs to `urls.py`:
182
+
183
+ ```python
184
+ # urls.py
185
+ from django.urls import path
186
+ from django.views.decorators.csrf import csrf_exempt
187
+ from .views import PaymeWebhookView, ClickWebhookView
188
+
189
+ urlpatterns = [
190
+ # ...
191
+ path('payments/webhook/payme/', csrf_exempt(PaymeWebhookView.as_view()), name='payme_webhook'),
192
+ path('payments/webhook/click/', csrf_exempt(ClickWebhookView.as_view()), name='click_webhook'),
193
+ ]
194
+ ```
195
+
196
+ ### FastAPI Integration
197
+
198
+ 1. Set up database models:
199
+
200
+ ```python
201
+ from datetime import datetime, timezone
202
+
203
+ from sqlalchemy.orm import sessionmaker
204
+ from sqlalchemy.ext.declarative import declarative_base
205
+ from sqlalchemy import create_engine, Column, Integer, String, Float, DateTime
206
+
207
+ from paytechuz.integrations.fastapi import Base as PaymentsBase
208
+ from paytechuz.integrations.fastapi.models import run_migrations
209
+
210
+
211
+ # Create database engine
212
+ SQLALCHEMY_DATABASE_URL = "sqlite:///./payments.db"
213
+ engine = create_engine(SQLALCHEMY_DATABASE_URL)
214
+
215
+ # Create base declarative class
216
+ Base = declarative_base()
217
+
218
+ # Create Order model
219
+ class Order(Base):
220
+ __tablename__ = "orders"
221
+
222
+ id = Column(Integer, primary_key=True, index=True)
223
+ product_name = Column(String, index=True)
224
+ amount = Column(Float)
225
+ status = Column(String, default="pending")
226
+ created_at = Column(DateTime, default=lambda: datetime.now(timezone.utc))
227
+
228
+ # Create payment tables using run_migrations
229
+ run_migrations(engine)
230
+
231
+ # Create Order table
232
+ Base.metadata.create_all(bind=engine)
233
+
234
+ # Create session
235
+ SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
236
+ ```
237
+
238
+ 2. Create webhook handlers:
239
+
240
+ ```python
241
+ from fastapi import FastAPI, Request, Depends
242
+
243
+ from sqlalchemy.orm import Session
244
+
245
+ from paytechuz.integrations.fastapi import PaymeWebhookHandler, ClickWebhookHandler
246
+
247
+
248
+ app = FastAPI()
249
+
250
+ # Dependency to get the database session
251
+ def get_db():
252
+ db = SessionLocal()
253
+ try:
254
+ yield db
255
+ finally:
256
+ db.close()
257
+
258
+ class CustomPaymeWebhookHandler(PaymeWebhookHandler):
259
+ def successfully_payment(self, params, transaction):
260
+ # Handle successful payment
261
+ order = self.db.query(Order).filter(Order.id == transaction.account_id).first()
262
+ order.status = "paid"
263
+ self.db.commit()
264
+
265
+ def cancelled_payment(self, params, transaction):
266
+ # Handle cancelled payment
267
+ order = self.db.query(Order).filter(Order.id == transaction.account_id).first()
268
+ order.status = "cancelled"
269
+ self.db.commit()
270
+
271
+ class CustomClickWebhookHandler(ClickWebhookHandler):
272
+ def successfully_payment(self, params, transaction):
273
+ # Handle successful payment
274
+ order = self.db.query(Order).filter(Order.id == transaction.account_id).first()
275
+ order.status = "paid"
276
+ self.db.commit()
277
+
278
+ def cancelled_payment(self, params, transaction):
279
+ # Handle cancelled payment
280
+ order = self.db.query(Order).filter(Order.id == transaction.account_id).first()
281
+ order.status = "cancelled"
282
+ self.db.commit()
283
+
284
+ @app.post("/payments/payme/webhook")
285
+ async def payme_webhook(request: Request, db: Session = Depends(get_db)):
286
+ handler = CustomPaymeWebhookHandler(
287
+ db=db,
288
+ payme_id="your_merchant_id",
289
+ payme_key="your_merchant_key",
290
+ account_model=Order,
291
+ account_field='id',
292
+ amount_field='amount'
293
+ )
294
+ return await handler.handle_webhook(request)
295
+
296
+ @app.post("/payments/click/webhook")
297
+ async def click_webhook(request: Request, db: Session = Depends(get_db)):
298
+ handler = CustomClickWebhookHandler(
299
+ db=db,
300
+ service_id="your_service_id",
301
+ secret_key="your_secret_key",
302
+ account_model=Order
303
+ )
304
+ return await handler.handle_webhook(request)
305
+ ```
306
+
307
+ ## Documentation
308
+
309
+ Detailed documentation is available in multiple languages:
310
+
311
+ - 📖 [English Documentation](src/docs/en/index.md)
312
+ - 📖 [O'zbek tilidagi hujjatlar](src/docs/index.md)
313
+
314
+ ### Framework-Specific Documentation
315
+
316
+ - [Django Integration Guide](src/docs/en/django_integration.md) | [Django integratsiyasi bo'yicha qo'llanma](src/docs/django_integration.md)
317
+ - [FastAPI Integration Guide](src/docs/en/fastapi_integration.md) | [FastAPI integratsiyasi bo'yicha qo'llanma](src/docs/fastapi_integration.md)
318
+
319
+ ## Supported Payment Systems
320
+
321
+ - **Payme** - [Official Website](https://payme.uz)
322
+ - **Click** - [Official Website](https://click.uz)
323
+
324
+ ## Contributing
325
+
326
+ Contributions are welcome! Please feel free to submit a Pull Request.
327
+
328
+ 📖 **Documentation:** [pay-tech.uz](https://pay-tech.uz)
329
+ 🐛 **Issues:** [GitHub Issues](https://github.com/PayTechUz/paytechuz-py/issues)
330
+ 💬 **Support:** [Telegram](https://t.me/paytechuz)
331
+
332
+ ## License
333
+
334
+ This project is licensed under the MIT License - see the LICENSE file for details.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "paytechuz"
7
- version = "0.2.20"
7
+ version = "0.2.22"
8
8
  description = "Unified Python package for Uzbekistan payment gateways"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.7"
@@ -8,7 +8,7 @@ long_description = (here / "README.md").read_text(encoding="utf-8")
8
8
 
9
9
  setup(
10
10
  name='paytechuz',
11
- version='0.2.20',
11
+ version='0.2.22',
12
12
  license='MIT',
13
13
  author="Muhammadali Akbarov",
14
14
  author_email='muhammadali17abc@gmail.com',
@@ -777,30 +777,36 @@ class ClickWebhook(View):
777
777
  """
778
778
  Check authentication using signature.
779
779
  """
780
- if str(params.get('service_id')) != self.service_id:
781
- raise PermissionDenied("Invalid service ID")
782
-
783
- # Check signature if secret key is provided
784
- if self.secret_key:
785
- sign_string = params.get('sign_string')
786
- sign_time = params.get('sign_time')
787
-
788
- if not sign_string or not sign_time:
789
- raise PermissionDenied("Missing signature parameters")
780
+ # Check if service_id and secret_key are set
781
+ if not self.service_id or not self.secret_key:
782
+ raise PermissionDenied("Missing required settings: service_id or secret_key")
790
783
 
791
- # Create string to sign
792
- to_sign = (
793
- f"{params.get('click_trans_id')}{params.get('service_id')}"
794
- )
795
- to_sign += f"{self.secret_key}{params.get('merchant_trans_id')}"
796
- to_sign += f"{params.get('amount')}{params.get('action')}"
797
- to_sign += f"{sign_time}"
798
-
799
- # Generate signature
800
- signature = hashlib.md5(to_sign.encode('utf-8')).hexdigest()
784
+ if str(params.get("service_id")) != self.service_id:
785
+ raise PermissionDenied("Invalid service ID")
801
786
 
802
- if signature != sign_string:
803
- raise PermissionDenied("Invalid signature")
787
+ sign_string = params.get("sign_string")
788
+ sign_time = params.get("sign_time")
789
+
790
+ if not sign_string or not sign_time:
791
+ raise PermissionDenied("Missing signature parameters")
792
+
793
+ # Prepare signature components; note merchant_prepare_id added compared to your original
794
+ text_parts = [
795
+ str(params.get("click_trans_id") or ""),
796
+ str(params.get("service_id") or ""),
797
+ str(self.secret_key or ""),
798
+ str(params.get("merchant_trans_id") or ""),
799
+ str(params.get("merchant_prepare_id") or ""), # added here
800
+ str(params.get("amount") or ""),
801
+ str(params.get("action") or ""),
802
+ str(sign_time)
803
+ ]
804
+
805
+ # Calculate hash
806
+ calculated_hash = hashlib.md5("".join(text_parts).encode("utf-8")).hexdigest()
807
+
808
+ if calculated_hash != sign_string:
809
+ raise PermissionDenied("Invalid signature")
804
810
 
805
811
  def _find_account(self, merchant_trans_id):
806
812
  """
@@ -989,42 +989,47 @@ class ClickWebhookHandler:
989
989
  """
990
990
  Check authentication using signature.
991
991
  """
992
- if str(params.get('service_id')) != self.service_id:
992
+ if not all([self.service_id, self.secret_key]):
993
993
  raise HTTPException(
994
994
  status_code=status.HTTP_401_UNAUTHORIZED,
995
- detail="Invalid service ID"
995
+ detail="Missing required settings: service_id or secret_key"
996
996
  )
997
997
 
998
- # Check signature if secret key is provided
999
- if self.secret_key:
1000
- sign_string = params.get('sign_string')
1001
- sign_time = params.get('sign_time')
998
+ if str(params.get("service_id")) != self.service_id:
999
+ raise HTTPException(
1000
+ status_code=status.HTTP_401_UNAUTHORIZED,
1001
+ detail="Invalid service ID"
1002
+ )
1002
1003
 
1003
- if not sign_string or not sign_time:
1004
- raise HTTPException(
1005
- status_code=status.HTTP_401_UNAUTHORIZED,
1006
- detail="Missing signature parameters"
1007
- )
1004
+ sign_string = params.get("sign_string")
1005
+ sign_time = params.get("sign_time")
1008
1006
 
1009
- # Create string to sign
1010
- to_sign = (
1011
- f"{params.get('click_trans_id')}"
1012
- f"{params.get('service_id')}"
1013
- f"{self.secret_key}"
1014
- f"{params.get('merchant_trans_id')}"
1015
- f"{params.get('amount')}"
1016
- f"{params.get('action')}"
1017
- f"{sign_time}"
1007
+ if not sign_string or not sign_time:
1008
+ raise HTTPException(
1009
+ status_code=status.HTTP_401_UNAUTHORIZED,
1010
+ detail="Missing signature parameters"
1018
1011
  )
1019
1012
 
1020
- # Generate signature
1021
- signature = hashlib.md5(to_sign.encode('utf-8')).hexdigest()
1013
+ # Prepare signature components
1014
+ text_parts = [
1015
+ str(params.get("click_trans_id") or ""),
1016
+ str(params.get("service_id") or ""),
1017
+ str(self.secret_key or ""),
1018
+ str(params.get("merchant_trans_id") or ""),
1019
+ str(params.get("merchant_prepare_id") or ""),
1020
+ str(params.get("amount") or ""),
1021
+ str(params.get("action") or ""),
1022
+ str(sign_time)
1023
+ ]
1024
+
1025
+ # Calculate hash
1026
+ calculated_hash = hashlib.md5("".join(text_parts).encode("utf-8")).hexdigest()
1022
1027
 
1023
- if signature != sign_string:
1024
- raise HTTPException(
1025
- status_code=status.HTTP_401_UNAUTHORIZED,
1026
- detail="Invalid signature"
1027
- )
1028
+ if calculated_hash != sign_string:
1029
+ raise HTTPException(
1030
+ status_code=status.HTTP_401_UNAUTHORIZED,
1031
+ detail="Invalid signature"
1032
+ )
1028
1033
 
1029
1034
  def _find_account(self, merchant_trans_id: str) -> Any:
1030
1035
  """