Repository: fossasia/open-event-server

Issue:
Configure charging the user through Paypal
##### Is your feature request related to a problem? Please describe.

Configure the Ticketing Manager to charge the user using Paypal.

##### Additional context

parent: #4852


Discussion:

Resolving patch:
diff --git a/app/api/helpers/payment.py b/app/api/helpers/payment.py
--- a/app/api/helpers/payment.py
+++ b/app/api/helpers/payment.py
@@ -1,22 +1,19 @@
+import json
+
 import requests
-import sqlalchemy
 import stripe
-import urllib.parse
-import json
 from flask import current_app
 from forex_python.converter import CurrencyRates
-from urllib.parse import urlencode
 
 from app.api.helpers.cache import cache
-from app.api.helpers.db import safe_query, save_to_db
-from app.api.helpers.files import make_frontend_url
+from app.api.helpers.db import safe_query
 from app.api.helpers.utilities import represents_int
 from app.models import db
-from app.models.order import Order
+from app.models.event import Event
 from app.models.stripe_authorization import StripeAuthorization
-from app.models.ticket_fee import TicketFees
 from app.settings import get_settings
 
+
 @cache.memoize(5)
 def forex(from_currency, to_currency, amount):
     try:
@@ -123,6 +120,13 @@ class PayPalPaymentsManager(object):
 
     @staticmethod
     def get_credentials(event=None, override_mode=False, is_testing=False):
+        """
+        Get Paypal credentials from settings module.
+        :param event: Associated event.
+        :param override_mode:
+        :param is_testing:
+        :return:
+        """
         if event and represents_int(event):
             event = safe_query(db, Event, 'id', event, 'event_id')
         settings = get_settings()
@@ -159,63 +163,20 @@ def get_credentials(event=None, override_mode=False, is_testing=False):
         else:
             return None
 
-    @staticmethod
-    def get_checkout_url(order, currency=None, credentials=None):
-        if not credentials:
-            credentials = PayPalPaymentsManager.get_credentials(order.event)
-
-        if not credentials:
-            raise Exception('PayPal credentials have not be set correctly')
-
-        if current_app.config['TESTING']:
-            return credentials['CHECKOUT_URL']
-
-        currency = order.event.payment_currency if not currency and order.event.payment_currency != "" else "USD"
-        data = {
-            'USER': credentials['USER'],
-            'PWD': credentials['PWD'],
-            'SIGNATURE': credentials['SIGNATURE'],
-            'SUBJECT': credentials['EMAIL'],
-
-            'METHOD': 'SetExpressCheckout',
-            'VERSION': PayPalPaymentsManager.api_version,
-            'PAYMENTREQUEST_0_PAYMENTACTION': 'SALE',
-            'PAYMENTREQUEST_0_AMT': order.amount,
-            'PAYMENTREQUEST_0_CURRENCYCODE': currency,
-            'RETURNURL': make_frontend_url(path='/orders/{identifier}/payment/success'.
-                                           format(identifier=order.identifier)),
-            'CANCELURL': make_frontend_url(path='/orders/{identifier}/payment/cancelled'.
-                                           format(identifier=order.identifier))
-        }
-
-        count = 1
-
-        if type(order) is Order:
-            for ticket_order in order.order_tickets:
-                data['L_PAYMENTREQUEST_' + str(count) + '_NAMEm'] = ticket_order.ticket.name
-                data['L_PAYMENTREQUEST_' + str(count) + '_QTYm'] = ticket_order.quantity
-                data['L_PAYMENTREQUEST_' + str(count) + '_AMTm'] = ticket_order.ticket.price
-                count += 1
-
-        response = requests.post(credentials['SERVER'], data=data)
-        if 'TOKEN' not in dict(urllib.parse.parse_qsl(response.text)):
-            raise Exception('PayPal Token could not be retrieved')
-        token = dict(urllib.parse.parse_qsl(response.text))['TOKEN']
-        order.paypal_token = token
-        save_to_db(order)
-        return credentials['CHECKOUT_URL'] + "?" + urlencode({
-            'cmd': '_express-checkout',
-            'token': token
-        })
-
     @staticmethod
     def get_approved_payment_details(order, credentials=None):
