paytechuz 0.2.19__tar.gz → 0.2.21__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.21/PKG-INFO +348 -0
  2. {paytechuz-0.2.19 → paytechuz-0.2.21}/pyproject.toml +1 -1
  3. {paytechuz-0.2.19 → paytechuz-0.2.21}/setup.py +1 -1
  4. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/paytechuz/integrations/fastapi/routes.py +32 -27
  5. paytechuz-0.2.21/src/paytechuz.egg-info/PKG-INFO +348 -0
  6. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/paytechuz.egg-info/SOURCES.txt +1 -0
  7. paytechuz-0.2.21/src/paytechuz.egg-info/requires.txt +19 -0
  8. paytechuz-0.2.21/src/paytechuz.egg-info/top_level.txt +4 -0
  9. paytechuz-0.2.19/PKG-INFO +0 -334
  10. paytechuz-0.2.19/src/paytechuz.egg-info/PKG-INFO +0 -334
  11. paytechuz-0.2.19/src/paytechuz.egg-info/top_level.txt +0 -1
  12. {paytechuz-0.2.19 → paytechuz-0.2.21}/MANIFEST.in +0 -0
  13. {paytechuz-0.2.19 → paytechuz-0.2.21}/README.md +0 -0
  14. {paytechuz-0.2.19 → paytechuz-0.2.21}/examples/paytechuz_fastapi/__init__.py +0 -0
  15. {paytechuz-0.2.19 → paytechuz-0.2.21}/examples/paytechuz_fastapi/app/__init__.py +0 -0
  16. {paytechuz-0.2.19 → paytechuz-0.2.21}/examples/paytechuz_fastapi/app/database.py +0 -0
  17. {paytechuz-0.2.19 → paytechuz-0.2.21}/examples/paytechuz_fastapi/app/models.py +0 -0
  18. {paytechuz-0.2.19 → paytechuz-0.2.21}/examples/paytechuz_fastapi/app/typing.py +0 -0
  19. {paytechuz-0.2.19 → paytechuz-0.2.21}/examples/paytechuz_fastapi/app/webhook_handlers.py +0 -0
  20. {paytechuz-0.2.19 → paytechuz-0.2.21}/examples/paytechuz_fastapi/main.py +0 -0
  21. {paytechuz-0.2.19 → paytechuz-0.2.21}/examples/paytechuz_fastapi/payments.db +0 -0
  22. {paytechuz-0.2.19 → paytechuz-0.2.21}/examples/paytechuz_fastapi/run.sh +0 -0
  23. {paytechuz-0.2.19 → paytechuz-0.2.21}/setup.cfg +0 -0
  24. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/__init__.py +0 -0
  25. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/core/__init__.py +0 -0
  26. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/core/base.py +0 -0
  27. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/core/constants.py +0 -0
  28. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/core/exceptions.py +0 -0
  29. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/core/http.py +0 -0
  30. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/core/payme/errors.py +0 -0
  31. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/core/utils.py +0 -0
  32. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/gateways/__init__.py +0 -0
  33. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/gateways/click/__init__.py +0 -0
  34. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/gateways/click/client.py +0 -0
  35. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/gateways/click/merchant.py +0 -0
  36. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/gateways/click/webhook.py +0 -0
  37. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/gateways/payme/__init__.py +0 -0
  38. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/gateways/payme/cards.py +0 -0
  39. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/gateways/payme/client.py +0 -0
  40. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/gateways/payme/receipts.py +0 -0
  41. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/gateways/payme/webhook.py +0 -0
  42. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/integrations/__init__.py +0 -0
  43. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/integrations/django/__init__.py +0 -0
  44. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/integrations/django/admin.py +0 -0
  45. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/integrations/django/apps.py +0 -0
  46. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/integrations/django/migrations/0001_initial.py +0 -0
  47. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/integrations/django/migrations/__init__.py +0 -0
  48. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/integrations/django/models.py +0 -0
  49. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/integrations/django/signals.py +0 -0
  50. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/integrations/django/views.py +0 -0
  51. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/integrations/django/webhooks.py +0 -0
  52. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/integrations/fastapi/__init__.py +0 -0
  53. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/integrations/fastapi/models.py +0 -0
  54. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/integrations/fastapi/routes.py +0 -0
  55. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/integrations/fastapi/schemas.py +0 -0
  56. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/paytechuz/__init__.py +0 -0
  57. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/paytechuz/core/__init__.py +0 -0
  58. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/paytechuz/core/base.py +0 -0
  59. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/paytechuz/core/constants.py +0 -0
  60. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/paytechuz/core/exceptions.py +0 -0
  61. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/paytechuz/core/http.py +0 -0
  62. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/paytechuz/core/payme/errors.py +0 -0
  63. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/paytechuz/core/utils.py +0 -0
  64. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/paytechuz/gateways/__init__.py +0 -0
  65. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/paytechuz/gateways/click/__init__.py +0 -0
  66. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/paytechuz/gateways/click/client.py +0 -0
  67. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/paytechuz/gateways/click/merchant.py +0 -0
  68. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/paytechuz/gateways/click/webhook.py +0 -0
  69. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/paytechuz/gateways/payme/__init__.py +0 -0
  70. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/paytechuz/gateways/payme/cards.py +0 -0
  71. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/paytechuz/gateways/payme/client.py +0 -0
  72. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/paytechuz/gateways/payme/receipts.py +0 -0
  73. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/paytechuz/gateways/payme/webhook.py +0 -0
  74. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/paytechuz/integrations/__init__.py +0 -0
  75. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/paytechuz/integrations/django/__init__.py +0 -0
  76. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/paytechuz/integrations/django/admin.py +0 -0
  77. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/paytechuz/integrations/django/apps.py +0 -0
  78. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/paytechuz/integrations/django/migrations/0001_initial.py +0 -0
  79. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/paytechuz/integrations/django/migrations/__init__.py +0 -0
  80. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/paytechuz/integrations/django/models.py +0 -0
  81. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/paytechuz/integrations/django/signals.py +0 -0
  82. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/paytechuz/integrations/django/views.py +0 -0
  83. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/paytechuz/integrations/django/webhooks.py +0 -0
  84. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/paytechuz/integrations/fastapi/__init__.py +0 -0
  85. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/paytechuz/integrations/fastapi/models.py +0 -0
  86. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/paytechuz/integrations/fastapi/schemas.py +0 -0
  87. {paytechuz-0.2.19 → paytechuz-0.2.21}/src/paytechuz.egg-info/dependency_links.txt +0 -0
  88. {paytechuz-0.2.19 → paytechuz-0.2.21}/tests/test_click_gateway.py +0 -0
