Commit d8c07ce2 by Clinton Blackburn

Extracted Apple Pay view and processor code out of CyberSource module

The Apple Pay code is not all CyberSource-specific. Extracting it allows it to be used with other payment processors.
parent c3615196
...@@ -5,6 +5,7 @@ from collections import namedtuple ...@@ -5,6 +5,7 @@ from collections import namedtuple
import waffle import waffle
from django.conf import settings from django.conf import settings
from django.utils.functional import cached_property
from oscar.core.loading import get_model from oscar.core.loading import get_model
PaymentProcessorResponse = get_model('payment', 'PaymentProcessorResponse') PaymentProcessorResponse = get_model('payment', 'PaymentProcessorResponse')
...@@ -146,3 +147,15 @@ class BaseClientSidePaymentProcessor(BasePaymentProcessor): # pylint: disable=a ...@@ -146,3 +147,15 @@ class BaseClientSidePaymentProcessor(BasePaymentProcessor): # pylint: disable=a
str str
""" """
return 'payment/{}.html'.format(self.NAME) return 'payment/{}.html'.format(self.NAME)
class ApplePayMixin(object):
@cached_property
def apple_pay_merchant_id_domain_association(self):
""" Returns the Apple Pay merchant domain association contents that will be served at
/.well-known/apple-developer-merchantid-domain-association.
Returns:
str
"""
return (self.configuration.get('apple_pay_merchant_id_domain_association') or '').strip()
...@@ -29,7 +29,11 @@ from ecommerce.extensions.payment.exceptions import ( ...@@ -29,7 +29,11 @@ from ecommerce.extensions.payment.exceptions import (
ProcessorMisconfiguredError ProcessorMisconfiguredError
) )
from ecommerce.extensions.payment.helpers import sign from ecommerce.extensions.payment.helpers import sign
from ecommerce.extensions.payment.processors import BaseClientSidePaymentProcessor, HandledProcessorResponse from ecommerce.extensions.payment.processors import (
ApplePayMixin,
BaseClientSidePaymentProcessor,
HandledProcessorResponse
)
from ecommerce.extensions.payment.utils import clean_field_value from ecommerce.extensions.payment.utils import clean_field_value
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -37,13 +41,14 @@ logger = logging.getLogger(__name__) ...@@ -37,13 +41,14 @@ logger = logging.getLogger(__name__)
OrderNumberGenerator = get_class('order.utils', 'OrderNumberGenerator') OrderNumberGenerator = get_class('order.utils', 'OrderNumberGenerator')
class Cybersource(BaseClientSidePaymentProcessor): class Cybersource(ApplePayMixin, BaseClientSidePaymentProcessor):
""" """
CyberSource Secure Acceptance Web/Mobile (February 2015) CyberSource Secure Acceptance Web/Mobile (February 2015)
For reference, see For reference, see
http://apps.cybersource.com/library/documentation/dev_guides/Secure_Acceptance_WM/Secure_Acceptance_WM.pdf. http://apps.cybersource.com/library/documentation/dev_guides/Secure_Acceptance_WM/Secure_Acceptance_WM.pdf.
""" """
NAME = 'cybersource' NAME = 'cybersource'
PCI_FIELDS = ('card_cvn', 'card_expiry_date', 'card_number', 'card_type',) PCI_FIELDS = ('card_cvn', 'card_expiry_date', 'card_number', 'card_type',)
...@@ -87,8 +92,6 @@ class Cybersource(BaseClientSidePaymentProcessor): ...@@ -87,8 +92,6 @@ class Cybersource(BaseClientSidePaymentProcessor):
# Apple Pay configuration # Apple Pay configuration
self.apple_pay_enabled = self.site.siteconfiguration.enable_apple_pay self.apple_pay_enabled = self.site.siteconfiguration.enable_apple_pay
self.apple_pay_merchant_identifier = configuration.get('apple_pay_merchant_identifier', '') self.apple_pay_merchant_identifier = configuration.get('apple_pay_merchant_identifier', '')
self.apple_pay_merchant_id_domain_association = configuration.get(
'apple_pay_merchant_id_domain_association', '').strip()
self.apple_pay_merchant_id_certificate_path = configuration.get('apple_pay_merchant_id_certificate_path', '') self.apple_pay_merchant_id_certificate_path = configuration.get('apple_pay_merchant_id_certificate_path', '')
self.apple_pay_country_code = configuration.get('apple_pay_country_code', '') self.apple_pay_country_code = configuration.get('apple_pay_country_code', '')
......
from django.conf import settings
from django.test import override_settings
from django.urls import reverse
from ecommerce.extensions.payment.tests.views.test_cybersource import LoginMixin
from ecommerce.tests.testcases import TestCase
class ApplePayMerchantDomainAssociationViewTests(LoginMixin, TestCase):
url = reverse('apple_pay_domain_association')
def setUp(self):
super(ApplePayMerchantDomainAssociationViewTests, self).setUp()
self.site_configuration.client_side_payment_processor = 'cybersource'
self.site_configuration.save()
def set_apple_pay_merchant_id_domain_association(self, value):
key = 'apple_pay_merchant_id_domain_association'
partner_code = self.site_configuration.partner.short_code
processor_name = self.site_configuration.client_side_payment_processor
settings.PAYMENT_PROCESSOR_CONFIG[partner_code][processor_name][key] = value
def assert_response_matches(self, response, expected_status_code, expected_content):
self.assertEqual(response.status_code, expected_status_code)
self.assertEqual(response.content, expected_content)
self.assertEqual(response['Content-Type'], 'text/plain')
@override_settings()
def test_get(self):
""" The view should return the the merchant domain association verification data. """
expected = 'fake-value'
self.set_apple_pay_merchant_id_domain_association(expected)
response = self.client.get(self.url)
self.assert_response_matches(response, 200, expected)
@override_settings()
def test_get_with_configuration_error(self):
""" The view should return HTTP 501 if Apple Pay is not properly configured. """
self.set_apple_pay_merchant_id_domain_association(None)
response = self.client.get(self.url)
content = 'Apple Pay is not configured for [{}].'.format(self.site.domain)
self.assert_response_matches(response, 501, content)
...@@ -254,29 +254,6 @@ class CybersourceInterstitialViewTests(CybersourceNotificationTestsMixin, TestCa ...@@ -254,29 +254,6 @@ class CybersourceInterstitialViewTests(CybersourceNotificationTestsMixin, TestCa
self.assertRedirects(response, self.get_full_url(path=reverse('payment_error')), status_code=302) self.assertRedirects(response, self.get_full_url(path=reverse('payment_error')), status_code=302)
class ApplePayMerchantDomainAssociationViewTests(LoginMixin, TestCase):
url = reverse('apple_pay_domain_association')
def assert_response_matches(self, response, expected_status_code, expected_content):
self.assertEqual(response.status_code, expected_status_code)
self.assertEqual(response.content, expected_content)
self.assertEqual(response['Content-Type'], 'text/plain')
def test_get(self):
""" The view should return the the merchant domain association verification data. """
response = self.client.get(self.url)
self.assert_response_matches(response, 200, settings.PAYMENT_PROCESSOR_CONFIG['edx']['cybersource'][
'apple_pay_merchant_id_domain_association'])
def test_get_with_configuration_error(self):
""" The view should return HTTP 501 if Apple Pay is not properly configured. """
settings.PAYMENT_PROCESSOR_CONFIG['edx']['cybersource'][
'apple_pay_merchant_id_domain_association'] = ''
response = self.client.get(self.url)
content = 'Apple Pay is not configured for [{}].'.format(self.site.domain)
self.assert_response_matches(response, 501, content)
@ddt.ddt @ddt.ddt
class ApplePayStartSessionViewTests(LoginMixin, TestCase): class ApplePayStartSessionViewTests(LoginMixin, TestCase):
url = reverse('cybersource:apple_pay:start_session') url = reverse('cybersource:apple_pay:start_session')
......
import logging
from django.http import HttpResponse
from django.views import View
logger = logging.getLogger(__name__)
class ApplePayMerchantDomainAssociationView(View):
def get(self, request, *args, **kwargs): # pylint: disable=unused-argument
site_configuration = self.request.site.siteconfiguration
payment_processor_class = site_configuration.get_client_side_payment_processor_class()
payment_processor = payment_processor_class(self.request.site)
content = payment_processor.apple_pay_merchant_id_domain_association
status_code = 200
if not content:
content = 'Apple Pay is not configured for [{}].'.format(request.site.domain)
# 501 Not Implemented -- https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.2
status_code = 501
logger.warning(content)
return HttpResponse(content, content_type='text/plain', status=status_code)
...@@ -10,7 +10,7 @@ from django.contrib.auth.decorators import login_required ...@@ -10,7 +10,7 @@ from django.contrib.auth.decorators import login_required
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.db import transaction from django.db import transaction
from django.http import HttpResponse, JsonResponse from django.http import JsonResponse
from django.shortcuts import redirect from django.shortcuts import redirect
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.utils.functional import cached_property from django.utils.functional import cached_property
...@@ -390,20 +390,6 @@ class CybersourceInterstitialView(CybersourceNotificationMixin, View): ...@@ -390,20 +390,6 @@ class CybersourceInterstitialView(CybersourceNotificationMixin, View):
return redirect(receipt_page_url) return redirect(receipt_page_url)
class ApplePayMerchantDomainAssociationView(CyberSourceProcessorMixin, View):
def get(self, request, *args, **kwargs): # pylint: disable=unused-argument
content = self.payment_processor.apple_pay_merchant_id_domain_association
status_code = 200
if not content:
content = 'Apple Pay is not configured for [{}].'.format(request.site.domain)
# 501 Not Implemented -- https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.2
status_code = 501
logger.warning(content)
return HttpResponse(content, content_type='text/plain', status=status_code)
class ApplePayStartSessionView(CyberSourceProcessorMixin, APIView): class ApplePayStartSessionView(CyberSourceProcessorMixin, APIView):
permission_classes = (permissions.IsAuthenticated,) permission_classes = (permissions.IsAuthenticated,)
......
...@@ -15,7 +15,7 @@ from rest_framework.documentation import include_docs_urls ...@@ -15,7 +15,7 @@ from rest_framework.documentation import include_docs_urls
from ecommerce.core import views as core_views from ecommerce.core import views as core_views
from ecommerce.core.url_utils import get_lms_dashboard_url from ecommerce.core.url_utils import get_lms_dashboard_url
from ecommerce.core.views import LogoutView from ecommerce.core.views import LogoutView
from ecommerce.extensions.payment.views.cybersource import ApplePayMerchantDomainAssociationView from ecommerce.extensions.payment.views.apple_pay import ApplePayMerchantDomainAssociationView
from ecommerce.extensions.urls import urlpatterns as extensions_patterns from ecommerce.extensions.urls import urlpatterns as extensions_patterns
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment