Commit 4fd1bd64 by Joyce Zhu Committed by GitHub

Merge pull request #869 from edx/JoyceZhu/cancel_endpoint

[SOL-1679] New cancel checkout endpoint
parents 819bfaa3 a159f682
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0016_siteconfiguration_enable_enrollment_codes'),
]
operations = [
migrations.AddField(
model_name='siteconfiguration',
name='payment_support_email',
field=models.CharField(default=b'support@example.com', help_text='Contact email for payment support issues.', max_length=255, verbose_name='Payment support email', blank=True),
),
]
...@@ -80,6 +80,13 @@ class SiteConfiguration(models.Model): ...@@ -80,6 +80,13 @@ class SiteConfiguration(models.Model):
blank=True, blank=True,
default=False default=False
) )
payment_support_email = models.CharField(
verbose_name=_('Payment support email'),
help_text=_('Contact email for payment support issues.'),
max_length=255,
blank=True,
default="support@example.com"
)
class Meta(object): class Meta(object):
unique_together = ('site', 'partner') unique_together = ('site', 'partner')
......
...@@ -5,10 +5,12 @@ from oscar.core.loading import get_class ...@@ -5,10 +5,12 @@ from oscar.core.loading import get_class
class CheckoutApplication(app.CheckoutApplication): class CheckoutApplication(app.CheckoutApplication):
free_checkout = get_class('checkout.views', 'FreeCheckoutView') free_checkout = get_class('checkout.views', 'FreeCheckoutView')
cancel_checkout = get_class('checkout.views', 'CancelCheckoutView')
def get_urls(self): def get_urls(self):
urls = [ urls = [
url(r'^free-checkout/$', self.free_checkout.as_view(), name='free-checkout'), url(r'^free-checkout/$', self.free_checkout.as_view(), name='free-checkout'),
url(r'^cancel-checkout/$', self.cancel_checkout.as_view(), name='cancel-checkout'),
url(r'^$', self.index_view.as_view(), name='index'), url(r'^$', self.index_view.as_view(), name='index'),
......
...@@ -54,3 +54,23 @@ class FreeCheckoutViewTests(TestCase): ...@@ -54,3 +54,23 @@ class FreeCheckoutViewTests(TestCase):
expected_url = '{}?orderNum={}'.format(receipt_page, Order.objects.first().number) expected_url = '{}?orderNum={}'.format(receipt_page, Order.objects.first().number)
self.assertRedirects(response, expected_url, fetch_redirect_response=False) self.assertRedirects(response, expected_url, fetch_redirect_response=False)
class CancelCheckoutViewTests(TestCase):
""" CancelCheckoutView view tests. """
path = reverse('checkout:cancel-checkout')
def setUp(self):
super(CancelCheckoutViewTests, self).setUp()
self.user = self.create_user()
self.client.login(username=self.user.username, password=self.password)
@httpretty.activate
def test_payment_support_email_in_context(self):
""" Verify that the view returns a payment support email in its context. """
response = self.client.get(self.path)
self.assertEqual(response.status_code, 200)
self.assertEqual(
response.context['payment_support_email'], self.request.site.siteconfiguration.payment_support_email
)
...@@ -8,7 +8,7 @@ from django.conf import settings ...@@ -8,7 +8,7 @@ from django.conf import settings
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.views.generic import RedirectView from django.views.generic import RedirectView, TemplateView
from oscar.apps.checkout.views import * # pylint: disable=wildcard-import, unused-wildcard-import from oscar.apps.checkout.views import * # pylint: disable=wildcard-import, unused-wildcard-import
from oscar.core.loading import get_class, get_model from oscar.core.loading import get_class, get_model
...@@ -52,3 +52,16 @@ class FreeCheckoutView(EdxOrderPlacementMixin, RedirectView): ...@@ -52,3 +52,16 @@ class FreeCheckoutView(EdxOrderPlacementMixin, RedirectView):
# page which displays the appropriate message for empty baskets. # page which displays the appropriate message for empty baskets.
url = reverse('basket:summary') url = reverse('basket:summary')
return url return url
class CancelCheckoutView(TemplateView):
""" Displays a cancellation message when the customer cancels checkout on the payment processor page. """
template_name = 'checkout/cancel_checkout.html'
def get_context_data(self, **kwargs):
context = super(CancelCheckoutView, self).get_context_data(**kwargs)
context.update({
'payment_support_email': self.request.site.siteconfiguration.payment_support_email,
})
return context
...@@ -14,7 +14,7 @@ from suds.sudsobject import asdict ...@@ -14,7 +14,7 @@ from suds.sudsobject import asdict
from suds.wsse import Security, UsernameToken from suds.wsse import Security, UsernameToken
from threadlocals.threadlocals import get_current_request from threadlocals.threadlocals import get_current_request
from ecommerce.core.url_utils import get_lms_url from ecommerce.core.url_utils import get_ecommerce_url, get_lms_url
from ecommerce.core.constants import ISO_8601_FORMAT from ecommerce.core.constants import ISO_8601_FORMAT
from ecommerce.extensions.order.constants import PaymentEventTypeName from ecommerce.extensions.order.constants import PaymentEventTypeName
from ecommerce.extensions.payment.constants import CYBERSOURCE_CARD_TYPE_MAP from ecommerce.extensions.payment.constants import CYBERSOURCE_CARD_TYPE_MAP
...@@ -69,7 +69,7 @@ class Cybersource(BasePaymentProcessor): ...@@ -69,7 +69,7 @@ class Cybersource(BasePaymentProcessor):
@property @property
def cancel_page_url(self): def cancel_page_url(self):
return get_lms_url(self.configuration['cancel_path']) return get_ecommerce_url(self.configuration['cancel_checkout_path'])
def get_transaction_parameters(self, basket, request=None): def get_transaction_parameters(self, basket, request=None):
""" """
......
...@@ -65,7 +65,7 @@ class Paypal(BasePaymentProcessor): ...@@ -65,7 +65,7 @@ class Paypal(BasePaymentProcessor):
@property @property
def cancel_url(self): def cancel_url(self):
return get_lms_url(self.configuration['cancel_path']) return get_ecommerce_url(self.configuration['cancel_checkout_path'])
@property @property
def error_url(self): def error_url(self):
......
...@@ -105,7 +105,7 @@ PAYMENT_PROCESSORS = ( ...@@ -105,7 +105,7 @@ PAYMENT_PROCESSORS = (
) )
PAYMENT_PROCESSOR_RECEIPT_PATH = '/commerce/checkout/receipt/' PAYMENT_PROCESSOR_RECEIPT_PATH = '/commerce/checkout/receipt/'
PAYMENT_PROCESSOR_CANCEL_PATH = '/commerce/checkout/cancel/' PAYMENT_PROCESSOR_CANCEL_PATH = '/checkout/cancel-checkout/'
PAYMENT_PROCESSOR_ERROR_PATH = '/commerce/checkout/error/' PAYMENT_PROCESSOR_ERROR_PATH = '/commerce/checkout/error/'
PAYMENT_PROCESSOR_CONFIG = { PAYMENT_PROCESSOR_CONFIG = {
...@@ -116,7 +116,7 @@ PAYMENT_PROCESSOR_CONFIG = { ...@@ -116,7 +116,7 @@ PAYMENT_PROCESSOR_CONFIG = {
'secret_key': None, 'secret_key': None,
'payment_page_url': None, 'payment_page_url': None,
'receipt_path': PAYMENT_PROCESSOR_RECEIPT_PATH, 'receipt_path': PAYMENT_PROCESSOR_RECEIPT_PATH,
'cancel_path': PAYMENT_PROCESSOR_CANCEL_PATH, 'cancel_checkout_path': PAYMENT_PROCESSOR_CANCEL_PATH,
'send_level_2_3_details': True, 'send_level_2_3_details': True,
}, },
'paypal': { 'paypal': {
...@@ -125,7 +125,7 @@ PAYMENT_PROCESSOR_CONFIG = { ...@@ -125,7 +125,7 @@ PAYMENT_PROCESSOR_CONFIG = {
'client_id': None, 'client_id': None,
'client_secret': None, 'client_secret': None,
'receipt_path': PAYMENT_PROCESSOR_RECEIPT_PATH, 'receipt_path': PAYMENT_PROCESSOR_RECEIPT_PATH,
'cancel_path': PAYMENT_PROCESSOR_CANCEL_PATH, 'cancel_checkout_path': PAYMENT_PROCESSOR_CANCEL_PATH,
'error_path': PAYMENT_PROCESSOR_ERROR_PATH, 'error_path': PAYMENT_PROCESSOR_ERROR_PATH,
}, },
}, },
......
...@@ -31,7 +31,7 @@ PAYMENT_PROCESSOR_CONFIG = { ...@@ -31,7 +31,7 @@ PAYMENT_PROCESSOR_CONFIG = {
'secret_key': 'fake-secret-key', 'secret_key': 'fake-secret-key',
'payment_page_url': 'https://testsecureacceptance.cybersource.com/pay', 'payment_page_url': 'https://testsecureacceptance.cybersource.com/pay',
'receipt_path': PAYMENT_PROCESSOR_RECEIPT_PATH, 'receipt_path': PAYMENT_PROCESSOR_RECEIPT_PATH,
'cancel_path': PAYMENT_PROCESSOR_CANCEL_PATH, 'cancel_checkout_path': PAYMENT_PROCESSOR_CANCEL_PATH,
'send_level_2_3_details': True, 'send_level_2_3_details': True,
}, },
'paypal': { 'paypal': {
...@@ -39,7 +39,7 @@ PAYMENT_PROCESSOR_CONFIG = { ...@@ -39,7 +39,7 @@ PAYMENT_PROCESSOR_CONFIG = {
'client_id': 'fake-client-id', 'client_id': 'fake-client-id',
'client_secret': 'fake-client-secret', 'client_secret': 'fake-client-secret',
'receipt_path': PAYMENT_PROCESSOR_RECEIPT_PATH, 'receipt_path': PAYMENT_PROCESSOR_RECEIPT_PATH,
'cancel_path': PAYMENT_PROCESSOR_CANCEL_PATH, 'cancel_checkout_path': PAYMENT_PROCESSOR_CANCEL_PATH,
'error_path': PAYMENT_PROCESSOR_ERROR_PATH, 'error_path': PAYMENT_PROCESSOR_ERROR_PATH,
}, },
}, },
......
...@@ -91,7 +91,7 @@ PAYMENT_PROCESSOR_CONFIG = { ...@@ -91,7 +91,7 @@ PAYMENT_PROCESSOR_CONFIG = {
'secret_key': 'fake-secret-key', 'secret_key': 'fake-secret-key',
'payment_page_url': 'https://testsecureacceptance.cybersource.com/pay', 'payment_page_url': 'https://testsecureacceptance.cybersource.com/pay',
'receipt_path': PAYMENT_PROCESSOR_RECEIPT_PATH, 'receipt_path': PAYMENT_PROCESSOR_RECEIPT_PATH,
'cancel_path': PAYMENT_PROCESSOR_CANCEL_PATH, 'cancel_checkout_path': PAYMENT_PROCESSOR_CANCEL_PATH,
'send_level_2_3_details': True, 'send_level_2_3_details': True,
}, },
'paypal': { 'paypal': {
...@@ -99,7 +99,7 @@ PAYMENT_PROCESSOR_CONFIG = { ...@@ -99,7 +99,7 @@ PAYMENT_PROCESSOR_CONFIG = {
'client_id': 'fake-client-id', 'client_id': 'fake-client-id',
'client_secret': 'fake-client-secret', 'client_secret': 'fake-client-secret',
'receipt_path': PAYMENT_PROCESSOR_RECEIPT_PATH, 'receipt_path': PAYMENT_PROCESSOR_RECEIPT_PATH,
'cancel_path': PAYMENT_PROCESSOR_CANCEL_PATH, 'cancel_checkout_path': PAYMENT_PROCESSOR_CANCEL_PATH,
'error_path': PAYMENT_PROCESSOR_ERROR_PATH, 'error_path': PAYMENT_PROCESSOR_ERROR_PATH,
}, },
}, },
......
...@@ -76,7 +76,7 @@ for __, configs in PAYMENT_PROCESSOR_CONFIG.iteritems(): ...@@ -76,7 +76,7 @@ for __, configs in PAYMENT_PROCESSOR_CONFIG.iteritems():
for __, config in configs.iteritems(): for __, config in configs.iteritems():
config.update({ config.update({
'receipt_path': PAYMENT_PROCESSOR_RECEIPT_PATH, 'receipt_path': PAYMENT_PROCESSOR_RECEIPT_PATH,
'cancel_path': PAYMENT_PROCESSOR_CANCEL_PATH, 'cancel_checkout_path': PAYMENT_PROCESSOR_CANCEL_PATH,
'error_path': PAYMENT_PROCESSOR_ERROR_PATH, 'error_path': PAYMENT_PROCESSOR_ERROR_PATH,
}) })
# END PAYMENT PROCESSOR OVERRIDES # END PAYMENT PROCESSOR OVERRIDES
...@@ -78,7 +78,7 @@ PAYMENT_PROCESSOR_CONFIG = { ...@@ -78,7 +78,7 @@ PAYMENT_PROCESSOR_CONFIG = {
'secret_key': 'fake-secret-key', 'secret_key': 'fake-secret-key',
'payment_page_url': 'https://replace-me/', 'payment_page_url': 'https://replace-me/',
'receipt_path': PAYMENT_PROCESSOR_RECEIPT_PATH, 'receipt_path': PAYMENT_PROCESSOR_RECEIPT_PATH,
'cancel_path': PAYMENT_PROCESSOR_CANCEL_PATH, 'cancel_checkout_path': PAYMENT_PROCESSOR_CANCEL_PATH,
'send_level_2_3_details': True, 'send_level_2_3_details': True,
}, },
'paypal': { 'paypal': {
...@@ -86,7 +86,7 @@ PAYMENT_PROCESSOR_CONFIG = { ...@@ -86,7 +86,7 @@ PAYMENT_PROCESSOR_CONFIG = {
'client_id': 'fake-client-id', 'client_id': 'fake-client-id',
'client_secret': 'fake-client-secret', 'client_secret': 'fake-client-secret',
'receipt_path': PAYMENT_PROCESSOR_RECEIPT_PATH, 'receipt_path': PAYMENT_PROCESSOR_RECEIPT_PATH,
'cancel_path': PAYMENT_PROCESSOR_CANCEL_PATH, 'cancel_checkout_path': PAYMENT_PROCESSOR_CANCEL_PATH,
'error_path': PAYMENT_PROCESSOR_ERROR_PATH, 'error_path': PAYMENT_PROCESSOR_ERROR_PATH,
}, },
'invoice': {} 'invoice': {}
...@@ -101,7 +101,7 @@ PAYMENT_PROCESSOR_CONFIG = { ...@@ -101,7 +101,7 @@ PAYMENT_PROCESSOR_CONFIG = {
'secret_key': 'other-fake-secret-key', 'secret_key': 'other-fake-secret-key',
'payment_page_url': 'https://replace-me/', 'payment_page_url': 'https://replace-me/',
'receipt_path': PAYMENT_PROCESSOR_RECEIPT_PATH, 'receipt_path': PAYMENT_PROCESSOR_RECEIPT_PATH,
'cancel_path': PAYMENT_PROCESSOR_CANCEL_PATH, 'cancel_checkout_path': PAYMENT_PROCESSOR_CANCEL_PATH,
'send_level_2_3_details': True, 'send_level_2_3_details': True,
}, },
'paypal': { 'paypal': {
...@@ -109,7 +109,7 @@ PAYMENT_PROCESSOR_CONFIG = { ...@@ -109,7 +109,7 @@ PAYMENT_PROCESSOR_CONFIG = {
'client_id': 'pther-fake-client-id', 'client_id': 'pther-fake-client-id',
'client_secret': 'pther-fake-client-secret', 'client_secret': 'pther-fake-client-secret',
'receipt_path': PAYMENT_PROCESSOR_RECEIPT_PATH, 'receipt_path': PAYMENT_PROCESSOR_RECEIPT_PATH,
'cancel_path': PAYMENT_PROCESSOR_CANCEL_PATH, 'cancel_checkout_path': PAYMENT_PROCESSOR_CANCEL_PATH,
'error_path': PAYMENT_PROCESSOR_ERROR_PATH, 'error_path': PAYMENT_PROCESSOR_ERROR_PATH,
}, },
'invoice': {} 'invoice': {}
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
// views // views
// -------------------- // --------------------
@import 'views/basket'; @import 'views/basket';
@import 'views/cancel_error';
@import 'views/credit'; @import 'views/credit';
@import 'views/course_admin'; @import 'views/course_admin';
@import 'views/coupon_admin'; @import 'views/coupon_admin';
......
.receipt-cancel-error {
font-family: $font-family-sans-serif;
h1 {
font-weight: 600;
margin-bottom: 30px;
text-align: center;
}
.nav-link {
border-bottom: none;
text-decoration: none !important;
&:active, &:hover, &:focus {
border-bottom: 1px dotted #0079bc;
}
}
p {
font-size: large;
}
}
\ No newline at end of file
{% extends 'edx/base.html' %}
{% load i18n %}
{% load staticfiles %}
{% block title %}
{% trans "Checkout Cancelled" %}
{% endblock title %}
{% block navbar %}
{% include 'edx/partials/_student_navbar.html' %}
{% endblock navbar %}
{% block content %}
<div class="container content-wrapper receipt-cancel-error">
<h1>{% trans "Checkout Cancelled" %}</h1>
<p> {% with "<a class='nav-link' href='mailto:"|add:payment_support_email|add:"'>"|safe as start_link %}
{% blocktrans with end_link="</a>"|safe %}
Your transaction has been cancelled. If you feel an error has occurred, contact {{ start_link }}
{{ payment_support_email }}{{ end_link }}.
{% endblocktrans %}
{% endwith %}
</p>
</div>
{% endblock content %}
\ No newline at end of file
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