import os import sys import json import logging from paypalcheckoutsdk.core import PayPalHttpClient, SandboxEnvironment, LiveEnvironment from paypalcheckoutsdk.orders import OrdersCreateRequest, OrdersCaptureRequest from paypalhttp.serializers.json_serializer import Json from django.conf import settings from django.contrib.sites.models import Site from django.urls import reverse_lazy logger = logging.getLogger(__name__) class PayPalClient: def __init__(self): self.client_id = settings.PAYPAL_CLIENT_ID self.client_secret = settings.PAYPAL_SECRET_ID self.site_domain = Site.objects.get_current().domain self.site_name = Site.objects.get_current().name """Setting up and Returns PayPal SDK environment with PayPal Access credentials. For demo purpose, we are using SandboxEnvironment. In production this will be LiveEnvironment.""" if settings.PAYPAL_ENVIRONMENT == 'LIVE': self.environment = LiveEnvironment(client_id=self.client_id, client_secret=self.client_secret) else: self.environment = SandboxEnvironment(client_id=self.client_id, client_secret=self.client_secret) """ Returns PayPal HTTP client instance with environment which has access credentials context. This can be used invoke PayPal API's provided the credentials have the access to do so. """ self.client = PayPalHttpClient(self.environment) def object_to_json(self, json_data): """ Function to print all json data in an organized readable manner """ result = {} if sys.version_info[0] < 3: itr = json_data.__dict__.iteritems() else: itr = json_data.__dict__.items() for key,value in itr: # Skip internal attributes. if key.startswith("__") or key.startswith("_"): continue result[key] = self.array_to_json_array(value) if isinstance(value, list) else\ self.object_to_json(value) if not self.is_primittive(value) else\ value return result def array_to_json_array(self, json_array): result =[] if isinstance(json_array, list): for item in json_array: result.append(self.object_to_json(item) if not self.is_primittive(item) \ else self.array_to_json_array(item) if isinstance(item, list) else item) return result def is_primittive(self, data): return isinstance(data, str) or isinstance(data, unicode) or isinstance(data, int) class CreateOrder(PayPalClient): """Setting up the JSON request body for creating the Order. The Intent in the request body should be set as "CAPTURE" for capture intent flow.""" def build_request_body(self, params): """Method to create body with CAPTURE intent""" processed_items = [{ # Shows within upper-right dropdown during payment approval 'name': f'{item["product"]}', # Item details will also be in the completed paypal.com transaction view 'description': 'Coffee', 'unit_amount': { 'currency_code': 'USD', 'value': f'{item["price"]}' }, 'quantity': f'{item["quantity"]}' } for item in params['items']] request_body = { "intent": "CAPTURE", "application_context": { "return_url": f"https://{self.site_domain}{reverse_lazy('storefront:payment-done')}", "cancel_url": f"https://{self.site_domain}{reverse_lazy('storefront:payment-canceled')}", "brand_name": f"{self.site_name}", # "landing_page": "BILLING", # "shipping_preference": "SET_PROVIDED_ADDRESS", # "user_action": "CONTINUE" }, "purchase_units": [ { # "reference_id": "PUHF", "description": "Coffee", # "custom_id": "CUST-HighFashions", # "soft_descriptor": "HighFashions", "amount": { "currency_code": "USD", "value": params['total_price'], "breakdown": { "item_total": { "currency_code": "USD", "value": params['item_total'] }, "shipping": { "currency_code": "USD", "value": params['shipping_price'] }, "tax_total": { "currency_code": "USD", "value": params['tax_total'] }, "discount": { "currency_code": "USD", "value": params['discount'] } } }, "items": processed_items, "shipping": { "method": params['shipping_method'], "address": params['shipping_address'] } } ] } logger.info(f'\nRequest body: {request_body}\n') return request_body """ This is the sample function which can be sued to create an order. It uses the JSON body returned by buildRequestBody() to create an new Order.""" def create_order(self, params, debug=False): request = OrdersCreateRequest() request.headers['prefer'] = 'return=representation' request.request_body(self.build_request_body(params)) response = self.client.execute(request) if debug: logger.info(f'\nStatus Code: {response.status_code}', ) logger.info(f'\nStatus: {response.result.status}', ) logger.info(f'\nOrder ID: {response.result.id}', ) logger.info(f'\nIntent: {response.result.intent}', ) logger.info(f"\njson_data: {response.result}") return response class CaptureOrder(PayPalClient): """this is the sample function performing payment capture on the order. Approved Order id should be passed as an argument to this function""" def capture_order(self, order_id, debug=False): """Method to capture order using order_id""" request = OrdersCaptureRequest(order_id) response = self.client.execute(request) data = response.result.__dict__['_dict'] data['redirect_urls'] = { 'return_url': f"https://{self.site_domain}{reverse_lazy('storefront:payment-done')}", 'cancel_url': f"https://{self.site_domain}{reverse_lazy('storefront:payment-canceled')}" } return data