diff --git a/config/settings/base.py b/config/settings/base.py index ab31e8e..bbe2b4e 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -25,6 +25,7 @@ INSTALLED_APPS = [ 'rest_framework', 'rest_framework_simplejwt', 'corsheaders', + 'payme', # apps 'core.apps.accounts', 'core.apps.orders', @@ -147,4 +148,13 @@ ALLOWED_ATMOS_IPS = [] CONSUMER_KEY = env.str('CONSUMER_KEY') CONSUMER_SECRET = env.str('CONSUMER_SECRET') STORE_ID = env.str('STORE_ID') -API_KEY = env.str('API_KEY') \ No newline at end of file +API_KEY = env.str('API_KEY') + + +PAYME_ID = env.str('PAYME_ID') +PAYME_KEY = env.str('PAYME_KEY') +PAYME_ACCOUNT_FIELD = "order_number" +PAYME_AMOUNT_FIELD = "total_price" +PAYME_ACCOUNT_MODEL = "core.apps.orders.models.Order" +PAYME_ONE_TIME_PAYMENT = True +PAYME_ACCOUNT_FIELD_TYPE = "int" diff --git a/config/urls.py b/config/urls.py index 3756ecd..bd3afc9 100644 --- a/config/urls.py +++ b/config/urls.py @@ -8,6 +8,8 @@ from rest_framework.permissions import IsAdminUser from drf_yasg.views import get_schema_view from drf_yasg import openapi +from core.apps.payment.views import PaymeCallBackAPIView + schema_view = get_schema_view( openapi.Info( @@ -34,7 +36,8 @@ urlpatterns = [ path('orders/', include('core.apps.orders.urls')), path('payment/', include('core.apps.payment.urls')), ] - )) + )), + path('payment/update/', PaymeCallBackAPIView.as_view()), ] diff --git a/core/apps/common/views.py b/core/apps/common/views.py index dfcb677..a33395c 100644 --- a/core/apps/common/views.py +++ b/core/apps/common/views.py @@ -2,7 +2,6 @@ from rest_framework import generics from rest_framework.response import Response from core.apps.common import models, serializers -from core.apps.payment.views import get_client_ip class SiteConfigApiView(generics.GenericAPIView): diff --git a/core/apps/payment/serializers.py b/core/apps/payment/serializers.py index 28b7fa6..c7b1dda 100644 --- a/core/apps/payment/serializers.py +++ b/core/apps/payment/serializers.py @@ -20,4 +20,13 @@ class VisaPaymentSerializer(serializers.Serializer): def validate_order_number(self, value): if not Order.objects.filter(order_number=value).exists(): raise serializers.ValidationError("Order not found") - return value \ No newline at end of file + return value + + +class PaymeSerializer(serializers.Serializer): + order_id = serializers.UUIDField() + + def validate_order_id(self, value): + if not Order.objects.filter(id=value).exists(): + raise serializers.ValidationError("order not found") + return value diff --git a/core/apps/payment/urls.py b/core/apps/payment/urls.py index ca556d6..c6d4343 100644 --- a/core/apps/payment/urls.py +++ b/core/apps/payment/urls.py @@ -1,9 +1,10 @@ from django.urls import path -from .views import AtmosCallbackApiView, PaymentGenerateLinkApiView, VisaMastercardPaymentApiView +from .views import AtmosCallbackApiView, PaymentGenerateLinkApiView, VisaMastercardPaymentApiView, PayPaymeApiView urlpatterns = [ path('callback/', AtmosCallbackApiView.as_view()), path('payment/', PaymentGenerateLinkApiView.as_view()), path('visa_mastercard/payment/', VisaMastercardPaymentApiView.as_view()), + path('payme/', PayPaymeApiView.as_view()), ] \ No newline at end of file diff --git a/core/apps/payment/views.py b/core/apps/payment/views.py index d66f787..058cfe4 100644 --- a/core/apps/payment/views.py +++ b/core/apps/payment/views.py @@ -1,6 +1,9 @@ import hashlib import uuid +from payme.views import PaymeWebHookAPIView, PaymeTransactions +from payme import Payme + from django.conf import settings from rest_framework.generics import GenericAPIView @@ -9,17 +12,18 @@ from rest_framework.response import Response from rest_framework import status, permissions from core.apps.orders.models import Order -from core.apps.payment.serializers import PaymentSerializer, VisaPaymentSerializer +from core.apps.payment.serializers import PaymentSerializer, VisaPaymentSerializer, PaymeSerializer from core.services.payment import Atmos +payme = Payme(settings.PAYME_ID) -def get_client_ip(request): - x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR") - if x_forwarded_for: - ip = x_forwarded_for.split(",")[0] - else: - ip = request.META.get("REMOTE_ADDR") - return ip +# def get_client_ip(request): +# x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR") +# if x_forwarded_for: +# ip = x_forwarded_for.split(",")[0] +# else: +# ip = request.META.get("REMOTE_ADDR") +# return ip class AtmosCallbackApiView(APIView): @@ -27,7 +31,7 @@ class AtmosCallbackApiView(APIView): permission_classes = [] def post(self, request): - client_ip = get_client_ip(request) + # client_ip = get_client_ip(request) # if client_ip not in settings.ALLOWED_ATMOS_IPS: # return Response({"status": 0, "message": "IP ruxsat etilmagan"}, status=403) data = request.data @@ -104,4 +108,58 @@ class VisaMastercardPaymentApiView(GenericAPIView): request_id=str(uuid.uuid4()), amount=data.get('amount'), ) - return Response({'success': True, 'link': res}) \ No newline at end of file + return Response({'success': True, 'link': res}) + + + + +class PaymeCallBackAPIView(PaymeWebHookAPIView): + def handle_created_payment(self, params, result, *args, **kwargs): + """ + Handle the successful payment. You can override this method + """ + print(f"Transaction created for this params: {params} and cr_result: {result}") + + def handle_successfully_payment(self, params, result, *args, **kwargs): + """ + Handle the successful payment. You can override this method + """ + transaction = PaymeTransactions.get_by_transaction_id( + transaction_id=params['id'] + ) + order = Order.objects.get(id=transaction.id) + order.is_paid = True + order.save() + print(f"Transaction successfully performed for this params: {params} and performed_result: {result}") + + def handle_cancelled_payment(self, params, result, *args, **kwargs): + """ + Handle the cancelled payment. You can override this method + """ + transaction = PaymeTransactions.get_by_transaction_id( + transaction_id=params['id'] + ) + if transaction.state == PaymeTransactions.CANCELED: + order = Order.objects.get(id=transaction.id) + order.is_paid = False + order.save() + print(f"Transaction cancelled for this params: {params} and cancelled_result: {result}") + + +class PayPaymeApiView(GenericAPIView): + serializer_class = PaymeSerializer + permission_classes = [permissions.IsAuthenticated] + queryset = Order.objects.all() + + def post(self, request): + serializer = self.serializer_class(data=request.data) + if serializer.is_valid(): + order_id = serializer.validated_data.get('order_id') + order = Order.objects.get(id=order_id) + payment_link = payme.initializer.generate_pay_link( + id=order_id, + amount=order.total_price * 100, + return_url="https://wisdom.uz", + ) + + return Response({'success': True, 'link': payment_link}, status=200) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 67b5f2e..427514f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -37,4 +37,5 @@ uritemplate==4.2.0 uvicorn==0.35.0 vine==5.1.0 wcwidth==0.2.13 -requests \ No newline at end of file +requests +payme-pkg \ No newline at end of file