paytechuz 0.1.2__py3-none-any.whl → 0.1.4__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.
Potentially problematic release.
This version of paytechuz might be problematic. Click here for more details.
- core/base.py +12 -3
- gateways/payme/client.py +124 -100
- paytechuz-0.1.4.dist-info/METADATA +146 -0
- {paytechuz-0.1.2.dist-info → paytechuz-0.1.4.dist-info}/RECORD +6 -7
- {paytechuz-0.1.2.dist-info → paytechuz-0.1.4.dist-info}/top_level.txt +0 -1
- paytechuz/__init__.py +0 -74
- paytechuz-0.1.2.dist-info/METADATA +0 -198
- {paytechuz-0.1.2.dist-info → paytechuz-0.1.4.dist-info}/WHEEL +0 -0
core/base.py
CHANGED
|
@@ -4,6 +4,7 @@ Base classes for payment gateways.
|
|
|
4
4
|
from abc import ABC, abstractmethod
|
|
5
5
|
from typing import Dict, Any, Optional, Union
|
|
6
6
|
|
|
7
|
+
|
|
7
8
|
class BasePaymentGateway(ABC):
|
|
8
9
|
"""
|
|
9
10
|
Base class for all payment gateways.
|
|
@@ -23,7 +24,12 @@ class BasePaymentGateway(ABC):
|
|
|
23
24
|
self.is_test_mode = is_test_mode
|
|
24
25
|
|
|
25
26
|
@abstractmethod
|
|
26
|
-
def create_payment(
|
|
27
|
+
def create_payment(
|
|
28
|
+
self,
|
|
29
|
+
amount: Union[int, float, str],
|
|
30
|
+
account_id: Union[int, str],
|
|
31
|
+
**kwargs
|
|
32
|
+
) -> Dict[str, Any]:
|
|
27
33
|
"""
|
|
28
34
|
Create a payment.
|
|
29
35
|
|
|
@@ -51,7 +57,11 @@ class BasePaymentGateway(ABC):
|
|
|
51
57
|
raise NotImplementedError
|
|
52
58
|
|
|
53
59
|
@abstractmethod
|
|
54
|
-
def cancel_payment(
|
|
60
|
+
def cancel_payment(
|
|
61
|
+
self,
|
|
62
|
+
transaction_id: str,
|
|
63
|
+
reason: Optional[str] = None
|
|
64
|
+
) -> Dict[str, Any]:
|
|
55
65
|
"""
|
|
56
66
|
Cancel payment.
|
|
57
67
|
|
|
@@ -65,7 +75,6 @@ class BasePaymentGateway(ABC):
|
|
|
65
75
|
raise NotImplementedError
|
|
66
76
|
|
|
67
77
|
|
|
68
|
-
|
|
69
78
|
class BaseWebhookHandler(ABC):
|
|
70
79
|
"""
|
|
71
80
|
Base class for payment gateway webhook handlers.
|
gateways/payme/client.py
CHANGED
|
@@ -3,6 +3,7 @@ Payme payment gateway client.
|
|
|
3
3
|
"""
|
|
4
4
|
import logging
|
|
5
5
|
from typing import Dict, Any, Optional, Union
|
|
6
|
+
import base64
|
|
6
7
|
|
|
7
8
|
from paytechuz.core.base import BasePaymentGateway
|
|
8
9
|
from paytechuz.core.http import HttpClient
|
|
@@ -13,6 +14,7 @@ from paytechuz.gateways.payme.receipts import PaymeReceipts
|
|
|
13
14
|
|
|
14
15
|
logger = logging.getLogger(__name__)
|
|
15
16
|
|
|
17
|
+
|
|
16
18
|
class PaymeGateway(BasePaymentGateway):
|
|
17
19
|
"""
|
|
18
20
|
Payme payment gateway implementation.
|
|
@@ -56,116 +58,138 @@ class PaymeGateway(BasePaymentGateway):
|
|
|
56
58
|
payme_key=payme_key
|
|
57
59
|
)
|
|
58
60
|
|
|
61
|
+
def generate_pay_link(
|
|
62
|
+
self,
|
|
63
|
+
id: Union[int, str],
|
|
64
|
+
amount: Union[int, float, str],
|
|
65
|
+
return_url: str,
|
|
66
|
+
account_field_name: str = "order_id"
|
|
67
|
+
) -> str:
|
|
68
|
+
"""
|
|
69
|
+
Generate a payment link for a specific order.
|
|
70
|
+
|
|
71
|
+
Parameters
|
|
72
|
+
----------
|
|
73
|
+
id : Union[int, str]
|
|
74
|
+
Unique identifier for the account/order.
|
|
75
|
+
amount : Union[int, float, str]
|
|
76
|
+
Payment amount in som.
|
|
77
|
+
return_url : str
|
|
78
|
+
URL to redirect after payment completion.
|
|
79
|
+
account_field_name : str, optional
|
|
80
|
+
Field name for account identifier (default: "order_id").
|
|
81
|
+
|
|
82
|
+
Returns
|
|
83
|
+
-------
|
|
84
|
+
str
|
|
85
|
+
Payme checkout URL with encoded parameters.
|
|
86
|
+
|
|
87
|
+
References
|
|
88
|
+
----------
|
|
89
|
+
https://developer.help.paycom.uz/initsializatsiya-platezhey/
|
|
90
|
+
"""
|
|
91
|
+
# Convert amount to tiyin (1 som = 100 tiyin)
|
|
92
|
+
amount_tiyin = int(float(amount) * 100)
|
|
93
|
+
|
|
94
|
+
# Build parameters
|
|
95
|
+
params = (
|
|
96
|
+
f'm={self.payme_id};'
|
|
97
|
+
f'ac.{account_field_name}={id};'
|
|
98
|
+
f'a={amount_tiyin};'
|
|
99
|
+
f'c={return_url}'
|
|
100
|
+
)
|
|
101
|
+
encoded_params = base64.b64encode(params.encode("utf-8")).decode("utf-8")
|
|
102
|
+
|
|
103
|
+
# Return URL based on environment
|
|
104
|
+
base_url = "https://test.paycom.uz" if self.is_test_mode else "https://checkout.paycom.uz"
|
|
105
|
+
return f"{base_url}/{encoded_params}"
|
|
106
|
+
|
|
107
|
+
async def generate_pay_link_async(
|
|
108
|
+
self,
|
|
109
|
+
id: Union[int, str],
|
|
110
|
+
amount: Union[int, float, str],
|
|
111
|
+
return_url: str,
|
|
112
|
+
account_field_name: str = "order_id"
|
|
113
|
+
) -> str:
|
|
114
|
+
"""
|
|
115
|
+
Async version of generate_pay_link.
|
|
116
|
+
|
|
117
|
+
Parameters
|
|
118
|
+
----------
|
|
119
|
+
id : Union[int, str]
|
|
120
|
+
Unique identifier for the account/order.
|
|
121
|
+
amount : Union[int, float, str]
|
|
122
|
+
Payment amount in som.
|
|
123
|
+
return_url : str
|
|
124
|
+
URL to redirect after payment completion.
|
|
125
|
+
account_field_name : str, optional
|
|
126
|
+
Field name for account identifier (default: "order_id").
|
|
127
|
+
|
|
128
|
+
Returns
|
|
129
|
+
-------
|
|
130
|
+
str
|
|
131
|
+
Payme checkout URL with encoded parameters.
|
|
132
|
+
"""
|
|
133
|
+
return self.generate_pay_link(
|
|
134
|
+
id=id,
|
|
135
|
+
amount=amount,
|
|
136
|
+
return_url=return_url,
|
|
137
|
+
account_field_name=account_field_name
|
|
138
|
+
)
|
|
139
|
+
|
|
59
140
|
@handle_exceptions
|
|
60
141
|
def create_payment(
|
|
61
142
|
self,
|
|
143
|
+
id: Union[int, str],
|
|
62
144
|
amount: Union[int, float, str],
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
) ->
|
|
145
|
+
return_url: str = "",
|
|
146
|
+
account_field_name: str = "order_id"
|
|
147
|
+
) -> str:
|
|
66
148
|
"""
|
|
67
|
-
Create a payment using Payme
|
|
149
|
+
Create a payment using Payme.
|
|
68
150
|
|
|
69
151
|
Args:
|
|
70
|
-
amount:
|
|
71
|
-
account_id:
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
- detail: Payment details
|
|
75
|
-
- callback_url: URL to redirect after payment
|
|
76
|
-
- return_url: URL to return after payment
|
|
77
|
-
- phone: Customer phone number
|
|
78
|
-
- email: Customer email
|
|
79
|
-
- language: Language code (uz, ru, en)
|
|
80
|
-
- expire_minutes: Payment expiration time in minutes
|
|
152
|
+
amount: Payment amount in som
|
|
153
|
+
account_id: Account or order ID
|
|
154
|
+
return_url: Return URL after payment (default: "")
|
|
155
|
+
account_field_name: Field name for account ID (default: "order_id")
|
|
81
156
|
|
|
82
157
|
Returns:
|
|
83
|
-
|
|
158
|
+
str: Payme payment URL
|
|
159
|
+
"""
|
|
160
|
+
return self.generate_pay_link(
|
|
161
|
+
id=id,
|
|
162
|
+
amount=amount,
|
|
163
|
+
return_url=return_url,
|
|
164
|
+
account_field_name=account_field_name
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
@handle_exceptions
|
|
168
|
+
async def create_payment_async(
|
|
169
|
+
self,
|
|
170
|
+
id: Union[int, str],
|
|
171
|
+
amount: Union[int, float, str],
|
|
172
|
+
return_url: str = "",
|
|
173
|
+
account_field_name: str = "order_id"
|
|
174
|
+
) -> str:
|
|
84
175
|
"""
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
amount=amount_tiyin,
|
|
103
|
-
account={"account_id": str(account_id)},
|
|
104
|
-
description=description,
|
|
105
|
-
detail=detail,
|
|
106
|
-
callback_url=callback_url,
|
|
107
|
-
return_url=return_url,
|
|
108
|
-
phone=phone,
|
|
109
|
-
email=email,
|
|
110
|
-
language=language,
|
|
111
|
-
expire_minutes=expire_minutes
|
|
112
|
-
)
|
|
113
|
-
|
|
114
|
-
# Extract receipt ID and payment URL
|
|
115
|
-
receipt_id = receipt_data.get('receipt', {}).get('_id')
|
|
116
|
-
payment_url = receipt_data.get('receipt', {}).get('pay_url')
|
|
117
|
-
|
|
118
|
-
return {
|
|
119
|
-
'transaction_id': receipt_id,
|
|
120
|
-
'payment_url': payment_url,
|
|
121
|
-
'amount': amount,
|
|
122
|
-
'account_id': account_id,
|
|
123
|
-
'status': 'created',
|
|
124
|
-
'raw_response': receipt_data
|
|
125
|
-
}
|
|
126
|
-
else:
|
|
127
|
-
# Generate a payment URL using payme-pkg style
|
|
128
|
-
# This is a fallback method that doesn't require authentication
|
|
129
|
-
import base64
|
|
130
|
-
from paytechuz.core.utils import generate_id
|
|
131
|
-
|
|
132
|
-
# Generate a unique transaction ID
|
|
133
|
-
transaction_id = generate_id("payme")
|
|
134
|
-
|
|
135
|
-
# Format amount to the smallest currency unit (tiyin)
|
|
136
|
-
# amount_tiyin is already in tiyin format
|
|
137
|
-
|
|
138
|
-
# Build the payment parameters string
|
|
139
|
-
# Format: m=merchant_id;ac.field=value;a=amount;c=callback_url
|
|
140
|
-
params_str = f"m={self.payme_id};ac.id={account_id};a={amount_tiyin}"
|
|
141
|
-
|
|
142
|
-
# Add callback URL if provided (this is used for return URL in payme-pkg)
|
|
143
|
-
if return_url:
|
|
144
|
-
params_str += f";c={return_url}"
|
|
145
|
-
|
|
146
|
-
# Encode the parameters string to base64
|
|
147
|
-
encoded_params = base64.b64encode(params_str.encode("utf-8")).decode("utf-8")
|
|
148
|
-
|
|
149
|
-
# Build the payment URL
|
|
150
|
-
if self.is_test_mode:
|
|
151
|
-
payment_url = f"https://test.paycom.uz/{encoded_params}"
|
|
152
|
-
else:
|
|
153
|
-
payment_url = f"https://checkout.paycom.uz/{encoded_params}"
|
|
154
|
-
|
|
155
|
-
# Print the parameters for debugging
|
|
156
|
-
print("Payme payment parameters:")
|
|
157
|
-
print(f"Parameters string: {params_str}")
|
|
158
|
-
print(f"Encoded parameters: {encoded_params}")
|
|
159
|
-
print(f"Payment URL: {payment_url}")
|
|
160
|
-
|
|
161
|
-
return {
|
|
162
|
-
'transaction_id': transaction_id,
|
|
163
|
-
'payment_url': payment_url,
|
|
164
|
-
'amount': amount,
|
|
165
|
-
'account_id': account_id,
|
|
166
|
-
'status': 'created',
|
|
167
|
-
'raw_response': {}
|
|
168
|
-
}
|
|
176
|
+
Async version of create_payment.
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
amount: Payment amount in som
|
|
180
|
+
account_id: Account or order ID
|
|
181
|
+
return_url: Return URL after payment (default: "")
|
|
182
|
+
account_field_name: Field name for account ID (default: "order_id")
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
str: Payme payment URL
|
|
186
|
+
"""
|
|
187
|
+
return await self.generate_pay_link_async(
|
|
188
|
+
id=id,
|
|
189
|
+
amount=amount,
|
|
190
|
+
return_url=return_url,
|
|
191
|
+
account_field_name=account_field_name
|
|
192
|
+
)
|
|
169
193
|
|
|
170
194
|
@handle_exceptions
|
|
171
195
|
def check_payment(self, transaction_id: str) -> Dict[str, Any]:
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: paytechuz
|
|
3
|
+
Version: 0.1.4
|
|
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://opensource.org/licenses/MIT)
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
### Basic Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install paytechuz
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Framework-Specific Installation
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# For Django
|
|
33
|
+
pip install paytechuz[django]
|
|
34
|
+
|
|
35
|
+
# For FastAPI
|
|
36
|
+
pip install paytechuz[fastapi]
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Quick Start
|
|
40
|
+
|
|
41
|
+
### Generate Payment Links
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
from paytechuz.gateways.payme import PaymeGateway
|
|
45
|
+
from paytechuz.gateways.click import ClickGateway
|
|
46
|
+
|
|
47
|
+
# Initialize gateways
|
|
48
|
+
payme = PaymeGateway()
|
|
49
|
+
click = ClickGateway()
|
|
50
|
+
|
|
51
|
+
# Generate payment links
|
|
52
|
+
payme_link = payme.generate_payment_link(
|
|
53
|
+
id="order_123",
|
|
54
|
+
amount=150000, # amount in UZS
|
|
55
|
+
return_url="https://example.com/return"
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
click_link = click.generate_payment_link(
|
|
59
|
+
id="order_123",
|
|
60
|
+
amount=150000, # amount in UZS
|
|
61
|
+
return_url="https://example.com/return"
|
|
62
|
+
)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Django Integration
|
|
66
|
+
|
|
67
|
+
1. Add to `INSTALLED_APPS`:
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
# settings.py
|
|
71
|
+
INSTALLED_APPS = [
|
|
72
|
+
# ...
|
|
73
|
+
'paytechuz.integrations.django',
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
PAYME_ID = 'your_payme_merchant_id'
|
|
77
|
+
PAYME_KEY = 'your_payme_merchant_key'
|
|
78
|
+
PAYME_ACCOUNT_MODEL = 'your_app.models.YourModel' # For example: 'orders.models.Order'
|
|
79
|
+
PAYME_ACCOUNT_FIELD = 'id'
|
|
80
|
+
PAYME_AMOUNT_FIELD = 'amount' # Field for storing payment amount
|
|
81
|
+
PAYME_ONE_TIME_PAYMENT = True # Allow only one payment per account
|
|
82
|
+
|
|
83
|
+
CLICK_SERVICE_ID = 'your_click_service_id'
|
|
84
|
+
CLICK_MERCHANT_ID = 'your_click_merchant_id'
|
|
85
|
+
CLICK_SECRET_KEY = 'your_click_secret_key'
|
|
86
|
+
CLICK_ACCOUNT_MODEL = 'your_app.models.YourModel'
|
|
87
|
+
CLICK_COMMISSION_PERCENT = 0.0
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
2. Create webhook handlers:
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
# views.py
|
|
94
|
+
from paytechuz.integrations.django.views import PaymeWebhookView
|
|
95
|
+
from .models import Order
|
|
96
|
+
|
|
97
|
+
class PaymeWebhookView(PaymeWebhookView):
|
|
98
|
+
def successfully_payment(self, params, transaction):
|
|
99
|
+
order = Order.objects.get(id=transaction.account_id)
|
|
100
|
+
order.status = 'paid'
|
|
101
|
+
order.save()
|
|
102
|
+
|
|
103
|
+
def cancelled_payment(self, params, transaction):
|
|
104
|
+
order = Order.objects.get(id=transaction.account_id)
|
|
105
|
+
order.status = 'cancelled'
|
|
106
|
+
order.save()
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### FastAPI Integration
|
|
110
|
+
|
|
111
|
+
1. Create webhook handler:
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
from fastapi import FastAPI, Request
|
|
115
|
+
from paytechuz.integrations.fastapi import PaymeWebhookHandler
|
|
116
|
+
|
|
117
|
+
app = FastAPI()
|
|
118
|
+
|
|
119
|
+
class CustomPaymeWebhookHandler(PaymeWebhookHandler):
|
|
120
|
+
def successfully_payment(self, params, transaction):
|
|
121
|
+
# Handle successful payment
|
|
122
|
+
order = self.db.query(Order).filter(Order.id == transaction.account_id).first()
|
|
123
|
+
order.status = "paid"
|
|
124
|
+
self.db.commit()
|
|
125
|
+
|
|
126
|
+
def cancelled_payment(self, params, transaction):
|
|
127
|
+
# Handle cancelled payment
|
|
128
|
+
order = self.db.query(Order).filter(Order.id == transaction.account_id).first()
|
|
129
|
+
order.status = "cancelled"
|
|
130
|
+
self.db.commit()
|
|
131
|
+
|
|
132
|
+
@app.post("/payments/payme/webhook")
|
|
133
|
+
async def payme_webhook(request: Request):
|
|
134
|
+
handler = CustomPaymeWebhookHandler(
|
|
135
|
+
payme_id="your_merchant_id",
|
|
136
|
+
payme_key="your_merchant_key"
|
|
137
|
+
)
|
|
138
|
+
return await handler.handle_webhook(request)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Documentation
|
|
142
|
+
|
|
143
|
+
Detailed documentation is available in multiple languages:
|
|
144
|
+
|
|
145
|
+
- 📖 [English Documentation](docs/en/index.md)
|
|
146
|
+
- 📖 [O'zbek tilidagi hujjatlar](docs/uz/index.md)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
core/base.py,sha256=
|
|
2
|
+
core/base.py,sha256=pW_0QrlyX0F8XVToeeDEI78ygAF2IgvvvyUGYMHH9bs,2565
|
|
3
3
|
core/constants.py,sha256=P2zeZ_cfZIttdC1vqkpIngkfRFh6loWzJYEgzQb5cKA,1660
|
|
4
4
|
core/exceptions.py,sha256=XMJkqiponTkvhjoh3S2iFNuU3UbBdFW4130kd0hpudg,5489
|
|
5
5
|
core/http.py,sha256=qmLR6ujxmIPjwoTS4vvKRIvDnNcpl84sS1HmVB890b0,7686
|
|
@@ -12,7 +12,7 @@ gateways/click/merchant.py,sha256=vJ_DivA1KfRT5p3sfA5yZGMYXoUmVbAM7QHvaXr6VCU,72
|
|
|
12
12
|
gateways/click/webhook.py,sha256=rph-NmjjnBKMW4rcxQTXrHHdK-uMrU39kXnbqK56leo,7936
|
|
13
13
|
gateways/payme/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
14
|
gateways/payme/cards.py,sha256=aL6su_ZTCBPU8qmrz2Jcw_Wn7Zf9RwIu8k10o4AWFTs,5420
|
|
15
|
-
gateways/payme/client.py,sha256=
|
|
15
|
+
gateways/payme/client.py,sha256=B4jxjX_7J_uR-NP8-B6wO_VrnwO_RRh1ATiKsO4ZinM,7855
|
|
16
16
|
gateways/payme/receipts.py,sha256=DdrZMPeDvQmGyqAEOTmtUorfcIVVb3t2tg31l7TXqHo,8904
|
|
17
17
|
gateways/payme/webhook.py,sha256=-0O8vzMtiu4U8FWFKDA6EfyoX4NEGqcEq-T0yNtVhM4,12374
|
|
18
18
|
integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -29,8 +29,7 @@ integrations/fastapi/__init__.py,sha256=DLnhAZQZf2ghu8BuFFfE7FzbNKWQQ2SLG8qxldRu
|
|
|
29
29
|
integrations/fastapi/models.py,sha256=eWGUpiKufj47AK8Hld4A91jRDj0ZKQzAf95CyUozmvo,4638
|
|
30
30
|
integrations/fastapi/routes.py,sha256=D17QeyY4-aX6tCNmk5h3UiavukvVrE5e6JOFCy4t_n8,36629
|
|
31
31
|
integrations/fastapi/schemas.py,sha256=CkNohj22mQQje8Pu_IkTQwUPAoYHNOKXlGjqaRX_SGQ,3784
|
|
32
|
-
paytechuz/
|
|
33
|
-
paytechuz-0.1.
|
|
34
|
-
paytechuz-0.1.
|
|
35
|
-
paytechuz-0.1.
|
|
36
|
-
paytechuz-0.1.2.dist-info/RECORD,,
|
|
32
|
+
paytechuz-0.1.4.dist-info/METADATA,sha256=4QSapmW-hD_4AJm0rE-OvnXIhRRgsePVnsX6UKczfh4,3861
|
|
33
|
+
paytechuz-0.1.4.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
|
34
|
+
paytechuz-0.1.4.dist-info/top_level.txt,sha256=tfgxeqY7QDfOFb07FwoMkscdgKV9VIbJLnJnh2FNjRM,27
|
|
35
|
+
paytechuz-0.1.4.dist-info/RECORD,,
|
paytechuz/__init__.py
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
"""PayTechUZ - Unified payment library for Uzbekistan payment systems.
|
|
2
|
-
|
|
3
|
-
This library provides a unified interface for working with Payme and Click
|
|
4
|
-
payment systems in Uzbekistan. It supports Django, Flask, and FastAPI.
|
|
5
|
-
"""
|
|
6
|
-
from typing import Any
|
|
7
|
-
|
|
8
|
-
__version__ = '0.1.1'
|
|
9
|
-
|
|
10
|
-
# Define dummy classes to avoid import errors
|
|
11
|
-
class PaymeGateway:
|
|
12
|
-
"""Dummy PaymeGateway class to avoid import errors."""
|
|
13
|
-
def __init__(self, **kwargs):
|
|
14
|
-
pass
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class ClickGateway:
|
|
18
|
-
"""Dummy ClickGateway class to avoid import errors."""
|
|
19
|
-
def __init__(self, **kwargs):
|
|
20
|
-
pass
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
class PaymentGateway:
|
|
24
|
-
"""Dummy PaymentGateway enum to avoid import errors."""
|
|
25
|
-
class PAYME:
|
|
26
|
-
value = 'payme'
|
|
27
|
-
|
|
28
|
-
class CLICK:
|
|
29
|
-
value = 'click'
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
# Import framework integrations - these imports are used to check availability
|
|
33
|
-
# of frameworks, not for direct usage
|
|
34
|
-
try:
|
|
35
|
-
import django # noqa: F401 - Used for availability check
|
|
36
|
-
HAS_DJANGO = True
|
|
37
|
-
except ImportError:
|
|
38
|
-
HAS_DJANGO = False
|
|
39
|
-
|
|
40
|
-
try:
|
|
41
|
-
import fastapi # noqa: F401 - Used for availability check
|
|
42
|
-
HAS_FASTAPI = True
|
|
43
|
-
except ImportError:
|
|
44
|
-
HAS_FASTAPI = False
|
|
45
|
-
|
|
46
|
-
try:
|
|
47
|
-
import flask # noqa: F401 - Used for availability check
|
|
48
|
-
HAS_FLASK = True
|
|
49
|
-
except ImportError:
|
|
50
|
-
HAS_FLASK = False
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
def create_gateway(gateway_type: str, **kwargs) -> Any:
|
|
54
|
-
"""
|
|
55
|
-
Create a payment gateway instance.
|
|
56
|
-
|
|
57
|
-
Args:
|
|
58
|
-
gateway_type: Type of gateway ('payme' or 'click')
|
|
59
|
-
**kwargs: Gateway-specific configuration
|
|
60
|
-
|
|
61
|
-
Returns:
|
|
62
|
-
Payment gateway instance
|
|
63
|
-
|
|
64
|
-
Raises:
|
|
65
|
-
ValueError: If the gateway type is not supported
|
|
66
|
-
ImportError: If the required gateway module is not available
|
|
67
|
-
"""
|
|
68
|
-
# Just use the dummy classes for now
|
|
69
|
-
if gateway_type.lower() == 'payme':
|
|
70
|
-
return PaymeGateway(**kwargs)
|
|
71
|
-
if gateway_type.lower() == 'click':
|
|
72
|
-
return ClickGateway(**kwargs)
|
|
73
|
-
|
|
74
|
-
raise ValueError(f"Unsupported gateway type: {gateway_type}")
|
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: paytechuz
|
|
3
|
-
Version: 0.1.2
|
|
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
|
-
Keywords: paytechuz,payme,click,uzbekistan,payment,gateway,payment-gateway,payment-processing,django,flask,fastapi
|
|
10
|
-
Classifier: Development Status :: 4 - Beta
|
|
11
|
-
Classifier: Intended Audience :: Developers
|
|
12
|
-
Classifier: Programming Language :: Python :: 3
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.6
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
21
|
-
Classifier: Operating System :: OS Independent
|
|
22
|
-
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
-
Requires-Python: >=3.6
|
|
24
|
-
Description-Content-Type: text/markdown
|
|
25
|
-
Requires-Dist: requests<3.0,>=2.0
|
|
26
|
-
Requires-Dist: dataclasses<1.0,>=0.6; python_version < "3.7"
|
|
27
|
-
Provides-Extra: django
|
|
28
|
-
Requires-Dist: django<5.0,>=3.0; extra == "django"
|
|
29
|
-
Requires-Dist: djangorestframework<4.0,>=3.0; extra == "django"
|
|
30
|
-
Provides-Extra: fastapi
|
|
31
|
-
Requires-Dist: fastapi<1.0.0,>=0.68.0; extra == "fastapi"
|
|
32
|
-
Requires-Dist: sqlalchemy<3.0,>=1.4; extra == "fastapi"
|
|
33
|
-
Requires-Dist: httpx<1.0,>=0.20; extra == "fastapi"
|
|
34
|
-
Requires-Dist: python-multipart==0.0.20; extra == "fastapi"
|
|
35
|
-
Requires-Dist: pydantic<2.0,>=1.8; extra == "fastapi"
|
|
36
|
-
Provides-Extra: flask
|
|
37
|
-
Requires-Dist: flask<3.0,>=2.0; extra == "flask"
|
|
38
|
-
Requires-Dist: flask-sqlalchemy<3.0,>=2.5; extra == "flask"
|
|
39
|
-
Dynamic: author
|
|
40
|
-
Dynamic: author-email
|
|
41
|
-
Dynamic: classifier
|
|
42
|
-
Dynamic: description
|
|
43
|
-
Dynamic: description-content-type
|
|
44
|
-
Dynamic: home-page
|
|
45
|
-
Dynamic: keywords
|
|
46
|
-
Dynamic: license
|
|
47
|
-
Dynamic: provides-extra
|
|
48
|
-
Dynamic: requires-dist
|
|
49
|
-
Dynamic: requires-python
|
|
50
|
-
Dynamic: summary
|
|
51
|
-
|
|
52
|
-
# paytechuz
|
|
53
|
-
|
|
54
|
-
paytechuz is a unified payment library for integration with popular payment systems in Uzbekistan (Payme and Click).
|
|
55
|
-
|
|
56
|
-
[](https://badge.fury.io/py/paytechuz)
|
|
57
|
-
[](https://pypi.org/project/paytechuz/)
|
|
58
|
-
[](https://opensource.org/licenses/MIT)
|
|
59
|
-
|
|
60
|
-
## Installation
|
|
61
|
-
|
|
62
|
-
To install paytechuz with all dependencies:
|
|
63
|
-
|
|
64
|
-
```bash
|
|
65
|
-
pip install paytechuz
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
For specific framework support:
|
|
69
|
-
|
|
70
|
-
```bash
|
|
71
|
-
# For Django
|
|
72
|
-
pip install paytechuz[django]
|
|
73
|
-
|
|
74
|
-
# For FastAPI
|
|
75
|
-
pip install paytechuz[fastapi]
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
## Quick Start
|
|
79
|
-
|
|
80
|
-
### Django Integration
|
|
81
|
-
|
|
82
|
-
1. Add the app to your `INSTALLED_APPS`:
|
|
83
|
-
|
|
84
|
-
```python
|
|
85
|
-
# settings.py
|
|
86
|
-
INSTALLED_APPS = [
|
|
87
|
-
# ...
|
|
88
|
-
'paytechuz.integrations.django',
|
|
89
|
-
]
|
|
90
|
-
|
|
91
|
-
# Payme settings
|
|
92
|
-
PAYME_ID = 'your_payme_merchant_id'
|
|
93
|
-
PAYME_KEY = 'your_payme_merchant_key'
|
|
94
|
-
PAYME_ACCOUNT_MODEL = 'your_app.YourModel' # For example: 'orders.Order'
|
|
95
|
-
PAYME_ACCOUNT_FIELD = 'id' # Field for account identifier
|
|
96
|
-
PAYME_AMOUNT_FIELD = 'amount' # Field for storing payment amount
|
|
97
|
-
PAYME_ONE_TIME_PAYMENT = True # Allow only one payment per account
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
2. Set up the webhook URLs:
|
|
101
|
-
|
|
102
|
-
```python
|
|
103
|
-
# urls.py
|
|
104
|
-
from django.urls import path
|
|
105
|
-
from django.views.decorators.csrf import csrf_exempt
|
|
106
|
-
|
|
107
|
-
from your_app.views import PaymeWebhookView, ClickWebhookView
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
urlpatterns = [
|
|
111
|
-
# ...
|
|
112
|
-
path('payments/payme/', csrf_exempt(PaymeWebhookView.as_view()), name='payme_webhook'),
|
|
113
|
-
path('payments/click/', csrf_exempt(ClickWebhookView.as_view()), name='click_webhook'),
|
|
114
|
-
]
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
3. Create custom webhook handlers:
|
|
118
|
-
|
|
119
|
-
```python
|
|
120
|
-
# views.py
|
|
121
|
-
from paytechuz.integrations.django.views import PaymeWebhookView as BasePaymeWebhookView
|
|
122
|
-
from .models import Order
|
|
123
|
-
|
|
124
|
-
class PaymeWebhookView(BasePaymeWebhookView):
|
|
125
|
-
def successfully_payment(self, params, transaction):
|
|
126
|
-
"""Called when payment is successful"""
|
|
127
|
-
order_id = transaction.account_id
|
|
128
|
-
order = Order.objects.get(id=order_id)
|
|
129
|
-
order.status = 'paid'
|
|
130
|
-
order.save()
|
|
131
|
-
|
|
132
|
-
def cancelled_payment(self, params, transaction):
|
|
133
|
-
"""Called when payment is cancelled"""
|
|
134
|
-
order_id = transaction.account_id
|
|
135
|
-
order = Order.objects.get(id=order_id)
|
|
136
|
-
order.status = 'cancelled'
|
|
137
|
-
order.save()
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
### FastAPI Integration
|
|
141
|
-
|
|
142
|
-
1. Create a custom webhook handler:
|
|
143
|
-
|
|
144
|
-
```python
|
|
145
|
-
from fastapi import APIRouter, Depends, Request
|
|
146
|
-
from sqlalchemy.orm import Session
|
|
147
|
-
|
|
148
|
-
from app.database.db import get_db
|
|
149
|
-
from app.models.models import Order
|
|
150
|
-
from paytechuz.integrations.fastapi import PaymeWebhookHandler
|
|
151
|
-
|
|
152
|
-
# Payme configuration
|
|
153
|
-
PAYME_ID = 'your_payme_id'
|
|
154
|
-
PAYME_KEY = 'your_payme_key'
|
|
155
|
-
|
|
156
|
-
class CustomPaymeWebhookHandler(PaymeWebhookHandler):
|
|
157
|
-
def successfully_payment(self, params, transaction) -> None:
|
|
158
|
-
"""Called when payment is successful"""
|
|
159
|
-
order = self.db.query(Order).filter(Order.id == transaction.account_id).first()
|
|
160
|
-
if order:
|
|
161
|
-
order.status = "paid"
|
|
162
|
-
self.db.commit()
|
|
163
|
-
|
|
164
|
-
def cancelled_payment(self, params, transaction) -> None:
|
|
165
|
-
"""Called when payment is cancelled"""
|
|
166
|
-
order = self.db.query(Order).filter(Order.id == transaction.account_id).first()
|
|
167
|
-
if order:
|
|
168
|
-
order.status = "cancelled"
|
|
169
|
-
self.db.commit()
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
2. Create a webhook endpoint:
|
|
173
|
-
|
|
174
|
-
```python
|
|
175
|
-
router = APIRouter()
|
|
176
|
-
|
|
177
|
-
@router.post("/payments/payme/webhook")
|
|
178
|
-
async def payme_webhook(request: Request, db: Session = Depends(get_db)):
|
|
179
|
-
"""Handle Payme webhook requests"""
|
|
180
|
-
handler = CustomPaymeWebhookHandler(
|
|
181
|
-
db=db,
|
|
182
|
-
payme_id=PAYME_ID,
|
|
183
|
-
payme_key=PAYME_KEY,
|
|
184
|
-
account_model=Order,
|
|
185
|
-
account_field="id",
|
|
186
|
-
amount_field="amount",
|
|
187
|
-
one_time_payment=False
|
|
188
|
-
)
|
|
189
|
-
result = await handler.handle_webhook(request)
|
|
190
|
-
return result
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
## Documentation
|
|
194
|
-
|
|
195
|
-
For detailed documentation, see:
|
|
196
|
-
|
|
197
|
-
- [English Documentation](paytechuz/docs/en/index.md)
|
|
198
|
-
- [O'zbek tilidagi hujjatlar](paytechuz/docs/index.md)
|
|
File without changes
|