Migrating to Postgres from sqlite3
This commit is contained in:
+137
-77
@@ -1,10 +1,12 @@
|
||||
import uuid
|
||||
import logging
|
||||
from yookassa import Configuration, Payment
|
||||
from yookassa.domain.request.payment_request_builder import PaymentRequestBuilder
|
||||
|
||||
import asyncio
|
||||
from typing import Optional, Dict, Any, List
|
||||
|
||||
from yookassa import Configuration, Payment as YooKassaPayment
|
||||
from yookassa.domain.request.payment_request_builder import PaymentRequestBuilder
|
||||
from yookassa.domain.common.confirmation_type import ConfirmationType
|
||||
|
||||
from config.settings import Settings
|
||||
|
||||
|
||||
@@ -14,61 +16,84 @@ class YooKassaService:
|
||||
shop_id: Optional[str],
|
||||
secret_key: Optional[str],
|
||||
configured_return_url: Optional[str],
|
||||
bot_username_for_default: Optional[str] = None,
|
||||
bot_username_for_default_return: Optional[str] = None,
|
||||
settings_obj: Optional[Settings] = None):
|
||||
|
||||
self.settings = settings_obj
|
||||
|
||||
if not shop_id or not secret_key:
|
||||
logging.warning(
|
||||
"YooKassa SHOP_ID or SECRET_KEY not configured. Payment functionality will be disabled."
|
||||
)
|
||||
"YooKassa SHOP_ID or SECRET_KEY not configured in settings. "
|
||||
"Payment functionality will be DISABLED.")
|
||||
self.configured = False
|
||||
else:
|
||||
Configuration.account_id = shop_id
|
||||
Configuration.secret_key = secret_key
|
||||
self.configured = True
|
||||
logging.info(f"YooKassa configured for shop_id: {shop_id}")
|
||||
try:
|
||||
Configuration.configure(shop_id, secret_key)
|
||||
self.configured = True
|
||||
logging.info(
|
||||
f"YooKassa SDK configured for shop_id: {shop_id[:5]}...")
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to configure YooKassa SDK: {e}",
|
||||
exc_info=True)
|
||||
self.configured = False
|
||||
|
||||
if configured_return_url:
|
||||
self.return_url = configured_return_url
|
||||
elif bot_username_for_default:
|
||||
self.return_url = f"https://t.me/{bot_username_for_default}"
|
||||
elif bot_username_for_default_return:
|
||||
self.return_url = f"https://t.me/{bot_username_for_default_return}"
|
||||
logging.info(
|
||||
f"YOOKASSA_RETURN_URL not set, using dynamic default: {self.return_url}"
|
||||
f"YOOKASSA_RETURN_URL not set, using dynamic default based on bot username: {self.return_url}"
|
||||
)
|
||||
else:
|
||||
self.return_url = "https://example.com/payment_error_no_return_url"
|
||||
self.return_url = "https://example.com/payment_error_no_return_url_configured"
|
||||
logging.warning(
|
||||
f"YOOKASSA_RETURN_URL not set AND bot username not provided. Using placeholder: {self.return_url}"
|
||||
f"CRITICAL: YOOKASSA_RETURN_URL not set AND bot username not provided. "
|
||||
f"Using placeholder: {self.return_url}. Payments may not complete correctly."
|
||||
)
|
||||
logging.info(
|
||||
f"YooKassa Service effective return_url: {self.return_url}")
|
||||
f"YooKassa Service effective return_url for payments: {self.return_url}"
|
||||
)
|
||||
|
||||
async def create_payment(
|
||||
self, amount: float, currency: str, description: str,
|
||||
metadata: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
||||
self,
|
||||
amount: float,
|
||||
currency: str,
|
||||
description: str,
|
||||
metadata: Dict[str, Any],
|
||||
receipt_email: Optional[str] = None,
|
||||
receipt_phone: Optional[str] = None) -> Optional[Dict[str, Any]]:
|
||||
if not self.configured:
|
||||
logging.error("YooKassa is not configured. Cannot create payment.")
|
||||
return None
|
||||
|
||||
if not self.settings:
|
||||
logging.error(
|
||||
"YooKassaService: Settings object not available for receipt creation."
|
||||
)
|
||||
return {
|
||||
"error": True,
|
||||
"internal_message": "Service settings not initialized."
|
||||
}
|
||||
|
||||
if not self.settings.YOOKASSA_DEFAULT_RECEIPT_EMAIL:
|
||||
logging.error(
|
||||
"CRITICAL: YOOKASSA_DEFAULT_RECEIPT_EMAIL is not configured. YooKassa payment will fail due to missing receipt customer contact."
|
||||
"YooKassaService: Settings object not available. Cannot create payment with receipt details."
|
||||
)
|
||||
return {
|
||||
"error":
|
||||
True,
|
||||
"internal_message":
|
||||
"YooKassa receipt email not configured by admin."
|
||||
"Service settings (Settings object) not initialized."
|
||||
}
|
||||
|
||||
customer_contact_for_receipt = {}
|
||||
if receipt_email:
|
||||
customer_contact_for_receipt["email"] = receipt_email
|
||||
elif receipt_phone:
|
||||
customer_contact_for_receipt["phone"] = receipt_phone
|
||||
elif self.settings.YOOKASSA_DEFAULT_RECEIPT_EMAIL:
|
||||
customer_contact_for_receipt[
|
||||
"email"] = self.settings.YOOKASSA_DEFAULT_RECEIPT_EMAIL
|
||||
else:
|
||||
logging.error(
|
||||
"CRITICAL: No email/phone for YooKassa receipt provided and YOOKASSA_DEFAULT_RECEIPT_EMAIL is not set."
|
||||
)
|
||||
return {
|
||||
"error":
|
||||
True,
|
||||
"internal_message":
|
||||
"YooKassa receipt customer contact (email/phone) missing and no default email configured."
|
||||
}
|
||||
|
||||
try:
|
||||
@@ -79,15 +104,15 @@ class YooKassaService:
|
||||
})
|
||||
builder.set_capture(True)
|
||||
builder.set_confirmation({
|
||||
"type": "redirect",
|
||||
"type": ConfirmationType.REDIRECT,
|
||||
"return_url": self.return_url
|
||||
})
|
||||
builder.set_description(description)
|
||||
builder.set_metadata(metadata)
|
||||
|
||||
receipt_items: List[Dict[str, Any]] = [{
|
||||
receipt_items_list: List[Dict[str, Any]] = [{
|
||||
"description":
|
||||
description,
|
||||
description[:128],
|
||||
"quantity":
|
||||
"1.00",
|
||||
"amount": {
|
||||
@@ -102,87 +127,122 @@ class YooKassaService:
|
||||
self.settings.YOOKASSA_PAYMENT_SUBJECT
|
||||
}]
|
||||
|
||||
receipt_customer: Dict[str, str] = {}
|
||||
if self.settings.YOOKASSA_DEFAULT_RECEIPT_EMAIL:
|
||||
receipt_customer[
|
||||
"email"] = self.settings.YOOKASSA_DEFAULT_RECEIPT_EMAIL
|
||||
|
||||
if not receipt_customer:
|
||||
logging.error(
|
||||
"YooKassa: No customer contact (email/phone) for receipt.")
|
||||
return {
|
||||
"error": True,
|
||||
"internal_message": "Receipt customer contact missing."
|
||||
}
|
||||
|
||||
receipt_payload: Dict[str, Any] = {
|
||||
"customer": receipt_customer,
|
||||
"items": receipt_items
|
||||
receipt_data_dict: Dict[str, Any] = {
|
||||
"customer": customer_contact_for_receipt,
|
||||
"items": receipt_items_list
|
||||
}
|
||||
|
||||
builder.set_receipt(receipt_payload)
|
||||
builder.set_receipt(receipt_data_dict)
|
||||
|
||||
idempotence_key = str(uuid.uuid4())
|
||||
payment_request = builder.build()
|
||||
|
||||
logging.info(
|
||||
f"Creating YooKassa payment (IDK: {idempotence_key}) with receipt. Email: {self.settings.YOOKASSA_DEFAULT_RECEIPT_EMAIL}"
|
||||
f"Creating YooKassa payment (Idempotence-Key: {idempotence_key}). "
|
||||
f"Amount: {amount} {currency}. Metadata: {metadata}. Receipt: {receipt_data_dict}"
|
||||
)
|
||||
|
||||
res = Payment.create(payment_request, idempotence_key)
|
||||
loop = asyncio.get_event_loop()
|
||||
response = await loop.run_in_executor(
|
||||
None, lambda: YooKassaPayment.create(payment_request,
|
||||
idempotence_key))
|
||||
|
||||
logging.info(
|
||||
f"YooKassa Payment.create response: ID={res.id}, Status={res.status}"
|
||||
f"YooKassa Payment.create response: ID={response.id}, Status={response.status}, Paid={response.paid}"
|
||||
)
|
||||
|
||||
return {
|
||||
"id":
|
||||
res.id,
|
||||
response.id,
|
||||
"confirmation_url":
|
||||
res.confirmation.confirmation_url
|
||||
if res.confirmation else None,
|
||||
response.confirmation.confirmation_url
|
||||
if response.confirmation else None,
|
||||
"status":
|
||||
res.status,
|
||||
response.status,
|
||||
"metadata":
|
||||
res.metadata,
|
||||
response.metadata,
|
||||
"amount_value":
|
||||
float(res.amount.value),
|
||||
float(response.amount.value),
|
||||
"amount_currency":
|
||||
res.amount.currency,
|
||||
"idempotence_key":
|
||||
response.amount.currency,
|
||||
"idempotence_key_used":
|
||||
idempotence_key,
|
||||
"paid":
|
||||
res.paid,
|
||||
response.paid,
|
||||
"refundable":
|
||||
res.refundable,
|
||||
response.refundable,
|
||||
"created_at":
|
||||
res.created_at.isoformat() if hasattr(
|
||||
res.created_at, 'isoformat') else str(res.created_at)
|
||||
response.created_at.isoformat() if hasattr(
|
||||
response.created_at, 'isoformat') else str(
|
||||
response.created_at),
|
||||
"description_from_yk":
|
||||
response.description,
|
||||
"test_mode":
|
||||
response.test if hasattr(response, 'test') else None
|
||||
}
|
||||
except Exception as e:
|
||||
logging.error(f"YooKassa payment creation failed: {e}",
|
||||
exc_info=True)
|
||||
return None
|
||||
|
||||
async def get_payment_info(self,
|
||||
payment_id: str) -> Optional[Dict[str, Any]]:
|
||||
async def get_payment_info(
|
||||
self, payment_id_in_yookassa: str) -> Optional[Dict[str, Any]]:
|
||||
if not self.configured:
|
||||
logging.error(
|
||||
"YooKassa is not configured. Cannot get payment info.")
|
||||
return None
|
||||
try:
|
||||
payment_info = Payment.find_one(payment_id)
|
||||
if payment_info:
|
||||
logging.info(
|
||||
f"Fetching payment info from YooKassa for ID: {payment_id_in_yookassa}"
|
||||
)
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
payment_info_yk = await loop.run_in_executor(
|
||||
None, lambda: YooKassaPayment.find_one(payment_id_in_yookassa))
|
||||
|
||||
if payment_info_yk:
|
||||
logging.info(
|
||||
f"YooKassa payment info for {payment_id_in_yookassa}: Status={payment_info_yk.status}, Paid={payment_info_yk.paid}"
|
||||
)
|
||||
return {
|
||||
"id": payment_info.id,
|
||||
"status": payment_info.status,
|
||||
"paid": payment_info.paid,
|
||||
"amount_value": float(payment_info.amount.value),
|
||||
"amount_currency": payment_info.amount.currency,
|
||||
"metadata": payment_info.metadata,
|
||||
"description": payment_info.description,
|
||||
"id":
|
||||
payment_info_yk.id,
|
||||
"status":
|
||||
payment_info_yk.status,
|
||||
"paid":
|
||||
payment_info_yk.paid,
|
||||
"amount_value":
|
||||
float(payment_info_yk.amount.value),
|
||||
"amount_currency":
|
||||
payment_info_yk.amount.currency,
|
||||
"metadata":
|
||||
payment_info_yk.metadata,
|
||||
"description":
|
||||
payment_info_yk.description,
|
||||
"refundable":
|
||||
payment_info_yk.refundable,
|
||||
"created_at":
|
||||
payment_info_yk.created_at.isoformat() if hasattr(
|
||||
payment_info_yk.created_at, 'isoformat') else str(
|
||||
payment_info_yk.created_at),
|
||||
"captured_at":
|
||||
payment_info_yk.captured_at.isoformat()
|
||||
if payment_info_yk.captured_at and hasattr(
|
||||
payment_info_yk.captured_at, 'isoformat') else None,
|
||||
"payment_method_type":
|
||||
payment_info_yk.payment_method.type
|
||||
if payment_info_yk.payment_method else None,
|
||||
"test_mode":
|
||||
payment_info_yk.test
|
||||
if hasattr(payment_info_yk, 'test') else None
|
||||
}
|
||||
return None
|
||||
else:
|
||||
logging.warning(
|
||||
f"No payment info found in YooKassa for ID: {payment_id_in_yookassa}"
|
||||
)
|
||||
return None
|
||||
except Exception as e:
|
||||
logging.error(
|
||||
f"YooKassa get payment info for {payment_id} failed: {e}")
|
||||
f"YooKassa get payment info for {payment_id_in_yookassa} failed: {e}",
|
||||
exc_info=True)
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user