paytechuz 0.2.21__tar.gz → 0.2.23__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.
- paytechuz-0.2.23/PKG-INFO +334 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/pyproject.toml +1 -1
- {paytechuz-0.2.21 → paytechuz-0.2.23}/setup.py +1 -1
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/integrations/django/webhooks.py +28 -22
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/paytechuz/__init__.py +1 -1
- paytechuz-0.2.23/src/paytechuz.egg-info/PKG-INFO +334 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/paytechuz.egg-info/SOURCES.txt +0 -1
- paytechuz-0.2.23/src/paytechuz.egg-info/top_level.txt +1 -0
- paytechuz-0.2.21/PKG-INFO +0 -348
- paytechuz-0.2.21/src/paytechuz.egg-info/PKG-INFO +0 -348
- paytechuz-0.2.21/src/paytechuz.egg-info/requires.txt +0 -19
- paytechuz-0.2.21/src/paytechuz.egg-info/top_level.txt +0 -4
- {paytechuz-0.2.21 → paytechuz-0.2.23}/MANIFEST.in +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/README.md +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/examples/paytechuz_fastapi/__init__.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/examples/paytechuz_fastapi/app/__init__.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/examples/paytechuz_fastapi/app/database.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/examples/paytechuz_fastapi/app/models.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/examples/paytechuz_fastapi/app/typing.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/examples/paytechuz_fastapi/app/webhook_handlers.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/examples/paytechuz_fastapi/main.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/examples/paytechuz_fastapi/payments.db +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/examples/paytechuz_fastapi/run.sh +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/setup.cfg +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/__init__.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/core/__init__.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/core/base.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/core/constants.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/core/exceptions.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/core/http.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/core/payme/errors.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/core/utils.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/gateways/__init__.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/gateways/click/__init__.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/gateways/click/client.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/gateways/click/merchant.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/gateways/click/webhook.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/gateways/payme/__init__.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/gateways/payme/cards.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/gateways/payme/client.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/gateways/payme/receipts.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/gateways/payme/webhook.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/integrations/__init__.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/integrations/django/__init__.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/integrations/django/admin.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/integrations/django/apps.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/integrations/django/migrations/0001_initial.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/integrations/django/migrations/__init__.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/integrations/django/models.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/integrations/django/signals.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/integrations/django/views.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/integrations/fastapi/__init__.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/integrations/fastapi/models.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/integrations/fastapi/routes.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/integrations/fastapi/schemas.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/paytechuz/core/__init__.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/paytechuz/core/base.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/paytechuz/core/constants.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/paytechuz/core/exceptions.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/paytechuz/core/http.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/paytechuz/core/payme/errors.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/paytechuz/core/utils.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/paytechuz/gateways/__init__.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/paytechuz/gateways/click/__init__.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/paytechuz/gateways/click/client.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/paytechuz/gateways/click/merchant.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/paytechuz/gateways/click/webhook.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/paytechuz/gateways/payme/__init__.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/paytechuz/gateways/payme/cards.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/paytechuz/gateways/payme/client.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/paytechuz/gateways/payme/receipts.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/paytechuz/gateways/payme/webhook.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/paytechuz/integrations/__init__.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/paytechuz/integrations/django/__init__.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/paytechuz/integrations/django/admin.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/paytechuz/integrations/django/apps.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/paytechuz/integrations/django/migrations/0001_initial.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/paytechuz/integrations/django/migrations/__init__.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/paytechuz/integrations/django/models.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/paytechuz/integrations/django/signals.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/paytechuz/integrations/django/views.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/paytechuz/integrations/django/webhooks.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/paytechuz/integrations/fastapi/__init__.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/paytechuz/integrations/fastapi/models.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/paytechuz/integrations/fastapi/routes.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/paytechuz/integrations/fastapi/schemas.py +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/src/paytechuz.egg-info/dependency_links.txt +0 -0
- {paytechuz-0.2.21 → paytechuz-0.2.23}/tests/test_click_gateway.py +0 -0
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: paytechuz
|
|
3
|
+
Version: 0.2.23
|
|
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
|
+
[](https://badge.fury.io/py/paytechuz)
|
|
19
|
+
[](https://pypi.org/project/paytechuz/)
|
|
20
|
+
[](https://pay-tech.uz)
|
|
21
|
+
[](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.
|
|
@@ -777,30 +777,36 @@ class ClickWebhook(View):
|
|
|
777
777
|
"""
|
|
778
778
|
Check authentication using signature.
|
|
779
779
|
"""
|
|
780
|
-
if
|
|
781
|
-
|
|
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
|
-
|
|
792
|
-
|
|
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
|
-
|
|
803
|
-
|
|
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
|
"""
|
|
@@ -6,7 +6,7 @@ payment systems in Uzbekistan. It supports Django, Flask, and FastAPI.
|
|
|
6
6
|
"""
|
|
7
7
|
from typing import Any
|
|
8
8
|
|
|
9
|
-
__version__ = '0.2.
|
|
9
|
+
__version__ = '0.2.23'
|
|
10
10
|
|
|
11
11
|
# Import framework integrations - these imports are used to check availability
|
|
12
12
|
# of frameworks, not for direct usage
|