@@ -0,0 +1,348 @@
1
+ Metadata-Version: 2.1
2
+ Name: paytechuz
3
+ Version: 0.2.21
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
+ Description: # paytechuz
10
+
11
+ [![PyPI version](https://badge.fury.io/py/paytechuz.svg)](https://badge.fury.io/py/paytechuz)
12
+ [![Python Versions](https://img.shields.io/pypi/pyversions/paytechuz.svg)](https://pypi.org/project/paytechuz/)
13
+ [![Documentation](https://img.shields.io/badge/docs-pay--tech.uz-blue.svg)](https://pay-tech.uz)
14
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
15
+
16
+ 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.
17
+
18
+ 📖 **[Complete Documentation](https://pay-tech.uz)** | 🚀 **[Quick Start Guide](https://pay-tech.uz/quickstart)**
19
+
20
+ ## Features
21
+
22
+ - 🔄 **API**: Consistent interface for multiple payment providers
23
+ - 🛡️ **Secure**: Built-in security features for payment processing
24
+ - 🔌 **Framework Integration**: Native support for Django and FastAPI
25
+ - 🌐 **Webhook Handling**: Easy-to-use webhook handlers for payment notifications
26
+ - 📊 **Transaction Management**: Automatic transaction tracking and management
27
+ - 🧩 **Extensible**: Easy to add new payment providers
28
+ ## Installation
29
+
30
+ ### Basic Installation
31
+
32
+ ```bash
33
+ pip install paytechuz
34
+ ```
35
+
36
+ ### Framework-Specific Installation
37
+
38
+ ```bash
39
+ # For Django
40
+ pip install paytechuz[django]
41
+
42
+ # For FastAPI
43
+ pip install paytechuz[fastapi]
44
+ ```
45
+
46
+ ## Quick Start
47
+
48
+ > 💡 **Need help?** Check out our [complete documentation](https://pay-tech.uz) for detailed guides and examples.
49
+
50
+ ### Generate Payment Links
51
+
52
+ ```python
53
+ from paytechuz.gateways.payme import PaymeGateway
54
+ from paytechuz.gateways.click import ClickGateway
55
+
56
+ # Initialize Payme gateway
57
+ payme = PaymeGateway(
58
+ payme_id="your_payme_id",
59
+ payme_key="your_payme_key",
60
+ is_test_mode=True # Set to False in production environment
61
+ )
62
+
63
+ # Initialize Click gateway
64
+ click = ClickGateway(
65
+ service_id="your_service_id",
66
+ merchant_id="your_merchant_id",
67
+ merchant_user_id="your_merchant_user_id",
68
+ secret_key="your_secret_key",
69
+ is_test_mode=True # Set to False in production environment
70
+ )
71
+
72
+ # Generate payment links
73
+ payme_link = payme.create_payment(
74
+ id="order_123",
75
+ amount=150000, # amount in UZS
76
+ return_url="https://example.com/return"
77
+ )
78
+
79
+ click_link = click.create_payment(
80
+ id="order_123",
81
+ amount=150000, # amount in UZS
82
+ description="Test payment",
83
+ return_url="https://example.com/return"
84
+ )
85
+ ```
86
+
87
+ ### Django Integration
88
+
89
+ 1. Create Order model:
90
+
91
+ ```python
92
+ # models.py
93
+ from django.db import models
94
+ from django.utils import timezone
95
+
96
+ class Order(models.Model):
97
+ STATUS_CHOICES = (
98
+ ('pending', 'Pending'),
99
+ ('paid', 'Paid'),
100
+ ('cancelled', 'Cancelled'),
101
+ ('delivered', 'Delivered'),
102
+ )
103
+
104
+ product_name = models.CharField(max_length=255)
105
+ amount = models.DecimalField(max_digits=12, decimal_places=2)
106
+ status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending')
107
+ created_at = models.DateTimeField(default=timezone.now)
108
+
109
+ def __str__(self):
110
+ return f"{self.id} - {self.product_name} ({self.amount})"
111
+ ```
112
+
113
+ 2. Add to `INSTALLED_APPS` and configure settings:
114
+
115
+ ```python
116
+ # settings.py
117
+ INSTALLED_APPS = [
118
+ # ...
119
+ 'paytechuz.integrations.django',
120
+ ]
121
+
122
+ PAYTECHUZ = {
123
+ 'PAYME': {
124
+ 'PAYME_ID': 'your_payme_id',
125
+ 'PAYME_KEY': 'your_payme_key',
126
+ 'ACCOUNT_MODEL': 'your_app.models.Order', # For example: 'orders.models.Order'
127
+ 'ACCOUNT_FIELD': 'id',
128
+ 'AMOUNT_FIELD': 'amount',
129
+ 'ONE_TIME_PAYMENT': True,
130
+ 'IS_TEST_MODE': True, # Set to False in production
131
+ },
132
+ 'CLICK': {
133
+ 'SERVICE_ID': 'your_service_id',
134
+ 'MERCHANT_ID': 'your_merchant_id',
135
+ 'MERCHANT_USER_ID': 'your_merchant_user_id',
136
+ 'SECRET_KEY': 'your_secret_key',
137
+ 'ACCOUNT_MODEL': 'your_app.models.Order',
138
+ 'COMMISSION_PERCENT': 0.0,
139
+ 'IS_TEST_MODE': True, # Set to False in production
140
+ }
141
+ }
142
+ ```
143
+
144
+ 3. Create webhook handlers:
145
+
146
+ ```python
147
+ # views.py
148
+ from paytechuz.integrations.django.views import BasePaymeWebhookView, BaseClickWebhookView
149
+ from .models import Order
150
+
151
+ class PaymeWebhookView(BasePaymeWebhookView):
152
+ def successfully_payment(self, params, transaction):
153
+ order = Order.objects.get(id=transaction.account_id)
154
+ order.status = 'paid'
155
+ order.save()
156
+
157
+ def cancelled_payment(self, params, transaction):
158
+ order = Order.objects.get(id=transaction.account_id)
159
+ order.status = 'cancelled'
160
+ order.save()
161
+
162
+ class ClickWebhookView(BaseClickWebhookView):
163
+ def successfully_payment(self, params, transaction):
164
+ order = Order.objects.get(id=transaction.account_id)
165
+ order.status = 'paid'
166
+ order.save()
167
+
168
+ def cancelled_payment(self, params, transaction):
169
+ order = Order.objects.get(id=transaction.account_id)
170
+ order.status = 'cancelled'
171
+ order.save()
172
+ ```
173
+
174
+ 4. Add webhook URLs to `urls.py`:
175
+
176
+ ```python
177
+ # urls.py
178
+ from django.urls import path
179
+ from django.views.decorators.csrf import csrf_exempt
180
+ from .views import PaymeWebhookView, ClickWebhookView
181
+
182
+ urlpatterns = [
183
+ # ...
184
+ path('payments/webhook/payme/', csrf_exempt(PaymeWebhookView.as_view()), name='payme_webhook'),
185
+ path('payments/webhook/click/', csrf_exempt(ClickWebhookView.as_view()), name='click_webhook'),
186
+ ]
187
+ ```
188
+
189
+ ### FastAPI Integration
190
+
191
+ 1. Set up database models:
192
+
193
+ ```python
194
+ from datetime import datetime, timezone
195
+
196
+ from sqlalchemy.orm import sessionmaker
197
+ from sqlalchemy.ext.declarative import declarative_base
198
+ from sqlalchemy import create_engine, Column, Integer, String, Float, DateTime
199
+
200
+ from paytechuz.integrations.fastapi import Base as PaymentsBase
201
+ from paytechuz.integrations.fastapi.models import run_migrations
202
+
203
+
204
+ # Create database engine
205
+ SQLALCHEMY_DATABASE_URL = "sqlite:///./payments.db"
206
+ engine = create_engine(SQLALCHEMY_DATABASE_URL)
207
+
208
+ # Create base declarative class
209
+ Base = declarative_base()
210
+
211
+ # Create Order model
212
+ class Order(Base):
213
+ __tablename__ = "orders"
214
+
215
+ id = Column(Integer, primary_key=True, index=True)
216
+ product_name = Column(String, index=True)
217
+ amount = Column(Float)
218
+ status = Column(String, default="pending")
219
+ created_at = Column(DateTime, default=lambda: datetime.now(timezone.utc))
220
+
221
+ # Create payment tables using run_migrations
222
+ run_migrations(engine)
223
+
224
+ # Create Order table
225
+ Base.metadata.create_all(bind=engine)
226
+
227
+ # Create session
228
+ SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
229
+ ```
230
+
231
+ 2. Create webhook handlers:
232
+
233
+ ```python
234
+ from fastapi import FastAPI, Request, Depends
235
+
236
+ from sqlalchemy.orm import Session
237
+
238
+ from paytechuz.integrations.fastapi import PaymeWebhookHandler, ClickWebhookHandler
239
+
240
+
241
+ app = FastAPI()
242
+
243
+ # Dependency to get the database session
244
+ def get_db():
245
+ db = SessionLocal()
246
+ try:
247
+ yield db
248
+ finally:
249
+ db.close()
250
+
251
+ class CustomPaymeWebhookHandler(PaymeWebhookHandler):
252
+ def successfully_payment(self, params, transaction):
253
+ # Handle successful payment
254
+ order = self.db.query(Order).filter(Order.id == transaction.account_id).first()
255
+ order.status = "paid"
256
+ self.db.commit()
257
+
258
+ def cancelled_payment(self, params, transaction):
259
+ # Handle cancelled payment
260
+ order = self.db.query(Order).filter(Order.id == transaction.account_id).first()
261
+ order.status = "cancelled"
262
+ self.db.commit()
263
+
264
+ class CustomClickWebhookHandler(ClickWebhookHandler):
265
+ def successfully_payment(self, params, transaction):
266
+ # Handle successful payment
267
+ order = self.db.query(Order).filter(Order.id == transaction.account_id).first()
268
+ order.status = "paid"
269
+ self.db.commit()
270
+
271
+ def cancelled_payment(self, params, transaction):
272
+ # Handle cancelled payment
273
+ order = self.db.query(Order).filter(Order.id == transaction.account_id).first()
274
+ order.status = "cancelled"
275
+ self.db.commit()
276
+
277
+ @app.post("/payments/payme/webhook")
278
+ async def payme_webhook(request: Request, db: Session = Depends(get_db)):
279
+ handler = CustomPaymeWebhookHandler(
280
+ db=db,
281
+ payme_id="your_merchant_id",
282
+ payme_key="your_merchant_key",
283
+ account_model=Order,
284
+ account_field='id',
285
+ amount_field='amount'
286
+ )
287
+ return await handler.handle_webhook(request)
288
+
289
+ @app.post("/payments/click/webhook")
290
+ async def click_webhook(request: Request, db: Session = Depends(get_db)):
291
+ handler = CustomClickWebhookHandler(
292
+ db=db,
293
+ service_id="your_service_id",
294
+ secret_key="your_secret_key",
295
+ account_model=Order
296
+ )
297
+ return await handler.handle_webhook(request)
298
+ ```
299
+
300
+ ## Documentation
301
+
302
+ Detailed documentation is available in multiple languages:
303
+
304
+ - 📖 [English Documentation](src/docs/en/index.md)
305
+ - 📖 [O'zbek tilidagi hujjatlar](src/docs/index.md)
306
+
307
+ ### Framework-Specific Documentation
308
+
309
+ - [Django Integration Guide](src/docs/en/django_integration.md) | [Django integratsiyasi bo'yicha qo'llanma](src/docs/django_integration.md)
310
+ - [FastAPI Integration Guide](src/docs/en/fastapi_integration.md) | [FastAPI integratsiyasi bo'yicha qo'llanma](src/docs/fastapi_integration.md)
311
+
312
+ ## Supported Payment Systems
313
+
314
+ - **Payme** - [Official Website](https://payme.uz)
315
+ - **Click** - [Official Website](https://click.uz)
316
+
317
+ ## Contributing
318
+
319
+ Contributions are welcome! Please feel free to submit a Pull Request.
320
+
321
+ 📖 **Documentation:** [pay-tech.uz](https://pay-tech.uz)
322
+ 🐛 **Issues:** [GitHub Issues](https://github.com/PayTechUz/paytechuz-py/issues)
323
+ 💬 **Support:** [Telegram](https://t.me/paytechuz)
324
+
325
+ ## License
326
+
327
+ This project is licensed under the MIT License - see the LICENSE file for details.
328
+
329
+ Keywords: paytechuz,payme,click,uzbekistan,payment,gateway,payment-gateway,payment-processing,django,flask,fastapi
330
+ Platform: UNKNOWN
331
+ Classifier: Development Status :: 4 - Beta
332
+ Classifier: Intended Audience :: Developers
333
+ Classifier: Programming Language :: Python :: 3
334
+ Classifier: Programming Language :: Python :: 3.6
335
+ Classifier: Programming Language :: Python :: 3.7
336
+ Classifier: Programming Language :: Python :: 3.8
337
+ Classifier: Programming Language :: Python :: 3.9
338
+ Classifier: Programming Language :: Python :: 3.10
339
+ Classifier: Programming Language :: Python :: 3.11
340
+ Classifier: Programming Language :: Python :: 3.12
341
+ Classifier: License :: OSI Approved :: MIT License
342
+ Classifier: Operating System :: OS Independent
343
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
344
+ Requires-Python: >=3.6
345
+ Description-Content-Type: text/markdown
346
+ Provides-Extra: django
347
+ Provides-Extra: fastapi
348
+ Provides-Extra: flask
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "paytechuz"
7
- version = "0.2.19"
7
+ version = "0.2.21"
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.19',
11
+ version='0.2.21',
12
12
  license='MIT',
13
13
  author="Muhammadali Akbarov",
14
14
  author_email='muhammadali17abc@gmail.com',
@@ -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
  """