Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
e1dfdc08
Commit
e1dfdc08
authored
Jun 10, 2015
by
jsa
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Receipt page now also handles Cybersource payment failures.
XCOM-398
parent
1b3efaba
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
188 additions
and
40 deletions
+188
-40
lms/djangoapps/commerce/tests/test_views.py
+60
-7
lms/djangoapps/commerce/views.py
+40
-3
lms/djangoapps/shoppingcart/processors/CyberSource2.py
+75
-22
lms/static/js/commerce/views/receipt_view.js
+2
-1
lms/templates/commerce/checkout_receipt.html
+11
-7
No files found.
lms/djangoapps/commerce/tests/test_views.py
View file @
e1dfdc08
...
@@ -4,10 +4,11 @@ import json
...
@@ -4,10 +4,11 @@ import json
from
uuid
import
uuid4
from
uuid
import
uuid4
from
nose.plugins.attrib
import
attr
from
nose.plugins.attrib
import
attr
from
ddt
import
ddt
,
data
import
ddt
from
django.core.urlresolvers
import
reverse
from
django.core.urlresolvers
import
reverse
from
django.test
import
TestCase
from
django.test
import
TestCase
from
django.test.utils
import
override_settings
from
django.test.utils
import
override_settings
import
mock
from
xmodule.modulestore.tests.django_utils
import
ModuleStoreTestCase
from
xmodule.modulestore.tests.django_utils
import
ModuleStoreTestCase
from
xmodule.modulestore.tests.factories
import
CourseFactory
from
xmodule.modulestore.tests.factories
import
CourseFactory
...
@@ -35,7 +36,7 @@ class UserMixin(object):
...
@@ -35,7 +36,7 @@ class UserMixin(object):
@attr
(
'shard_1'
)
@attr
(
'shard_1'
)
@ddt
@ddt
.ddt
@override_settings
(
ECOMMERCE_API_URL
=
TEST_API_URL
,
ECOMMERCE_API_SIGNING_KEY
=
TEST_API_SIGNING_KEY
)
@override_settings
(
ECOMMERCE_API_URL
=
TEST_API_URL
,
ECOMMERCE_API_SIGNING_KEY
=
TEST_API_SIGNING_KEY
)
class
BasketsViewTests
(
EnrollmentEventTestMixin
,
UserMixin
,
ModuleStoreTestCase
):
class
BasketsViewTests
(
EnrollmentEventTestMixin
,
UserMixin
,
ModuleStoreTestCase
):
"""
"""
...
@@ -102,7 +103,7 @@ class BasketsViewTests(EnrollmentEventTestMixin, UserMixin, ModuleStoreTestCase)
...
@@ -102,7 +103,7 @@ class BasketsViewTests(EnrollmentEventTestMixin, UserMixin, ModuleStoreTestCase)
self
.
client
.
logout
()
self
.
client
.
logout
()
self
.
assertEqual
(
403
,
self
.
_post_to_view
()
.
status_code
)
self
.
assertEqual
(
403
,
self
.
_post_to_view
()
.
status_code
)
@data
(
'delete'
,
'get'
,
'put'
)
@d
dt.d
ata
(
'delete'
,
'get'
,
'put'
)
def
test_post_required
(
self
,
method
):
def
test_post_required
(
self
,
method
):
"""
"""
Verify that the view only responds to POST operations.
Verify that the view only responds to POST operations.
...
@@ -157,7 +158,7 @@ class BasketsViewTests(EnrollmentEventTestMixin, UserMixin, ModuleStoreTestCase)
...
@@ -157,7 +158,7 @@ class BasketsViewTests(EnrollmentEventTestMixin, UserMixin, ModuleStoreTestCase)
else
:
else
:
self
.
assertResponsePaymentData
(
response
)
self
.
assertResponsePaymentData
(
response
)
@data
(
True
,
False
)
@d
dt.d
ata
(
True
,
False
)
def
test_course_with_honor_seat_sku
(
self
,
user_is_active
):
def
test_course_with_honor_seat_sku
(
self
,
user_is_active
):
"""
"""
If the course has a SKU, the view should get authorization from the E-Commerce API before enrolling
If the course has a SKU, the view should get authorization from the E-Commerce API before enrolling
...
@@ -172,7 +173,7 @@ class BasketsViewTests(EnrollmentEventTestMixin, UserMixin, ModuleStoreTestCase)
...
@@ -172,7 +173,7 @@ class BasketsViewTests(EnrollmentEventTestMixin, UserMixin, ModuleStoreTestCase)
with
mock_create_basket
(
response
=
return_value
):
with
mock_create_basket
(
response
=
return_value
):
self
.
_test_successful_ecommerce_api_call
()
self
.
_test_successful_ecommerce_api_call
()
@data
(
True
,
False
)
@d
dt.d
ata
(
True
,
False
)
def
test_course_with_paid_seat_sku
(
self
,
user_is_active
):
def
test_course_with_paid_seat_sku
(
self
,
user_is_active
):
"""
"""
If the course has a SKU, the view should return data that the client
If the course has a SKU, the view should return data that the client
...
@@ -341,11 +342,63 @@ class BasketOrderViewTests(UserMixin, TestCase):
...
@@ -341,11 +342,63 @@ class BasketOrderViewTests(UserMixin, TestCase):
@attr
(
'shard_1'
)
@attr
(
'shard_1'
)
class
ReceiptViewTests
(
TestCase
):
@ddt.ddt
class
ReceiptViewTests
(
UserMixin
,
TestCase
):
""" Tests for the receipt view. """
""" Tests for the receipt view. """
def
test_login_required
(
self
):
def
test_login_required
(
self
):
""" The view should redirect to the login page if the user is not logged in. """
""" The view should redirect to the login page if the user is not logged in. """
self
.
client
.
logout
()
self
.
client
.
logout
()
response
=
self
.
client
.
ge
t
(
reverse
(
'commerce:checkout_receipt'
))
response
=
self
.
client
.
pos
t
(
reverse
(
'commerce:checkout_receipt'
))
self
.
assertEqual
(
response
.
status_code
,
302
)
self
.
assertEqual
(
response
.
status_code
,
302
)
def
post_to_receipt_page
(
self
,
post_data
):
""" DRY helper """
response
=
self
.
client
.
post
(
reverse
(
'commerce:checkout_receipt'
),
params
=
{
'basket_id'
:
1
},
data
=
post_data
)
self
.
assertEqual
(
response
.
status_code
,
200
)
return
response
@ddt.data
(
'decision'
,
'reason_code'
,
'signed_field_names'
,
None
)
def
test_is_cybersource
(
self
,
post_key
):
"""
Ensure the view uses three specific POST keys to detect a request initiated by Cybersource.
"""
self
.
_login
()
post_data
=
{
'decision'
:
'REJECT'
,
'reason_code'
:
'200'
,
'signed_field_names'
:
'dummy'
}
if
post_key
is
not
None
:
# a key will be missing; we will not expect the receipt page to handle a cybersource decision
del
post_data
[
post_key
]
expected_pattern
=
r"<title>(\s+)Receipt"
else
:
expected_pattern
=
r"<title>(\s+)Payment Failed"
response
=
self
.
post_to_receipt_page
(
post_data
)
self
.
assertRegexpMatches
(
response
.
content
,
expected_pattern
)
@ddt.data
(
'ACCEPT'
,
'REJECT'
,
'ERROR'
)
def
test_cybersource_decision
(
self
,
decision
):
"""
Ensure the view renders a page appropriately depending on the Cybersource decision.
"""
self
.
_login
()
post_data
=
{
'decision'
:
decision
,
'reason_code'
:
'200'
,
'signed_field_names'
:
'dummy'
}
expected_pattern
=
r"<title>(\s+)Receipt"
if
decision
==
'ACCEPT'
else
r"<title>(\s+)Payment Failed"
response
=
self
.
post_to_receipt_page
(
post_data
)
self
.
assertRegexpMatches
(
response
.
content
,
expected_pattern
)
@ddt.data
(
True
,
False
)
@mock.patch
(
'commerce.views.is_user_payment_error'
)
def
test_cybersource_message
(
self
,
is_user_message_expected
,
mock_is_user_payment_error
):
"""
Ensure that the page displays the right message for the reason_code (it
may be a user error message or a system error message).
"""
mock_is_user_payment_error
.
return_value
=
is_user_message_expected
self
.
_login
()
response
=
self
.
post_to_receipt_page
({
'decision'
:
'REJECT'
,
'reason_code'
:
'99'
,
'signed_field_names'
:
'dummy'
})
self
.
assertTrue
(
mock_is_user_payment_error
.
called
)
self
.
assertTrue
(
mock_is_user_payment_error
.
call_args
[
0
][
0
],
'99'
)
user_message
=
"There was a problem with this transaction"
system_message
=
"A system error occurred while processing your payment"
self
.
assertRegexpMatches
(
response
.
content
,
user_message
if
is_user_message_expected
else
system_message
)
self
.
assertNotRegexpMatches
(
response
.
content
,
user_message
if
not
is_user_message_expected
else
system_message
)
lms/djangoapps/commerce/views.py
View file @
e1dfdc08
...
@@ -3,7 +3,6 @@ import logging
...
@@ -3,7 +3,6 @@ import logging
from
django.conf
import
settings
from
django.conf
import
settings
from
django.contrib.auth.decorators
import
login_required
from
django.contrib.auth.decorators
import
login_required
from
django.views.decorators.cache
import
cache_page
from
django.views.decorators.csrf
import
csrf_exempt
from
django.views.decorators.csrf
import
csrf_exempt
from
ecommerce_api_client
import
exceptions
from
ecommerce_api_client
import
exceptions
from
opaque_keys
import
InvalidKeyError
from
opaque_keys
import
InvalidKeyError
...
@@ -26,6 +25,8 @@ from student.models import CourseEnrollment
...
@@ -26,6 +25,8 @@ from student.models import CourseEnrollment
from
openedx.core.lib.api.authentication
import
SessionAuthenticationAllowInactiveUser
from
openedx.core.lib.api.authentication
import
SessionAuthenticationAllowInactiveUser
from
util.json_request
import
JsonResponse
from
util.json_request
import
JsonResponse
from
verify_student.models
import
SoftwareSecurePhotoVerification
from
verify_student.models
import
SoftwareSecurePhotoVerification
from
shoppingcart.processors.CyberSource2
import
is_user_payment_error
from
django.utils.translation
import
ugettext
as
_
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
...
@@ -137,7 +138,6 @@ class BasketsView(APIView):
...
@@ -137,7 +138,6 @@ class BasketsView(APIView):
@csrf_exempt
@csrf_exempt
@cache_page
(
1800
)
def
checkout_cancel
(
_request
):
def
checkout_cancel
(
_request
):
""" Checkout/payment cancellation view. """
""" Checkout/payment cancellation view. """
context
=
{
'payment_support_email'
:
microsite
.
get_value
(
'payment_support_email'
,
settings
.
PAYMENT_SUPPORT_EMAIL
)}
context
=
{
'payment_support_email'
:
microsite
.
get_value
(
'payment_support_email'
,
settings
.
PAYMENT_SUPPORT_EMAIL
)}
...
@@ -148,9 +148,46 @@ def checkout_cancel(_request):
...
@@ -148,9 +148,46 @@ def checkout_cancel(_request):
@login_required
@login_required
def
checkout_receipt
(
request
):
def
checkout_receipt
(
request
):
""" Receipt view. """
""" Receipt view. """
page_title
=
_
(
'Receipt'
)
is_payment_complete
=
True
payment_support_email
=
microsite
.
get_value
(
'payment_support_email'
,
settings
.
PAYMENT_SUPPORT_EMAIL
)
payment_support_link
=
'<a href=
\"
mailto:{email}
\"
>{email}</a>'
.
format
(
email
=
payment_support_email
)
is_cybersource
=
all
(
k
in
request
.
POST
for
k
in
(
'signed_field_names'
,
'decision'
,
'reason_code'
))
if
is_cybersource
and
request
.
POST
[
'decision'
]
!=
'ACCEPT'
:
# Cybersource may redirect users to this view if it couldn't recover
# from an error while capturing payment info.
is_payment_complete
=
False
page_title
=
_
(
'Payment Failed'
)
reason_code
=
request
.
POST
[
'reason_code'
]
# if the problem was with the info submitted by the user, we present more detailed messages.
if
is_user_payment_error
(
reason_code
):
error_summary
=
_
(
"There was a problem with this transaction. You have not been charged."
)
error_text
=
_
(
"Make sure your information is correct, or try again with a different card or another form of payment."
)
else
:
error_summary
=
_
(
"A system error occurred while processing your payment. You have not been charged."
)
error_text
=
_
(
"Please wait a few minutes and then try again."
)
for_help_text
=
_
(
"For help, contact {payment_support_link}."
)
.
format
(
payment_support_link
=
payment_support_link
)
else
:
# if anything goes wrong rendering the receipt, it indicates a problem fetching order data.
error_summary
=
_
(
"An error occurred while creating your receipt."
)
error_text
=
None
# nothing particularly helpful to say if this happens.
for_help_text
=
_
(
"If your course does not appear on your dashboard, contact {payment_support_link}."
)
.
format
(
payment_support_link
=
payment_support_link
)
context
=
{
context
=
{
'page_title'
:
page_title
,
'is_payment_complete'
:
is_payment_complete
,
'platform_name'
:
microsite
.
get_value
(
'platform_name'
,
settings
.
PLATFORM_NAME
),
'platform_name'
:
microsite
.
get_value
(
'platform_name'
,
settings
.
PLATFORM_NAME
),
'verified'
:
SoftwareSecurePhotoVerification
.
verification_valid_or_pending
(
request
.
user
)
.
exists
()
'verified'
:
SoftwareSecurePhotoVerification
.
verification_valid_or_pending
(
request
.
user
)
.
exists
(),
'error_summary'
:
error_summary
,
'error_text'
:
error_text
,
'for_help_text'
:
for_help_text
,
'payment_support_email'
:
payment_support_email
,
}
}
return
render_to_response
(
'commerce/checkout_receipt.html'
,
context
)
return
render_to_response
(
'commerce/checkout_receipt.html'
,
context
)
...
...
lms/djangoapps/shoppingcart/processors/CyberSource2.py
View file @
e1dfdc08
...
@@ -32,7 +32,7 @@ from collections import OrderedDict, defaultdict
...
@@ -32,7 +32,7 @@ from collections import OrderedDict, defaultdict
from
decimal
import
Decimal
,
InvalidOperation
from
decimal
import
Decimal
,
InvalidOperation
from
hashlib
import
sha256
from
hashlib
import
sha256
from
django.conf
import
settings
from
django.conf
import
settings
from
django.utils.translation
import
ugettext
as
_
from
django.utils.translation
import
ugettext
as
_
,
ugettext_noop
from
edxmako.shortcuts
import
render_to_string
from
edxmako.shortcuts
import
render_to_string
from
shoppingcart.models
import
Order
from
shoppingcart.models
import
Order
from
shoppingcart.processors.exceptions
import
*
from
shoppingcart.processors.exceptions
import
*
...
@@ -41,6 +41,10 @@ from microsite_configuration import microsite
...
@@ -41,6 +41,10 @@ from microsite_configuration import microsite
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
# Translators: this text appears when an unfamiliar error code occurs during payment,
# for which we don't know a user-friendly message to display in advance.
DEFAULT_REASON
=
ugettext_noop
(
"UNKNOWN REASON"
)
def
process_postpay_callback
(
params
):
def
process_postpay_callback
(
params
):
"""
"""
...
@@ -582,18 +586,29 @@ CARDTYPE_MAP.update(
...
@@ -582,18 +586,29 @@ CARDTYPE_MAP.update(
}
}
)
)
REASONCODE_MAP
=
defaultdict
(
lambda
:
"UNKNOWN REASON"
)
# Note: these messages come directly from official Cybersource documentation at:
# http://apps.cybersource.com/library/documentation/dev_guides/CC_Svcs_SO_API/html/wwhelp/wwhimpl/js/html/wwhelp.htm#href=reason_codes.html
REASONCODE_MAP
=
defaultdict
(
lambda
:
DEFAULT_REASON
)
REASONCODE_MAP
.
update
(
REASONCODE_MAP
.
update
(
{
{
'100'
:
_
(
'Successful transaction.'
),
'100'
:
_
(
'Successful transaction.'
),
'101'
:
_
(
'The request is missing one or more required fields.'
),
'102'
:
_
(
'One or more fields in the request contains invalid data.'
),
'102'
:
_
(
'One or more fields in the request contains invalid data.'
),
'104'
:
dedent
(
_
(
'104'
:
dedent
(
_
(
"""
"""
The
access_key and transaction_uuid fields for this authorization request matches the access_key and
The
merchant reference code for this authorization request matches the merchant reference code of another
transaction_uuid of another authorization request that you sent in the l
ast 15 minutes.
authorization request that you sent within the p
ast 15 minutes.
Possible
fix: retry the payment after 15 minutes
.
Possible
action: Resend the request with a unique merchant reference code
.
"""
)),
"""
)),
'110'
:
_
(
'Only a partial amount was approved.'
),
'110'
:
_
(
'Only a partial amount was approved.'
),
'150'
:
_
(
'General system failure.'
),
'151'
:
dedent
(
_
(
"""
The request was received but there was a server timeout. This error does not include timeouts between the
client and the server.
"""
)),
'152'
:
_
(
'The request was received, but a service did not finish running in time.'
),
'200'
:
dedent
(
_
(
'200'
:
dedent
(
_
(
"""
"""
The authorization request was approved by the issuing bank but declined by CyberSource
The authorization request was approved by the issuing bank but declined by CyberSource
...
@@ -603,63 +618,101 @@ REASONCODE_MAP.update(
...
@@ -603,63 +618,101 @@ REASONCODE_MAP.update(
"""
"""
The issuing bank has questions about the request. You do not receive an
The issuing bank has questions about the request. You do not receive an
authorization code programmatically, but you might receive one verbally by calling the processor.
authorization code programmatically, but you might receive one verbally by calling the processor.
Possible
fix: retry with another form of payment
Possible
action: retry with another form of payment.
"""
)),
"""
)),
'202'
:
dedent
(
_
(
'202'
:
dedent
(
_
(
"""
"""
Expired card. You might also receive this if the expiration date you
Expired card. You might also receive this if the expiration date you
provided does not match the date the issuing bank has on file.
provided does not match the date the issuing bank has on file.
Possible
fix: retry with another form of payment
Possible
action: retry with another form of payment.
"""
)),
"""
)),
'203'
:
dedent
(
_
(
'203'
:
dedent
(
_
(
"""
"""
General decline of the card. No other information provided by the issuing bank.
General decline of the card. No other information provided by the issuing bank.
Possible
fix: retry with another form of payment
Possible
action: retry with another form of payment.
"""
)),
"""
)),
'204'
:
_
(
'Insufficient funds in the account. Possible
fix: retry with another form of payment
'
),
'204'
:
_
(
'Insufficient funds in the account. Possible
action: retry with another form of payment.
'
),
# 205 was Stolen or lost card. Might as well not show this message to the person using such a card.
# 205 was Stolen or lost card. Might as well not show this message to the person using such a card.
'205'
:
_
(
'Stolen or lost card'
),
'205'
:
_
(
'Stolen or lost card
.
'
),
'207'
:
_
(
'Issuing bank unavailable. Possible
fix: retry again after a few minutes
'
),
'207'
:
_
(
'Issuing bank unavailable. Possible
action: retry again after a few minutes.
'
),
'208'
:
dedent
(
_
(
'208'
:
dedent
(
_
(
"""
"""
Inactive card or card not authorized for card-not-present transactions.
Inactive card or card not authorized for card-not-present transactions.
Possible
fix: retry with another form of payment
Possible
action: retry with another form of payment.
"""
)),
"""
)),
'210'
:
_
(
'The card has reached the credit limit. Possible fix: retry with another form of payment'
),
'209'
:
_
(
'CVN did not match.'
),
'211'
:
_
(
'Invalid card verification number (CVN). Possible fix: retry with another form of payment'
),
'210'
:
_
(
'The card has reached the credit limit. Possible action: retry with another form of payment.'
),
'211'
:
_
(
'Invalid card verification number (CVN). Possible action: retry with another form of payment.'
),
# 221 was The customer matched an entry on the processor's negative file.
# 221 was The customer matched an entry on the processor's negative file.
# Might as well not show this message to the person using such a card.
# Might as well not show this message to the person using such a card.
'221'
:
_
(
'The customer matched an entry on the processors negative file.'
),
'221'
:
_
(
'The customer matched an entry on the processors negative file.'
),
'222'
:
_
(
'Account frozen. Possible
fix: retry with another form of payment
'
),
'222'
:
_
(
'Account frozen. Possible
action: retry with another form of payment.
'
),
'230'
:
dedent
(
_
(
'230'
:
dedent
(
_
(
"""
"""
The authorization request was approved by the issuing bank but declined by
The authorization request was approved by the issuing bank but declined by
CyberSource because it did not pass the CVN check.
CyberSource because it did not pass the CVN check.
Possible
fix: retry with another form of payment
Possible
action: retry with another form of payment.
"""
)),
"""
)),
'231'
:
_
(
'Invalid account number. Possible
fix: retry with another form of payment
'
),
'231'
:
_
(
'Invalid account number. Possible
action: retry with another form of payment.
'
),
'232'
:
dedent
(
_
(
'232'
:
dedent
(
_
(
"""
"""
The card type is not accepted by the payment processor.
The card type is not accepted by the payment processor.
Possible
fix: retry with another form of payment
Possible
action: retry with another form of payment.
"""
)),
"""
)),
'233'
:
_
(
'General decline by the processor. Possible
fix: retry with another form of payment
'
),
'233'
:
_
(
'General decline by the processor. Possible
action: retry with another form of payment.
'
),
'234'
:
_
(
'234'
:
_
(
"There is a problem with the information in your CyberSource account. Please let us know at {0}"
"There is a problem with the information in your CyberSource account. Please let us know at {0}"
)
.
format
(
settings
.
PAYMENT_SUPPORT_EMAIL
),
)
.
format
(
settings
.
PAYMENT_SUPPORT_EMAIL
),
'236'
:
_
(
'Processor Failure. Possible fix: retry the payment'
),
'235'
:
_
(
'The requested capture amount exceeds the originally authorized amount.'
),
'236'
:
_
(
'Processor Failure. Possible action: retry the payment'
),
'237'
:
_
(
'The authorization has already been reversed.'
),
'238'
:
_
(
'The authorization has already been captured.'
),
'239'
:
_
(
'The requested transaction amount must match the previous transaction amount.'
),
'240'
:
dedent
(
_
(
'240'
:
dedent
(
_
(
"""
"""
The card type sent is invalid or does not correlate with the credit card number.
The card type sent is invalid or does not correlate with the credit card number.
Possible fix: retry with the same card or another form of payment
Possible action: retry with the same card or another form of payment.
"""
)),
'241'
:
_
(
'The request ID is invalid.'
),
'242'
:
dedent
(
_
(
"""
You requested a capture, but there is no corresponding, unused authorization record. Occurs if there was
not a previously successful authorization request or if the previously successful authorization has already
been used by another capture request.
"""
)),
'243'
:
_
(
'The transaction has already been settled or reversed.'
),
'246'
:
dedent
(
_
(
"""
Either the capture or credit is not voidable because the capture or credit information has already been
submitted to your processor, or you requested a void for a type of transaction that cannot be voided.
"""
)),
"""
)),
'247'
:
_
(
'You requested a credit for a capture that was previously voided.'
),
'250'
:
_
(
'The request was received, but there was a timeout at the payment processor.'
),
'254'
:
_
(
'Stand-alone credits are not allowed.'
),
'475'
:
_
(
'The cardholder is enrolled for payer authentication'
),
'475'
:
_
(
'The cardholder is enrolled for payer authentication'
),
'476'
:
_
(
'Payer authentication could not be authenticated'
),
'476'
:
_
(
'Payer authentication could not be authenticated'
),
'520'
:
dedent
(
_
(
'520'
:
dedent
(
_
(
"""
"""
The authorization request was approved by the issuing bank but declined by CyberSource based
The authorization request was approved by the issuing bank but declined by CyberSource based
on your legacy Smart Authorization settings.
on your legacy Smart Authorization settings.
Possible
fix
: retry with a different form of payment.
Possible
action
: retry with a different form of payment.
"""
)),
"""
)),
}
}
)
)
def
is_user_payment_error
(
reason_code
):
"""
Decide, based on the reason_code, whether or not it signifies a problem
with something the user did (rather than a system error beyond the user's
control).
This function is used to determine whether we can/should show the user a
message with suggested actions to fix the problem, or simply apologize and
ask her to try again later.
"""
reason_code
=
str
(
reason_code
)
if
reason_code
not
in
REASONCODE_MAP
or
REASONCODE_MAP
[
reason_code
]
==
DEFAULT_REASON
:
return
False
return
(
200
<=
int
(
reason_code
)
<=
233
)
or
int
(
reason_code
)
in
(
101
,
102
,
240
)
lms/static/js/commerce/views/receipt_view.js
View file @
e1dfdc08
...
@@ -50,8 +50,9 @@ var edx = edx || {};
...
@@ -50,8 +50,9 @@ var edx = edx || {};
var
self
=
this
,
var
self
=
this
,
orderId
=
$
.
url
(
'?basket_id'
)
||
$
.
url
(
'?payment-order-num'
);
orderId
=
$
.
url
(
'?basket_id'
)
||
$
.
url
(
'?payment-order-num'
);
if
(
orderId
)
{
if
(
orderId
&&
this
.
$el
.
data
(
'is-payment-complete'
)
===
'True'
)
{
// Get the order details
// Get the order details
self
.
$el
.
removeClass
(
'hidden'
);
self
.
getReceiptData
(
orderId
).
then
(
self
.
renderReceipt
,
self
.
renderError
);
self
.
getReceiptData
(
orderId
).
then
(
self
.
renderReceipt
,
self
.
renderError
);
}
else
{
}
else
{
self
.
renderError
();
self
.
renderError
();
...
...
lms/templates/commerce/checkout_receipt.html
View file @
e1dfdc08
...
@@ -6,9 +6,7 @@ from django.utils.translation import ugettext as _
...
@@ -6,9 +6,7 @@ from django.utils.translation import ugettext as _
<
%
inherit
file=
"../main.html"
/>
<
%
inherit
file=
"../main.html"
/>
<
%
block
name=
"bodyclass"
>
register verification-process step-requirements
</
%
block>
<
%
block
name=
"bodyclass"
>
register verification-process step-requirements
</
%
block>
<
%
block
name=
"pagetitle"
>
<
%
block
name=
"pagetitle"
>
${page_title}
</
%
block>
${_("Receipt")}
</
%
block>
<
%
block
name=
"header_extras"
>
<
%
block
name=
"header_extras"
>
<script
type=
"text/template"
id=
"receipt-tpl"
>
<script
type=
"text/template"
id=
"receipt-tpl"
>
...
@@ -33,11 +31,17 @@ ${_("Receipt")}
...
@@ -33,11 +31,17 @@ ${_("Receipt")}
<i
class=
"msg-icon icon fa fa-exclamation-triangle"
aria-hidden=
"true"
></i>
<i
class=
"msg-icon icon fa fa-exclamation-triangle"
aria-hidden=
"true"
></i>
<div
class=
"msg-content"
>
<div
class=
"msg-content"
>
<h3
class=
"title"
>
<h3
class=
"title"
>
<span
class=
"sr"
>
${
_("Error:")
}
</span>
<span
class=
"sr"
>
${
error_summary
}
</span>
${
_("Error")
}
${
error_summary
}
</h3>
</h3>
%if error_text:
<div
class=
"copy"
>
<div
class=
"copy"
>
<p>
${ _("Could not retrieve payment information") }
</p>
<p>
${error_text}
</p>
<br/>
</div>
%endif
<div
class=
"msg"
>
<p>
${for_help_text}
</p>
</div>
</div>
</div>
</div>
</div>
</div>
...
@@ -46,7 +50,7 @@ ${_("Receipt")}
...
@@ -46,7 +50,7 @@ ${_("Receipt")}
<div
class=
"container"
>
<div
class=
"container"
>
<section
class=
"wrapper carousel"
>
<section
class=
"wrapper carousel"
>
<div
id=
"receipt-container"
class=
"pay-and-verify
"
data-platform-name=
'${platform_name}'
data-verified=
'${verified}'
>
<div
id=
"receipt-container"
class=
"pay-and-verify
hidden"
data-is-payment-complete=
'${is_payment_complete}'
data-platform-name=
'${platform_name}'
data-verified=
'${verified}'
>
<h1>
${_("Loading Order Data...")}
</h1>
<h1>
${_("Loading Order Data...")}
</h1>
<span>
${ _("Please wait while we retrieve your order details.") }
</span>
<span>
${ _("Please wait while we retrieve your order details.") }
</span>
</div>
</div>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment