shops-payment-processing 1.2.0.dev33607__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.
- shops_payment_processing-1.2.0.dev33607/PKG-INFO +12 -0
- shops_payment_processing-1.2.0.dev33607/README.md +0 -0
- shops_payment_processing-1.2.0.dev33607/pyproject.toml +32 -0
- shops_payment_processing-1.2.0.dev33607/shops_payment_processing/__init__.py +0 -0
- shops_payment_processing-1.2.0.dev33607/shops_payment_processing/__init__.pyi +0 -0
- shops_payment_processing-1.2.0.dev33607/shops_payment_processing/handlers/__init__.py +0 -0
- shops_payment_processing-1.2.0.dev33607/shops_payment_processing/handlers/__init__.pyi +0 -0
- shops_payment_processing-1.2.0.dev33607/shops_payment_processing/handlers/invoice_creation.py +295 -0
- shops_payment_processing-1.2.0.dev33607/shops_payment_processing/handlers/invoice_creation.pyi +23 -0
- shops_payment_processing-1.2.0.dev33607/shops_payment_processing/logging_config.py +3 -0
- shops_payment_processing-1.2.0.dev33607/shops_payment_processing/logging_config.pyi +3 -0
- shops_payment_processing-1.2.0.dev33607/shops_payment_processing/models/__init__.py +0 -0
- shops_payment_processing-1.2.0.dev33607/shops_payment_processing/models/__init__.pyi +0 -0
- shops_payment_processing-1.2.0.dev33607/shops_payment_processing/models/invoice.py +16 -0
- shops_payment_processing-1.2.0.dev33607/shops_payment_processing/models/invoice.pyi +14 -0
- shops_payment_processing-1.2.0.dev33607/shops_payment_processing/models/order.py +89 -0
- shops_payment_processing-1.2.0.dev33607/shops_payment_processing/models/order.pyi +71 -0
- shops_payment_processing-1.2.0.dev33607/shops_payment_processing/models/payment.py +22 -0
- shops_payment_processing-1.2.0.dev33607/shops_payment_processing/models/payment.pyi +17 -0
- shops_payment_processing-1.2.0.dev33607/shops_payment_processing/models/shop.py +10 -0
- shops_payment_processing-1.2.0.dev33607/shops_payment_processing/models/shop.pyi +9 -0
- shops_payment_processing-1.2.0.dev33607/shops_payment_processing/models/user.py +9 -0
- shops_payment_processing-1.2.0.dev33607/shops_payment_processing/models/user.pyi +8 -0
- shops_payment_processing-1.2.0.dev33607/shops_payment_processing/services/__init__.py +0 -0
- shops_payment_processing-1.2.0.dev33607/shops_payment_processing/services/__init__.pyi +0 -0
- shops_payment_processing-1.2.0.dev33607/shops_payment_processing/services/base.py +186 -0
- shops_payment_processing-1.2.0.dev33607/shops_payment_processing/services/base.pyi +65 -0
- shops_payment_processing-1.2.0.dev33607/shops_payment_processing/services/cloudpayments.py +180 -0
- shops_payment_processing-1.2.0.dev33607/shops_payment_processing/services/cloudpayments.pyi +13 -0
- shops_payment_processing-1.2.0.dev33607/shops_payment_processing/services/life_pay.py +61 -0
- shops_payment_processing-1.2.0.dev33607/shops_payment_processing/services/life_pay.pyi +13 -0
- shops_payment_processing-1.2.0.dev33607/shops_payment_processing/services/tkassa.py +114 -0
- shops_payment_processing-1.2.0.dev33607/shops_payment_processing/services/tkassa.pyi +10 -0
- shops_payment_processing-1.2.0.dev33607/shops_payment_processing/services/yookassa.py +115 -0
- shops_payment_processing-1.2.0.dev33607/shops_payment_processing/services/yookassa.pyi +10 -0
- shops_payment_processing-1.2.0.dev33607/shops_payment_processing/utils/__init__.py +0 -0
- shops_payment_processing-1.2.0.dev33607/shops_payment_processing/utils/__init__.pyi +0 -0
- shops_payment_processing-1.2.0.dev33607/shops_payment_processing/utils/link_generation.py +9 -0
- shops_payment_processing-1.2.0.dev33607/shops_payment_processing/utils/link_generation.pyi +1 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: shops-payment-processing
|
|
3
|
+
Version: 1.2.0.dev33607
|
|
4
|
+
Summary: Payment processing library for tg-shops.
|
|
5
|
+
Author-email: Pavel Mulin <pavel@shopsbuilder.app>
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Requires-Dist: annotated-types>=0.7.0
|
|
8
|
+
Requires-Dist: fastapi>=0.112.2
|
|
9
|
+
Requires-Dist: httpx>=0.25.0
|
|
10
|
+
Requires-Dist: pydantic-core>=2.27.1
|
|
11
|
+
Requires-Dist: pydantic>=2.10.3
|
|
12
|
+
Requires-Dist: typing-extensions>=4.12.2
|
|
File without changes
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "shops-payment-processing"
|
|
3
|
+
version = "v1.2.0.dev33607"
|
|
4
|
+
description = "Payment processing library for tg-shops."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.10"
|
|
7
|
+
authors = [{ name = "Pavel Mulin", email = "pavel@shopsbuilder.app" }]
|
|
8
|
+
dependencies = [
|
|
9
|
+
"fastapi>=0.112.2",
|
|
10
|
+
"httpx>=0.25.0",
|
|
11
|
+
"pydantic>=2.10.3",
|
|
12
|
+
"pydantic-core>=2.27.1",
|
|
13
|
+
"annotated-types>=0.7.0",
|
|
14
|
+
"typing_extensions>=4.12.2",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[build-system]
|
|
18
|
+
requires = ["hatchling"]
|
|
19
|
+
build-backend = "hatchling.build"
|
|
20
|
+
|
|
21
|
+
# If your package directory is named `shops_payment_processing/`
|
|
22
|
+
# this ensures hatch builds the right wheel contents.
|
|
23
|
+
[tool.hatch.build.targets.wheel]
|
|
24
|
+
packages = ["shops_payment_processing"]
|
|
25
|
+
|
|
26
|
+
# Optional: include package sources in sdist (nice for PyPI)
|
|
27
|
+
[tool.hatch.build.targets.sdist]
|
|
28
|
+
include = [
|
|
29
|
+
"shops_payment_processing/**",
|
|
30
|
+
"README.md",
|
|
31
|
+
"pyproject.toml",
|
|
32
|
+
]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
from fastapi import HTTPException
|
|
2
|
+
|
|
3
|
+
from shops_payment_processing.logging_config import logger
|
|
4
|
+
from shops_payment_processing.models.invoice import InvoiceWithPaymentLinkMessageModel
|
|
5
|
+
from shops_payment_processing.models.order import OrderResponseModel, PaymentTypes
|
|
6
|
+
from shops_payment_processing.models.payment import PaymentMethodDBResponseModel
|
|
7
|
+
from shops_payment_processing.models.shop import ShopDetailsModel
|
|
8
|
+
from shops_payment_processing.models.user import UserModel
|
|
9
|
+
from shops_payment_processing.services.base import CreateInvoiceRequest
|
|
10
|
+
from shops_payment_processing.services.cloudpayments import CloudPaymentsAPI
|
|
11
|
+
from shops_payment_processing.services.life_pay import LifePayAPI
|
|
12
|
+
from shops_payment_processing.services.tkassa import TKassaAPI
|
|
13
|
+
from shops_payment_processing.services.yookassa import YooKassaAPI
|
|
14
|
+
from shops_payment_processing.utils.link_generation import generate_web_app_order_link
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class InvoiceCreation:
|
|
18
|
+
def __init__(self, base_redirect_url: str, base_callback_url: str):
|
|
19
|
+
self.BASE_REDIRECT_URL = base_redirect_url
|
|
20
|
+
self.BASE_CALLBACK_URL = base_callback_url
|
|
21
|
+
|
|
22
|
+
async def get_life_pay_sbp_payment_data(
|
|
23
|
+
self,
|
|
24
|
+
user: UserModel,
|
|
25
|
+
order: OrderResponseModel,
|
|
26
|
+
payment: PaymentMethodDBResponseModel,
|
|
27
|
+
shop_details: ShopDetailsModel,
|
|
28
|
+
) -> InvoiceWithPaymentLinkMessageModel:
|
|
29
|
+
fiat_currency = order.basket.products[0].currency
|
|
30
|
+
if fiat_currency != "RUB":
|
|
31
|
+
raise HTTPException(
|
|
32
|
+
status_code=404, detail="LifePay payment method is only available for RUB currency"
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
login = ""
|
|
36
|
+
if payment.meta:
|
|
37
|
+
for meta in payment.meta:
|
|
38
|
+
if meta.key == "LIFE_PAY_LOGIN" and meta.value:
|
|
39
|
+
login = meta.value[0]
|
|
40
|
+
break
|
|
41
|
+
if not login or not payment.payment_data:
|
|
42
|
+
raise HTTPException(
|
|
43
|
+
status_code=404, detail="LifePay login was not configured for the shop"
|
|
44
|
+
)
|
|
45
|
+
life_pay_connector = LifePayAPI(callback_base_url=self.BASE_CALLBACK_URL)
|
|
46
|
+
invoice_response = await life_pay_connector.create_sbp_invoice(
|
|
47
|
+
shop_name=shop_details.shop_name, api_key=payment.payment_data, login=login, order=order
|
|
48
|
+
)
|
|
49
|
+
if invoice_response.status_code != 200:
|
|
50
|
+
raise HTTPException(
|
|
51
|
+
status_code=500, detail="Error creating LifePay invoice: " + invoice_response.text
|
|
52
|
+
)
|
|
53
|
+
invoice_json = invoice_response.json()
|
|
54
|
+
invoice_data = invoice_json.get("data")
|
|
55
|
+
if not invoice_data:
|
|
56
|
+
invoice_error_message = invoice_json.get("message")
|
|
57
|
+
if invoice_error_message:
|
|
58
|
+
raise HTTPException(status_code=500, detail=invoice_error_message)
|
|
59
|
+
raise HTTPException(
|
|
60
|
+
status_code=500, detail="Error creating LifePay invoice: " + invoice_response.text
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
logger.debug(f"LifePay SBP invoice data: {invoice_data}")
|
|
64
|
+
payment_url = invoice_data.get("paymentUrlWeb")
|
|
65
|
+
number = invoice_data.get("number")
|
|
66
|
+
qr_url = invoice_data.get("paymentUrl")
|
|
67
|
+
if not payment_url or not number or not qr_url:
|
|
68
|
+
raise HTTPException(
|
|
69
|
+
status_code=500, detail="Missing data from LifePay: " + invoice_response.text
|
|
70
|
+
)
|
|
71
|
+
return InvoiceWithPaymentLinkMessageModel(
|
|
72
|
+
chat_id=user.tg_id,
|
|
73
|
+
order_id=order.id,
|
|
74
|
+
order_number=order.order_number,
|
|
75
|
+
payload=number,
|
|
76
|
+
currency=fiat_currency,
|
|
77
|
+
payment_address=qr_url,
|
|
78
|
+
payment_link=payment_url,
|
|
79
|
+
amount=order.basket.amount,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
async def get_yookassa_payment_data(
|
|
83
|
+
self,
|
|
84
|
+
user: UserModel,
|
|
85
|
+
order: OrderResponseModel,
|
|
86
|
+
payment: PaymentMethodDBResponseModel,
|
|
87
|
+
shop_details: ShopDetailsModel,
|
|
88
|
+
) -> InvoiceWithPaymentLinkMessageModel:
|
|
89
|
+
fiat_currency = order.basket.products[0].currency
|
|
90
|
+
if fiat_currency != "RUB":
|
|
91
|
+
raise HTTPException(
|
|
92
|
+
status_code=404, detail="Yookassa payment method is only available for RUB currency"
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
account_id = ""
|
|
96
|
+
if payment.meta:
|
|
97
|
+
for meta in payment.meta:
|
|
98
|
+
if meta.key == "YOOKASSA_ACCOUNT_ID" and meta.value:
|
|
99
|
+
account_id = meta.value[0]
|
|
100
|
+
break
|
|
101
|
+
if not account_id or not payment.payment_data:
|
|
102
|
+
raise HTTPException(
|
|
103
|
+
status_code=404, detail="YooKassa login was not configured for the shop"
|
|
104
|
+
)
|
|
105
|
+
yookassa_connector = YooKassaAPI(account_id, payment.payment_data)
|
|
106
|
+
invoice_json = await yookassa_connector.create_sbp_invoice(
|
|
107
|
+
shop_name=shop_details.shop_name,
|
|
108
|
+
order=order,
|
|
109
|
+
receipt_email=shop_details.contact_email,
|
|
110
|
+
redirect_url=generate_web_app_order_link(
|
|
111
|
+
shop_name=shop_details.shop_name,
|
|
112
|
+
order_id=order.id,
|
|
113
|
+
tg_web_app_url=self.BASE_REDIRECT_URL,
|
|
114
|
+
),
|
|
115
|
+
)
|
|
116
|
+
invoice_data = invoice_json.get("confirmation", {})
|
|
117
|
+
if not invoice_data:
|
|
118
|
+
invoice_error_message = invoice_json.get("description")
|
|
119
|
+
if invoice_error_message:
|
|
120
|
+
raise HTTPException(status_code=500, detail=invoice_error_message)
|
|
121
|
+
raise HTTPException(status_code=500, detail=f"YooKassa: {invoice_data}")
|
|
122
|
+
|
|
123
|
+
logger.debug(f"YooKassa SBP invoice data: {invoice_data}")
|
|
124
|
+
payment_url = invoice_data.get("confirmation_url")
|
|
125
|
+
number = invoice_json.get("id")
|
|
126
|
+
qr_url = invoice_data.get("confirmation_url")
|
|
127
|
+
if not payment_url or not number or not qr_url:
|
|
128
|
+
raise HTTPException(status_code=500, detail=f"YooKassa: {invoice_data}")
|
|
129
|
+
return InvoiceWithPaymentLinkMessageModel(
|
|
130
|
+
chat_id=user.tg_id,
|
|
131
|
+
order_id=order.id,
|
|
132
|
+
order_number=order.order_number,
|
|
133
|
+
payload=number,
|
|
134
|
+
currency=fiat_currency,
|
|
135
|
+
payment_address=qr_url,
|
|
136
|
+
payment_link=payment_url,
|
|
137
|
+
amount=order.basket.amount,
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
async def get_tkassa_payment_data(
|
|
141
|
+
self,
|
|
142
|
+
user: UserModel,
|
|
143
|
+
order: OrderResponseModel,
|
|
144
|
+
payment: PaymentMethodDBResponseModel,
|
|
145
|
+
shop_details: ShopDetailsModel,
|
|
146
|
+
) -> InvoiceWithPaymentLinkMessageModel:
|
|
147
|
+
fiat_currency = order.basket.products[0].currency
|
|
148
|
+
if fiat_currency != "RUB":
|
|
149
|
+
raise HTTPException(
|
|
150
|
+
status_code=404, detail="Tkassa payment method is only available for RUB currency"
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
terminal_id = ""
|
|
154
|
+
if payment.meta:
|
|
155
|
+
for meta in payment.meta:
|
|
156
|
+
if meta.key == "TKASSA_TERMINAL_ID" and meta.value:
|
|
157
|
+
terminal_id = meta.value[0]
|
|
158
|
+
break
|
|
159
|
+
if not terminal_id or not payment.payment_data:
|
|
160
|
+
raise HTTPException(
|
|
161
|
+
status_code=404, detail="Tkassa credentials was not configured for the shop"
|
|
162
|
+
)
|
|
163
|
+
tkassa_connector = TKassaAPI(
|
|
164
|
+
terminal_id=terminal_id, terminal_password=payment.payment_data
|
|
165
|
+
)
|
|
166
|
+
invoice_json = await tkassa_connector.create_sbp_invoice(
|
|
167
|
+
shop_name=shop_details.shop_name,
|
|
168
|
+
receipt_email=shop_details.contact_email,
|
|
169
|
+
order=order,
|
|
170
|
+
redirect_url=generate_web_app_order_link(
|
|
171
|
+
shop_name=shop_details.shop_name,
|
|
172
|
+
order_id=order.id,
|
|
173
|
+
tg_web_app_url=self.BASE_REDIRECT_URL,
|
|
174
|
+
),
|
|
175
|
+
)
|
|
176
|
+
if not invoice_json.get("Success"):
|
|
177
|
+
invoice_error_message = invoice_json.get("Message")
|
|
178
|
+
invoice_error_details = invoice_json.get("Details")
|
|
179
|
+
if invoice_error_message:
|
|
180
|
+
invoice_error_response_message: str = invoice_error_message
|
|
181
|
+
if invoice_error_details:
|
|
182
|
+
invoice_error_response_message = (
|
|
183
|
+
invoice_error_response_message + " : " + invoice_error_details
|
|
184
|
+
)
|
|
185
|
+
raise HTTPException(status_code=500, detail=invoice_error_response_message)
|
|
186
|
+
raise HTTPException(status_code=500, detail=f"Tkassa: {invoice_json}")
|
|
187
|
+
|
|
188
|
+
logger.debug(f"Tkassa SBP invoice data: {invoice_json}")
|
|
189
|
+
payment_url = invoice_json.get("PaymentURL")
|
|
190
|
+
number = invoice_json.get("PaymentId")
|
|
191
|
+
qr_url = invoice_json.get("PaymentURL")
|
|
192
|
+
if not payment_url or not number or not qr_url:
|
|
193
|
+
raise HTTPException(status_code=500, detail=f"Tkassa: {invoice_json}")
|
|
194
|
+
return InvoiceWithPaymentLinkMessageModel(
|
|
195
|
+
chat_id=user.tg_id,
|
|
196
|
+
order_id=order.id,
|
|
197
|
+
order_number=order.order_number,
|
|
198
|
+
payload=number,
|
|
199
|
+
currency=fiat_currency,
|
|
200
|
+
payment_address=qr_url,
|
|
201
|
+
payment_link=payment_url,
|
|
202
|
+
amount=order.basket.amount,
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
async def get_cloudpayments_data(
|
|
206
|
+
self,
|
|
207
|
+
user: UserModel,
|
|
208
|
+
order: OrderResponseModel,
|
|
209
|
+
payment: PaymentMethodDBResponseModel,
|
|
210
|
+
shop_details: ShopDetailsModel,
|
|
211
|
+
) -> InvoiceWithPaymentLinkMessageModel:
|
|
212
|
+
fiat_currency = order.basket.products[0].currency
|
|
213
|
+
if fiat_currency != "RUB":
|
|
214
|
+
raise HTTPException(
|
|
215
|
+
status_code=404, detail="CloudPayments method is only available for RUB currency"
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
account_id = ""
|
|
219
|
+
if payment.meta:
|
|
220
|
+
for meta in payment.meta:
|
|
221
|
+
if meta.key == "CLOUDPAYMENTS_ACCOUNT_ID" and meta.value:
|
|
222
|
+
account_id = meta.value[0]
|
|
223
|
+
break
|
|
224
|
+
if not account_id or not payment.payment_data:
|
|
225
|
+
raise HTTPException(
|
|
226
|
+
status_code=404, detail="CloudPayments credentials were not configured for the shop"
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
# Create request DTO
|
|
230
|
+
invoice_request = CreateInvoiceRequest(
|
|
231
|
+
shop_name=shop_details.shop_name,
|
|
232
|
+
order_id=order.id,
|
|
233
|
+
order_number=order.order_number,
|
|
234
|
+
amount=order.basket.amount,
|
|
235
|
+
currency=fiat_currency,
|
|
236
|
+
description=f"Заказ {order.order_number}",
|
|
237
|
+
customer_phone=order.user_contact_number,
|
|
238
|
+
customer_email=shop_details.contact_email,
|
|
239
|
+
metadata={
|
|
240
|
+
"user_id": user.tg_id,
|
|
241
|
+
"orderId": order.id,
|
|
242
|
+
"orderNumber": order.order_number,
|
|
243
|
+
},
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
# Create API client and call create_invoice
|
|
247
|
+
cloudpayments_connector = CloudPaymentsAPI(
|
|
248
|
+
account_id=account_id, password=payment.payment_data
|
|
249
|
+
)
|
|
250
|
+
invoice_response = await cloudpayments_connector.create_invoice(invoice_request)
|
|
251
|
+
if not invoice_response.payment_id or not invoice_response.payment_url:
|
|
252
|
+
raise HTTPException(status_code=500, detail="Error creating CloudPayments invoice")
|
|
253
|
+
|
|
254
|
+
logger.debug(f"CloudPayments invoice data: {invoice_response.raw_response}")
|
|
255
|
+
|
|
256
|
+
return InvoiceWithPaymentLinkMessageModel(
|
|
257
|
+
chat_id=user.tg_id,
|
|
258
|
+
order_id=order.id,
|
|
259
|
+
order_number=order.order_number,
|
|
260
|
+
payload=invoice_response.payment_id,
|
|
261
|
+
currency=fiat_currency,
|
|
262
|
+
payment_address=invoice_response.payment_url,
|
|
263
|
+
payment_link=invoice_response.payment_url,
|
|
264
|
+
amount=order.basket.amount,
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
async def get_invoice_data(
|
|
268
|
+
self,
|
|
269
|
+
payment_type: PaymentMethodDBResponseModel,
|
|
270
|
+
user: UserModel,
|
|
271
|
+
order: OrderResponseModel,
|
|
272
|
+
shop_details: ShopDetailsModel,
|
|
273
|
+
) -> InvoiceWithPaymentLinkMessageModel | None:
|
|
274
|
+
invoice_data = None
|
|
275
|
+
if payment_type.type == PaymentTypes.life_pay.value:
|
|
276
|
+
invoice_data = await self.get_life_pay_sbp_payment_data(
|
|
277
|
+
user=user, order=order, payment=payment_type, shop_details=shop_details
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
elif payment_type.type == PaymentTypes.yookassa.value:
|
|
281
|
+
invoice_data = await self.get_yookassa_payment_data(
|
|
282
|
+
user=user, order=order, payment=payment_type, shop_details=shop_details
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
elif payment_type.type == PaymentTypes.tkassa.value:
|
|
286
|
+
invoice_data = await self.get_tkassa_payment_data(
|
|
287
|
+
user=user, order=order, payment=payment_type, shop_details=shop_details
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
elif payment_type.type == PaymentTypes.cloud_payments.value:
|
|
291
|
+
invoice_data = await self.get_cloudpayments_data(
|
|
292
|
+
user=user, order=order, payment=payment_type, shop_details=shop_details
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
return invoice_data
|
shops_payment_processing-1.2.0.dev33607/shops_payment_processing/handlers/invoice_creation.pyi
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from _typeshed import Incomplete
|
|
2
|
+
from shops_payment_processing.logging_config import logger as logger
|
|
3
|
+
from shops_payment_processing.models.invoice import InvoiceWithPaymentLinkMessageModel as InvoiceWithPaymentLinkMessageModel
|
|
4
|
+
from shops_payment_processing.models.order import OrderResponseModel as OrderResponseModel, PaymentTypes as PaymentTypes
|
|
5
|
+
from shops_payment_processing.models.payment import PaymentMethodDBResponseModel as PaymentMethodDBResponseModel
|
|
6
|
+
from shops_payment_processing.models.shop import ShopDetailsModel as ShopDetailsModel
|
|
7
|
+
from shops_payment_processing.models.user import UserModel as UserModel
|
|
8
|
+
from shops_payment_processing.services.base import CreateInvoiceRequest as CreateInvoiceRequest
|
|
9
|
+
from shops_payment_processing.services.cloudpayments import CloudPaymentsAPI as CloudPaymentsAPI
|
|
10
|
+
from shops_payment_processing.services.life_pay import LifePayAPI as LifePayAPI
|
|
11
|
+
from shops_payment_processing.services.tkassa import TKassaAPI as TKassaAPI
|
|
12
|
+
from shops_payment_processing.services.yookassa import YooKassaAPI as YooKassaAPI
|
|
13
|
+
from shops_payment_processing.utils.link_generation import generate_web_app_order_link as generate_web_app_order_link
|
|
14
|
+
|
|
15
|
+
class InvoiceCreation:
|
|
16
|
+
BASE_REDIRECT_URL: Incomplete
|
|
17
|
+
BASE_CALLBACK_URL: Incomplete
|
|
18
|
+
def __init__(self, base_redirect_url: str, base_callback_url: str) -> None: ...
|
|
19
|
+
async def get_life_pay_sbp_payment_data(self, user: UserModel, order: OrderResponseModel, payment: PaymentMethodDBResponseModel, shop_details: ShopDetailsModel) -> InvoiceWithPaymentLinkMessageModel: ...
|
|
20
|
+
async def get_yookassa_payment_data(self, user: UserModel, order: OrderResponseModel, payment: PaymentMethodDBResponseModel, shop_details: ShopDetailsModel) -> InvoiceWithPaymentLinkMessageModel: ...
|
|
21
|
+
async def get_tkassa_payment_data(self, user: UserModel, order: OrderResponseModel, payment: PaymentMethodDBResponseModel, shop_details: ShopDetailsModel) -> InvoiceWithPaymentLinkMessageModel: ...
|
|
22
|
+
async def get_cloudpayments_data(self, user: UserModel, order: OrderResponseModel, payment: PaymentMethodDBResponseModel, shop_details: ShopDetailsModel) -> InvoiceWithPaymentLinkMessageModel: ...
|
|
23
|
+
async def get_invoice_data(self, payment_type: PaymentMethodDBResponseModel, user: UserModel, order: OrderResponseModel, shop_details: ShopDetailsModel) -> InvoiceWithPaymentLinkMessageModel | None: ...
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from pydantic import BaseModel
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class InvoiceBaseModel(BaseModel):
|
|
5
|
+
chat_id: int
|
|
6
|
+
order_id: str
|
|
7
|
+
order_number: str # order_number
|
|
8
|
+
payload: str # can be used for subscription for update
|
|
9
|
+
amount: float
|
|
10
|
+
currency: str
|
|
11
|
+
payment_address: str
|
|
12
|
+
payment_timeout: int | None = None
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class InvoiceWithPaymentLinkMessageModel(InvoiceBaseModel):
|
|
16
|
+
payment_link: str
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from pydantic import BaseModel
|
|
2
|
+
|
|
3
|
+
class InvoiceBaseModel(BaseModel):
|
|
4
|
+
chat_id: int
|
|
5
|
+
order_id: str
|
|
6
|
+
order_number: str
|
|
7
|
+
payload: str
|
|
8
|
+
amount: float
|
|
9
|
+
currency: str
|
|
10
|
+
payment_address: str
|
|
11
|
+
payment_timeout: int | None
|
|
12
|
+
|
|
13
|
+
class InvoiceWithPaymentLinkMessageModel(InvoiceBaseModel):
|
|
14
|
+
payment_link: str
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from typing import Annotated
|
|
3
|
+
|
|
4
|
+
from annotated_types import Ge
|
|
5
|
+
from pydantic import BaseModel, Field, field_validator
|
|
6
|
+
from pydantic_core.core_schema import ValidationInfo
|
|
7
|
+
from typing_extensions import Doc
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class PaymentTypes(str, Enum):
|
|
11
|
+
manual_payment_request = "ManualPaymentRequest"
|
|
12
|
+
external_card_payment_provider = "ExternalCardPaymentProvider"
|
|
13
|
+
crypto_ton = "CryptoTON"
|
|
14
|
+
xtr = "XTR"
|
|
15
|
+
life_pay = "LifePay"
|
|
16
|
+
yookassa = "yookassa"
|
|
17
|
+
tkassa = "tkassa"
|
|
18
|
+
cloud_payments = "CloudPayments"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class DBProductInBasketV2(BaseModel):
|
|
22
|
+
id: str # product ID
|
|
23
|
+
unique_id: str # unique product ID in the basket, used it for product deleting
|
|
24
|
+
extra_option_ids: list[str] = []
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class MetaBaseModel(BaseModel):
|
|
28
|
+
metadata: dict = Field(default_factory=dict, exclude=True)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class BaseProductResponseModel(MetaBaseModel):
|
|
32
|
+
id: str
|
|
33
|
+
name: str
|
|
34
|
+
description: str = ""
|
|
35
|
+
price: float
|
|
36
|
+
final_price: float = 0
|
|
37
|
+
currency: str
|
|
38
|
+
preview_url: list[str] = []
|
|
39
|
+
stock_qty: int
|
|
40
|
+
orders_qty: int = 0
|
|
41
|
+
|
|
42
|
+
@field_validator("final_price", mode="before")
|
|
43
|
+
def set_final_price(cls, value, values: ValidationInfo):
|
|
44
|
+
if not value:
|
|
45
|
+
return values.data.get("price")
|
|
46
|
+
return value
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class ProductsInBasket(BaseProductResponseModel):
|
|
50
|
+
count_in_basket: int = 1
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class UserBasketResponseModel(BaseModel):
|
|
54
|
+
id: str | None = None
|
|
55
|
+
user_id: str | None = None
|
|
56
|
+
order_id: str | None = None
|
|
57
|
+
products_id: list[DBProductInBasketV2] = []
|
|
58
|
+
coupon: str | None = None
|
|
59
|
+
coupon_discount: Annotated[
|
|
60
|
+
float, Doc("The amount of discount from attached coupon, if any.")
|
|
61
|
+
] = 0
|
|
62
|
+
amount: Annotated[float, Ge(0)] = 0 # this amount already includes discount
|
|
63
|
+
preview_url: str = ""
|
|
64
|
+
products: list[ProductsInBasket] = []
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class ExtraFieldPayload(BaseModel):
|
|
68
|
+
name: str
|
|
69
|
+
value: str
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class OrderDeliveryTypeModel(BaseModel):
|
|
73
|
+
name: str
|
|
74
|
+
address: str | None = ""
|
|
75
|
+
amount: float | None = 0.0
|
|
76
|
+
extra_fields_payload: list[ExtraFieldPayload] | None = []
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class OrderResponseModel(UserBasketResponseModel):
|
|
80
|
+
id: str
|
|
81
|
+
delivery: OrderDeliveryTypeModel
|
|
82
|
+
basket: UserBasketResponseModel
|
|
83
|
+
user_id: str | None = None
|
|
84
|
+
basket_id: str | None = None
|
|
85
|
+
status: str | None = None
|
|
86
|
+
order_number: str = "#0001"
|
|
87
|
+
process_key: int | None = None
|
|
88
|
+
coupon: str | None = None
|
|
89
|
+
user_contact_number: str | None = None
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from annotated_types import Ge as Ge
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
from pydantic_core.core_schema import ValidationInfo as ValidationInfo
|
|
5
|
+
from typing import Annotated
|
|
6
|
+
|
|
7
|
+
class PaymentTypes(str, Enum):
|
|
8
|
+
manual_payment_request = 'ManualPaymentRequest'
|
|
9
|
+
external_card_payment_provider = 'ExternalCardPaymentProvider'
|
|
10
|
+
crypto_ton = 'CryptoTON'
|
|
11
|
+
xtr = 'XTR'
|
|
12
|
+
life_pay = 'LifePay'
|
|
13
|
+
yookassa = 'yookassa'
|
|
14
|
+
tkassa = 'tkassa'
|
|
15
|
+
cloud_payments = 'CloudPayments'
|
|
16
|
+
|
|
17
|
+
class DBProductInBasketV2(BaseModel):
|
|
18
|
+
id: str
|
|
19
|
+
unique_id: str
|
|
20
|
+
extra_option_ids: list[str]
|
|
21
|
+
|
|
22
|
+
class MetaBaseModel(BaseModel):
|
|
23
|
+
metadata: dict
|
|
24
|
+
|
|
25
|
+
class BaseProductResponseModel(MetaBaseModel):
|
|
26
|
+
id: str
|
|
27
|
+
name: str
|
|
28
|
+
description: str
|
|
29
|
+
price: float
|
|
30
|
+
final_price: float
|
|
31
|
+
currency: str
|
|
32
|
+
preview_url: list[str]
|
|
33
|
+
stock_qty: int
|
|
34
|
+
orders_qty: int
|
|
35
|
+
def set_final_price(cls, value, values: ValidationInfo): ...
|
|
36
|
+
|
|
37
|
+
class ProductsInBasket(BaseProductResponseModel):
|
|
38
|
+
count_in_basket: int
|
|
39
|
+
|
|
40
|
+
class UserBasketResponseModel(BaseModel):
|
|
41
|
+
id: str | None
|
|
42
|
+
user_id: str | None
|
|
43
|
+
order_id: str | None
|
|
44
|
+
products_id: list[DBProductInBasketV2]
|
|
45
|
+
coupon: str | None
|
|
46
|
+
coupon_discount: Annotated[float, None]
|
|
47
|
+
amount: Annotated[float, None]
|
|
48
|
+
preview_url: str
|
|
49
|
+
products: list[ProductsInBasket]
|
|
50
|
+
|
|
51
|
+
class ExtraFieldPayload(BaseModel):
|
|
52
|
+
name: str
|
|
53
|
+
value: str
|
|
54
|
+
|
|
55
|
+
class OrderDeliveryTypeModel(BaseModel):
|
|
56
|
+
name: str
|
|
57
|
+
address: str | None
|
|
58
|
+
amount: float | None
|
|
59
|
+
extra_fields_payload: list[ExtraFieldPayload] | None
|
|
60
|
+
|
|
61
|
+
class OrderResponseModel(UserBasketResponseModel):
|
|
62
|
+
id: str
|
|
63
|
+
delivery: OrderDeliveryTypeModel
|
|
64
|
+
basket: UserBasketResponseModel
|
|
65
|
+
user_id: str | None
|
|
66
|
+
basket_id: str | None
|
|
67
|
+
status: str | None
|
|
68
|
+
order_number: str
|
|
69
|
+
process_key: int | None
|
|
70
|
+
coupon: str | None
|
|
71
|
+
user_contact_number: str | None
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from pydantic import BaseModel
|
|
2
|
+
|
|
3
|
+
from shops_payment_processing.models.order import PaymentTypes
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Metadata(BaseModel):
|
|
7
|
+
key: str
|
|
8
|
+
value: list[str]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class PaymentMethodModel(BaseModel):
|
|
12
|
+
name: str
|
|
13
|
+
type: PaymentTypes
|
|
14
|
+
payment_data: str | None = None # payment_token, TON address etc....
|
|
15
|
+
meta: list[Metadata] | None = []
|
|
16
|
+
|
|
17
|
+
class Config:
|
|
18
|
+
use_enum_values = True
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class PaymentMethodDBResponseModel(PaymentMethodModel):
|
|
22
|
+
id: str
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from pydantic import BaseModel
|
|
2
|
+
from shops_payment_processing.models.order import PaymentTypes as PaymentTypes
|
|
3
|
+
|
|
4
|
+
class Metadata(BaseModel):
|
|
5
|
+
key: str
|
|
6
|
+
value: list[str]
|
|
7
|
+
|
|
8
|
+
class PaymentMethodModel(BaseModel):
|
|
9
|
+
name: str
|
|
10
|
+
type: PaymentTypes
|
|
11
|
+
payment_data: str | None
|
|
12
|
+
meta: list[Metadata] | None
|
|
13
|
+
class Config:
|
|
14
|
+
use_enum_values: bool
|
|
15
|
+
|
|
16
|
+
class PaymentMethodDBResponseModel(PaymentMethodModel):
|
|
17
|
+
id: str
|
|
File without changes
|
|
File without changes
|