+        """
+        Use the paypal token to get the approved payment details from Paypal
+        :param order: Order to charge for
+        :param credentials: the paypal credentials
+        :return: payer_id and other details returned by Paypal.
+        """
 
         if not credentials:
             credentials = PayPalPaymentsManager.get_credentials(order.event)
 
         if not credentials:
-            raise Exception('PayPal credentials have not be set correctly')
+            raise Exception('PayPal credentials have not been set correctly')
 
         data = {
             'USER': credentials['USER'],
@@ -231,10 +192,18 @@ def get_approved_payment_details(order, credentials=None):
             return data
 
         response = requests.post(credentials['SERVER'], data=data)
-        return dict(urllib.parse.parse_qsl(response.text))
+        return json.loads(response.text)
 
     @staticmethod
     def capture_payment(order, payer_id, currency=None, credentials=None):
+        """
+        capture order payment using payer_id and token.
+        :param order: Order to charge for.
+        :param payer_id: The payer_id obtained from Paypal using the get_approved_payment_details method.
+        :param currency: currency
+        :param credentials: the paypal credentials.
+        :return:
+        """
         if not credentials:
             credentials = PayPalPaymentsManager.get_credentials(order.event)
 
@@ -262,4 +231,4 @@ def capture_payment(order, payer_id, currency=None, credentials=None):
         }
 
         response = requests.post(credentials['SERVER'], data=data)
-        return dict(urllib.parse.parse_qsl(response.text))
+        return json.loads(response.text)
diff --git a/app/api/helpers/ticketing.py b/app/api/helpers/ticketing.py
--- a/app/api/helpers/ticketing.py
+++ b/app/api/helpers/ticketing.py
@@ -1,7 +1,11 @@
 from datetime import datetime
 
+from flask_login import current_user
+
 from app.api.helpers.db import save_to_db, get_count
 from app.api.helpers.files import make_frontend_url
+from app.api.helpers.mail import send_email_to_attendees
+from app.api.helpers.notification import send_notif_to_attendees, send_notif_ticket_purchase_organizer
 from app.api.helpers.payment import StripePaymentsManager, PayPalPaymentsManager
 from app.models import db
 from app.models.ticket_fee import TicketFees
@@ -132,14 +136,13 @@ def charge_paypal_order_payment(order, token_id):
                 order.completed_at = datetime.utcnow()
                 save_to_db(order)
 
-                invoice_id = order.get_invoice_number()
-                order_url = make_frontend_url(path="/{identifier}/view/".format(identifier=order.identifier))
+                # send email and notifications
+                send_email_to_attendees(order, current_user.id)
+                send_notif_to_attendees(order, current_user.id)
 
-                # trigger_after_purchase_notifications(order.user.email, order.event_id, order.event, invoice_id,
-                # order_url)
-                # send_email_for_after_purchase(order.user.email, invoice_id, order_url, order.event.name,
-                # order.event.organizer_name)
-                # send_notif_for_after_purchase(order.user, invoice_id, order_url)
+                order_url = make_frontend_url(path='/orders/{identifier}'.format(identifier=order.identifier))
+                for organizer in order.event.organizers:
+                    send_notif_ticket_purchase_organizer(organizer, order.invoice_number, order_url, order.event.name)
 
                 return True, order
             else:
diff --git a/app/api/schema/orders.py b/app/api/schema/orders.py
--- a/app/api/schema/orders.py
+++ b/app/api/schema/orders.py
@@ -21,13 +21,15 @@ class Meta:
 
     @post_dump
     def generate_payment_url(self, data):
+        """
+        generate payment url for an order
+        :param data:
+        :return:
+        """
         if 'POST' in request.method or ('GET' in request.method and 'regenerate' in request.args) and 'completed' != \
            data["status"]:
             if data['payment_mode'] == 'stripe':
                 data['payment_url'] = 'stripe://payment'
-            elif data['payment_mode'] == 'paypal':
-                order = Order.query.filter_by(id=data['id']).first()
-                data['payment_url'] = PayPalPaymentsManager.get_checkout_url(order)
         return data
 
     @validates_schema


Test patch: