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
b475ac36
Commit
b475ac36
authored
Aug 21, 2013
by
Diana Huang
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Some pep8/pylint cleanup
parent
055ad535
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
124 additions
and
111 deletions
+124
-111
lms/djangoapps/shoppingcart/exceptions.py
+2
-0
lms/djangoapps/shoppingcart/processors/CyberSource.py
+95
-94
lms/djangoapps/shoppingcart/processors/__init__.py
+2
-1
lms/djangoapps/shoppingcart/processors/exceptions.py
+5
-2
lms/djangoapps/shoppingcart/processors/tests/test_CyberSource.py
+13
-13
lms/djangoapps/shoppingcart/views.py
+7
-1
No files found.
lms/djangoapps/shoppingcart/exceptions.py
View file @
b475ac36
class
PaymentException
(
Exception
):
class
PaymentException
(
Exception
):
pass
pass
class
PurchasedCallbackException
(
PaymentException
):
class
PurchasedCallbackException
(
PaymentException
):
pass
pass
class
InvalidCartItem
(
PaymentException
):
class
InvalidCartItem
(
PaymentException
):
pass
pass
lms/djangoapps/shoppingcart/processors/CyberSource.py
View file @
b475ac36
...
@@ -15,7 +15,8 @@ from django.conf import settings
...
@@ -15,7 +15,8 @@ from django.conf import settings
from
django.utils.translation
import
ugettext
as
_
from
django.utils.translation
import
ugettext
as
_
from
mitxmako.shortcuts
import
render_to_string
from
mitxmako.shortcuts
import
render_to_string
from
shoppingcart.models
import
Order
from
shoppingcart.models
import
Order
from
.exceptions
import
*
from
shoppingcart.processors.exceptions
import
*
def
process_postpay_callback
(
params
):
def
process_postpay_callback
(
params
):
"""
"""
...
@@ -42,19 +43,19 @@ def process_postpay_callback(params):
...
@@ -42,19 +43,19 @@ def process_postpay_callback(params):
return
{
'success'
:
False
,
return
{
'success'
:
False
,
'order'
:
result
[
'order'
],
'order'
:
result
[
'order'
],
'error_html'
:
get_processor_decline_html
(
params
)}
'error_html'
:
get_processor_decline_html
(
params
)}
except
CCProcessorException
as
e
:
except
CCProcessorException
as
e
rror
:
return
{
'success'
:
False
,
return
{
'success'
:
False
,
'order'
:
None
,
#
due to exception we may not have the order
'order'
:
None
,
#
due to exception we may not have the order
'error_html'
:
get_processor_exception_html
(
e
)}
'error_html'
:
get_processor_exception_html
(
e
rror
)}
def
hash
(
value
):
def
processor_
hash
(
value
):
"""
"""
Performs the base64(HMAC_SHA1(key, value)) used by CyberSource Hosted Order Page
Performs the base64(HMAC_SHA1(key, value)) used by CyberSource Hosted Order Page
"""
"""
shared_secret
=
settings
.
CC_PROCESSOR
[
'CyberSource'
]
.
get
(
'SHARED_SECRET'
,
''
)
shared_secret
=
settings
.
CC_PROCESSOR
[
'CyberSource'
]
.
get
(
'SHARED_SECRET'
,
''
)
hash_obj
=
hmac
.
new
(
shared_secret
,
value
,
sha1
)
hash_obj
=
hmac
.
new
(
shared_secret
,
value
,
sha1
)
return
binascii
.
b2a_base64
(
hash_obj
.
digest
())[:
-
1
]
# last character is a '\n', which we don't want
return
binascii
.
b2a_base64
(
hash_obj
.
digest
())[:
-
1
]
# last character is a '\n', which we don't want
def
sign
(
params
,
signed_fields_key
=
'orderPage_signedFields'
,
full_sig_key
=
'orderPage_signaturePublic'
):
def
sign
(
params
,
signed_fields_key
=
'orderPage_signedFields'
,
full_sig_key
=
'orderPage_signaturePublic'
):
...
@@ -62,19 +63,19 @@ def sign(params, signed_fields_key='orderPage_signedFields', full_sig_key='order
...
@@ -62,19 +63,19 @@ def sign(params, signed_fields_key='orderPage_signedFields', full_sig_key='order
params needs to be an ordered dict, b/c cybersource documentation states that order is important.
params needs to be an ordered dict, b/c cybersource documentation states that order is important.
Reverse engineered from PHP version provided by cybersource
Reverse engineered from PHP version provided by cybersource
"""
"""
merchant_id
=
settings
.
CC_PROCESSOR
[
'CyberSource'
]
.
get
(
'MERCHANT_ID'
,
''
)
merchant_id
=
settings
.
CC_PROCESSOR
[
'CyberSource'
]
.
get
(
'MERCHANT_ID'
,
''
)
order
Page_version
=
settings
.
CC_PROCESSOR
[
'CyberSource'
]
.
get
(
'ORDERPAGE_VERSION'
,
'7'
)
order
_page_version
=
settings
.
CC_PROCESSOR
[
'CyberSource'
]
.
get
(
'ORDERPAGE_VERSION'
,
'7'
)
serial_number
=
settings
.
CC_PROCESSOR
[
'CyberSource'
]
.
get
(
'SERIAL_NUMBER'
,
''
)
serial_number
=
settings
.
CC_PROCESSOR
[
'CyberSource'
]
.
get
(
'SERIAL_NUMBER'
,
''
)
params
[
'merchantID'
]
=
merchant_id
params
[
'merchantID'
]
=
merchant_id
params
[
'orderPage_timestamp'
]
=
int
(
time
.
time
()
*
1000
)
params
[
'orderPage_timestamp'
]
=
int
(
time
.
time
()
*
1000
)
params
[
'orderPage_version'
]
=
order
P
age_version
params
[
'orderPage_version'
]
=
order
_p
age_version
params
[
'orderPage_serialNumber'
]
=
serial_number
params
[
'orderPage_serialNumber'
]
=
serial_number
fields
=
","
.
join
(
params
.
keys
())
fields
=
","
.
join
(
params
.
keys
())
values
=
","
.
join
([
"{0}={1}"
.
format
(
i
,
params
[
i
])
for
i
in
params
.
keys
()])
values
=
","
.
join
([
"{0}={1}"
.
format
(
i
,
params
[
i
])
for
i
in
params
.
keys
()])
fields_sig
=
hash
(
fields
)
fields_sig
=
processor_
hash
(
fields
)
values
+=
",signedFieldsPublicSignature="
+
fields_sig
values
+=
",signedFieldsPublicSignature="
+
fields_sig
params
[
full_sig_key
]
=
hash
(
values
)
params
[
full_sig_key
]
=
processor_
hash
(
values
)
params
[
signed_fields_key
]
=
fields
params
[
signed_fields_key
]
=
fields
return
params
return
params
...
@@ -90,10 +91,10 @@ def verify_signatures(params, signed_fields_key='signedFields', full_sig_key='si
...
@@ -90,10 +91,10 @@ def verify_signatures(params, signed_fields_key='signedFields', full_sig_key='si
"""
"""
signed_fields
=
params
.
get
(
signed_fields_key
,
''
)
.
split
(
','
)
signed_fields
=
params
.
get
(
signed_fields_key
,
''
)
.
split
(
','
)
data
=
","
.
join
([
"{0}={1}"
.
format
(
k
,
params
.
get
(
k
,
''
))
for
k
in
signed_fields
])
data
=
","
.
join
([
"{0}={1}"
.
format
(
k
,
params
.
get
(
k
,
''
))
for
k
in
signed_fields
])
signed_fields_sig
=
hash
(
params
.
get
(
signed_fields_key
,
''
))
signed_fields_sig
=
processor_
hash
(
params
.
get
(
signed_fields_key
,
''
))
data
+=
",signedFieldsPublicSignature="
+
signed_fields_sig
data
+=
",signedFieldsPublicSignature="
+
signed_fields_sig
returned_sig
=
params
.
get
(
full_sig_key
,
''
)
returned_sig
=
params
.
get
(
full_sig_key
,
''
)
if
hash
(
data
)
!=
returned_sig
:
if
processor_
hash
(
data
)
!=
returned_sig
:
raise
CCProcessorSignatureException
()
raise
CCProcessorSignatureException
()
...
@@ -101,7 +102,7 @@ def render_purchase_form_html(cart):
...
@@ -101,7 +102,7 @@ def render_purchase_form_html(cart):
"""
"""
Renders the HTML of the hidden POST form that must be used to initiate a purchase with CyberSource
Renders the HTML of the hidden POST form that must be used to initiate a purchase with CyberSource
"""
"""
purchase_endpoint
=
settings
.
CC_PROCESSOR
[
'CyberSource'
]
.
get
(
'PURCHASE_ENDPOINT'
,
''
)
purchase_endpoint
=
settings
.
CC_PROCESSOR
[
'CyberSource'
]
.
get
(
'PURCHASE_ENDPOINT'
,
''
)
total_cost
=
cart
.
total_cost
total_cost
=
cart
.
total_cost
amount
=
"{0:0.2f}"
.
format
(
total_cost
)
amount
=
"{0:0.2f}"
.
format
(
total_cost
)
...
@@ -133,15 +134,15 @@ def payment_accepted(params):
...
@@ -133,15 +134,15 @@ def payment_accepted(params):
"""
"""
#make sure required keys are present and convert their values to the right type
#make sure required keys are present and convert their values to the right type
valid_params
=
{}
valid_params
=
{}
for
key
,
type
in
[(
'orderNumber'
,
int
),
for
key
,
key_
type
in
[(
'orderNumber'
,
int
),
(
'orderCurrency'
,
str
),
(
'orderCurrency'
,
str
),
(
'decision'
,
str
)]:
(
'decision'
,
str
)]:
if
key
not
in
params
:
if
key
not
in
params
:
raise
CCProcessorDataException
(
raise
CCProcessorDataException
(
_
(
"The payment processor did not return a required parameter: {0}"
.
format
(
key
))
_
(
"The payment processor did not return a required parameter: {0}"
.
format
(
key
))
)
)
try
:
try
:
valid_params
[
key
]
=
type
(
params
[
key
])
valid_params
[
key
]
=
key_
type
(
params
[
key
])
except
ValueError
:
except
ValueError
:
raise
CCProcessorDataException
(
raise
CCProcessorDataException
(
_
(
"The payment processor returned a badly-typed value {0} for param {1}."
.
format
(
params
[
key
],
key
))
_
(
"The payment processor returned a badly-typed value {0} for param {1}."
.
format
(
params
[
key
],
key
))
...
@@ -170,7 +171,7 @@ def payment_accepted(params):
...
@@ -170,7 +171,7 @@ def payment_accepted(params):
'order'
:
order
}
'order'
:
order
}
else
:
else
:
raise
CCProcessorWrongAmountException
(
raise
CCProcessorWrongAmountException
(
_
(
"The amount charged by the processor {0} {1} is different than the total cost of the order {2} {3}."
\
_
(
"The amount charged by the processor {0} {1} is different than the total cost of the order {2} {3}."
.
format
(
charged_amt
,
valid_params
[
'orderCurrency'
],
.
format
(
charged_amt
,
valid_params
[
'orderCurrency'
],
order
.
total_cost
,
order
.
currency
))
order
.
total_cost
,
order
.
currency
))
)
)
...
@@ -200,26 +201,27 @@ def record_purchase(params, order):
...
@@ -200,26 +201,27 @@ def record_purchase(params, order):
city
=
params
.
get
(
'billTo_city'
,
''
),
city
=
params
.
get
(
'billTo_city'
,
''
),
state
=
params
.
get
(
'billTo_state'
,
''
),
state
=
params
.
get
(
'billTo_state'
,
''
),
country
=
params
.
get
(
'billTo_country'
,
''
),
country
=
params
.
get
(
'billTo_country'
,
''
),
postalcode
=
params
.
get
(
'billTo_postalCode'
,
''
),
postalcode
=
params
.
get
(
'billTo_postalCode'
,
''
),
ccnum
=
ccnum
,
ccnum
=
ccnum
,
cardtype
=
CARDTYPE_MAP
[
params
.
get
(
'card_cardType'
,
'UNKNOWN'
)],
cardtype
=
CARDTYPE_MAP
[
params
.
get
(
'card_cardType'
,
'UNKNOWN'
)],
processor_reply_dump
=
json
.
dumps
(
params
)
processor_reply_dump
=
json
.
dumps
(
params
)
)
)
def
get_processor_decline_html
(
params
):
def
get_processor_decline_html
(
params
):
"""Have to parse through the error codes to return a helpful message"""
"""Have to parse through the error codes to return a helpful message"""
payment_support_email
=
settings
.
PAYMENT_SUPPORT_EMAIL
payment_support_email
=
settings
.
PAYMENT_SUPPORT_EMAIL
msg
=
_
(
dedent
(
msg
=
_
(
dedent
(
"""
"""
<p class="error_msg">
<p class="error_msg">
Sorry! Our payment processor did not accept your payment.
Sorry! Our payment processor did not accept your payment.
The decision in they returned was <span class="decision">{decision}</span>,
The decision in they returned was <span class="decision">{decision}</span>,
and the reason was <span class="reason">{reason_code}:{reason_msg}</span>.
and the reason was <span class="reason">{reason_code}:{reason_msg}</span>.
You were not charged. Please try a different form of payment.
You were not charged. Please try a different form of payment.
Contact us with payment-specific questions at {email}.
Contact us with payment-specific questions at {email}.
</p>
</p>
"""
))
"""
))
return
msg
.
format
(
return
msg
.
format
(
decision
=
params
[
'decision'
],
decision
=
params
[
'decision'
],
...
@@ -234,43 +236,43 @@ def get_processor_exception_html(exception):
...
@@ -234,43 +236,43 @@ def get_processor_exception_html(exception):
payment_support_email
=
settings
.
PAYMENT_SUPPORT_EMAIL
payment_support_email
=
settings
.
PAYMENT_SUPPORT_EMAIL
if
isinstance
(
exception
,
CCProcessorDataException
):
if
isinstance
(
exception
,
CCProcessorDataException
):
msg
=
_
(
dedent
(
msg
=
_
(
dedent
(
"""
"""
<p class="error_msg">
<p class="error_msg">
Sorry! Our payment processor sent us back a payment confirmation that had inconsistent data!
Sorry! Our payment processor sent us back a payment confirmation that had inconsistent data!
We apologize that we cannot verify whether the charge went through and take further action on your order.
We apologize that we cannot verify whether the charge went through and take further action on your order.
The specific error message is: <span class="exception_msg">{msg}</span>.
The specific error message is: <span class="exception_msg">{msg}</span>.
Your credit card may possibly have been charged. Contact us with payment-specific questions at {email}.
Your credit card may possibly have been charged. Contact us with payment-specific questions at {email}.
</p>
</p>
"""
.
format
(
msg
=
exception
.
message
,
email
=
payment_support_email
)))
"""
.
format
(
msg
=
exception
.
message
,
email
=
payment_support_email
)))
return
msg
return
msg
elif
isinstance
(
exception
,
CCProcessorWrongAmountException
):
elif
isinstance
(
exception
,
CCProcessorWrongAmountException
):
msg
=
_
(
dedent
(
msg
=
_
(
dedent
(
"""
"""
<p class="error_msg">
<p class="error_msg">
Sorry! Due to an error your purchase was charged for a different amount than the order total!
Sorry! Due to an error your purchase was charged for a different amount than the order total!
The specific error message is: <span class="exception_msg">{msg}</span>.
The specific error message is: <span class="exception_msg">{msg}</span>.
Your credit card has probably been charged. Contact us with payment-specific questions at {email}.
Your credit card has probably been charged. Contact us with payment-specific questions at {email}.
</p>
</p>
"""
.
format
(
msg
=
exception
.
message
,
email
=
payment_support_email
)))
"""
.
format
(
msg
=
exception
.
message
,
email
=
payment_support_email
)))
return
msg
return
msg
elif
isinstance
(
exception
,
CCProcessorSignatureException
):
elif
isinstance
(
exception
,
CCProcessorSignatureException
):
msg
=
_
(
dedent
(
msg
=
_
(
dedent
(
"""
"""
<p class="error_msg">
<p class="error_msg">
Sorry! Our payment processor sent us back a corrupted message regarding your charge, so we are
Sorry! Our payment processor sent us back a corrupted message regarding your charge, so we are
unable to validate that the message actually came from the payment processor.
unable to validate that the message actually came from the payment processor.
The specific error message is: <span class="exception_msg">{msg}</span>.
The specific error message is: <span class="exception_msg">{msg}</span>.
We apologize that we cannot verify whether the charge went through and take further action on your order.
We apologize that we cannot verify whether the charge went through and take further action on your order.
Your credit card may possibly have been charged. Contact us with payment-specific questions at {email}.
Your credit card may possibly have been charged. Contact us with payment-specific questions at {email}.
</p>
</p>
"""
.
format
(
msg
=
exception
.
message
,
email
=
payment_support_email
)))
"""
.
format
(
msg
=
exception
.
message
,
email
=
payment_support_email
)))
return
msg
return
msg
# fallthrough case, which basically never happens
# fallthrough case, which basically never happens
return
'<p class="error_msg">EXCEPTION!</p>'
return
'<p class="error_msg">EXCEPTION!</p>'
CARDTYPE_MAP
=
defaultdict
(
lambda
:
"UNKNOWN"
)
CARDTYPE_MAP
=
defaultdict
(
lambda
:
"UNKNOWN"
)
CARDTYPE_MAP
.
update
(
CARDTYPE_MAP
.
update
(
{
{
'001'
:
'Visa'
,
'001'
:
'Visa'
,
...
@@ -294,110 +296,110 @@ CARDTYPE_MAP.update(
...
@@ -294,110 +296,110 @@ CARDTYPE_MAP.update(
}
}
)
)
REASONCODE_MAP
=
defaultdict
(
lambda
:
"UNKNOWN REASON"
)
REASONCODE_MAP
=
defaultdict
(
lambda
:
"UNKNOWN REASON"
)
REASONCODE_MAP
.
update
(
REASONCODE_MAP
.
update
(
{
{
'100'
:
_
(
'Successful transaction.'
),
'100'
:
_
(
'Successful transaction.'
),
'101'
:
_
(
'The request is missing one or more required fields.'
),
'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 merchantReferenceCode sent with this authorization request matches the
The merchantReferenceCode sent with this authorization request matches the
merchantReferenceCode of another authorization request that you sent in the last 15 minutes.
merchantReferenceCode of another authorization request that you sent in the last 15 minutes.
Possible fix: retry the payment after 15 minutes.
Possible fix: retry the payment after 15 minutes.
"""
)),
"""
)),
'150'
:
_
(
'Error: General system failure. Possible fix: retry the payment after a few minutes.'
),
'150'
:
_
(
'Error: General system failure. Possible fix: retry the payment after a few minutes.'
),
'151'
:
_
(
dedent
(
'151'
:
_
(
dedent
(
"""
"""
Error: The request was received but there was a server timeout.
Error: The request was received but there was a server timeout.
This error does not include timeouts between the client and the server.
This error does not include timeouts between the client and the server.
Possible fix: retry the payment after some time.
Possible fix: retry the payment after some time.
"""
)),
"""
)),
'152'
:
_
(
dedent
(
'152'
:
_
(
dedent
(
"""
"""
Error: The request was received, but a service did not finish running in time
Error: The request was received, but a service did not finish running in time
Possible fix: retry the payment after some time.
Possible fix: retry the payment after some time.
"""
)),
"""
)),
'201'
:
_
(
'The issuing bank has questions about the request. Possible fix: retry with another form of payment'
),
'201'
:
_
(
'The issuing bank has questions about the request. Possible fix: 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 fix: 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 fix: 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 fix: 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'
:
_
(
'Unknown reason'
),
'205'
:
_
(
'Unknown reason'
),
'207'
:
_
(
'Issuing bank unavailable. Possible fix: retry again after a few minutes'
),
'207'
:
_
(
'Issuing bank unavailable. Possible fix: 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 fix: retry with another form of payment
"""
)),
"""
)),
'210'
:
_
(
'The card has reached the credit limit. Possible fix: retry with another form of payment'
),
'210'
:
_
(
'The card has reached the credit limit. Possible fix: retry with another form of payment'
),
'211'
:
_
(
'Invalid card verification number. Possible fix: retry with another form of payment'
),
'211'
:
_
(
'Invalid card verification number. Possible fix: 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'
:
_
(
'Unknown reason'
),
'221'
:
_
(
'Unknown reason'
),
'231'
:
_
(
'Invalid account number. Possible fix: retry with another form of payment'
),
'231'
:
_
(
'Invalid account number. Possible fix: 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 fix: 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 fix: retry with another form of payment'
),
'234'
:
_
(
dedent
(
'234'
:
_
(
dedent
(
"""
"""
There is a problem with our CyberSource merchant configuration. Please let us know at {0}
There is a problem with our CyberSource merchant configuration. Please let us know at {0}
"""
.
format
(
settings
.
PAYMENT_SUPPORT_EMAIL
))),
"""
.
format
(
settings
.
PAYMENT_SUPPORT_EMAIL
))),
# reason code 235 only applies if we are processing a capture through the API. so we should never see it
# reason code 235 only applies if we are processing a capture through the API. so we should never see it
'235'
:
_
(
'The requested amount exceeds the originally authorized amount.'
),
'235'
:
_
(
'The requested amount exceeds the originally authorized amount.'
),
'236'
:
_
(
'Processor Failure. Possible fix: retry the payment'
),
'236'
:
_
(
'Processor Failure. Possible fix: retry the payment'
),
# reason code 238 only applies if we are processing a capture through the API. so we should never see it
# reason code 238 only applies if we are processing a capture through the API. so we should never see it
'238'
:
_
(
'The authorization has already been captured'
),
'238'
:
_
(
'The authorization has already been captured'
),
# reason code 239 only applies if we are processing a capture or credit through the API,
# reason code 239 only applies if we are processing a capture or credit through the API,
# so we should never see it
# so we should never see it
'239'
:
_
(
'The requested transaction amount must match the previous transaction amount.'
),
'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 fix: retry with the same card or another form of payment
"""
)),
"""
)),
# reason code 241 only applies when we are processing a capture or credit through the API,
# reason code 241 only applies when we are processing a capture or credit through the API,
# so we should never see it
# so we should never see it
'241'
:
_
(
'The request ID is invalid.'
),
'241'
:
_
(
'The request ID is invalid.'
),
# reason code 242 occurs if there was not a previously successful authorization request or
# reason code 242 occurs if there was not a previously successful authorization request or
# if the previously successful authorization has already been used by another capture request.
# if the previously successful authorization has already been used by another capture request.
# This reason code only applies when we are processing a capture through the API
# This reason code only applies when we are processing a capture through the API
# so we should never see it
# so we should never see it
'242'
:
_
(
dedent
(
'242'
:
_
(
dedent
(
"""
"""
You requested a capture through the API, but there is no corresponding, unused authorization record.
You requested a capture through the API, but there is no corresponding, unused authorization record.
"""
)),
"""
)),
# we should never see 243
# we should never see 243
'243'
:
_
(
'The transaction has already been settled or reversed.'
),
'243'
:
_
(
'The transaction has already been settled or reversed.'
),
# reason code 246 applies only if we are processing a void through the API. so we should never see it
# reason code 246 applies only if we are processing a void through the API. so we should never see it
'246'
:
_
(
dedent
(
'246'
:
_
(
dedent
(
"""
"""
The capture or credit is not voidable because the capture or credit information has already been
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.
submitted to your processor. Or, you requested a void for a type of transaction that cannot be voided.
"""
)),
"""
)),
# reason code 247 applies only if we are processing a void through the API. so we should never see it
# reason code 247 applies only if we are processing a void through the API. so we should never see it
'247'
:
_
(
'You requested a credit for a capture that was previously voided'
),
'247'
:
_
(
'You requested a credit for a capture that was previously voided'
),
'250'
:
_
(
dedent
(
'250'
:
_
(
dedent
(
"""
"""
Error: The request was received, but there was a timeout at the payment processor.
Error: The request was received, but there was a timeout at the payment processor.
Possible fix: retry the payment.
Possible fix: retry the payment.
"""
)),
"""
)),
'520'
:
_
(
dedent
(
'520'
:
_
(
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.'
Possible fix: retry with a different form of payment.
Possible fix: retry with a different form of payment.
"""
)),
"""
)),
}
}
)
)
\ No newline at end of file
lms/djangoapps/shoppingcart/processors/__init__.py
View file @
b475ac36
...
@@ -7,6 +7,7 @@ module = __import__('shoppingcart.processors.' + processor_name,
...
@@ -7,6 +7,7 @@ module = __import__('shoppingcart.processors.' + processor_name,
'process_postpay_callback'
,
'process_postpay_callback'
,
])
])
def
render_purchase_form_html
(
*
args
,
**
kwargs
):
def
render_purchase_form_html
(
*
args
,
**
kwargs
):
"""
"""
The top level call to this module to begin the purchase.
The top level call to this module to begin the purchase.
...
@@ -16,6 +17,7 @@ def render_purchase_form_html(*args, **kwargs):
...
@@ -16,6 +17,7 @@ def render_purchase_form_html(*args, **kwargs):
"""
"""
return
module
.
render_purchase_form_html
(
*
args
,
**
kwargs
)
return
module
.
render_purchase_form_html
(
*
args
,
**
kwargs
)
def
process_postpay_callback
(
*
args
,
**
kwargs
):
def
process_postpay_callback
(
*
args
,
**
kwargs
):
"""
"""
The top level call to this module after the purchase.
The top level call to this module after the purchase.
...
@@ -29,4 +31,3 @@ def process_postpay_callback(*args, **kwargs):
...
@@ -29,4 +31,3 @@ def process_postpay_callback(*args, **kwargs):
return a helpful-enough error message in error_html.
return a helpful-enough error message in error_html.
"""
"""
return
module
.
process_postpay_callback
(
*
args
,
**
kwargs
)
return
module
.
process_postpay_callback
(
*
args
,
**
kwargs
)
lms/djangoapps/shoppingcart/processors/exceptions.py
View file @
b475ac36
from
shoppingcart.exceptions
import
PaymentException
from
shoppingcart.exceptions
import
PaymentException
class
CCProcessorException
(
PaymentException
):
class
CCProcessorException
(
PaymentException
):
pass
pass
class
CCProcessorSignatureException
(
CCProcessorException
):
class
CCProcessorSignatureException
(
CCProcessorException
):
pass
pass
class
CCProcessorDataException
(
CCProcessorException
):
class
CCProcessorDataException
(
CCProcessorException
):
pass
pass
class
CCProcessorWrongAmountException
(
CCProcessorException
):
class
CCProcessorWrongAmountException
(
CCProcessorException
):
pass
pass
\ No newline at end of file
lms/djangoapps/shoppingcart/processors/tests/test_CyberSource.py
View file @
b475ac36
...
@@ -13,15 +13,16 @@ from mock import patch, Mock
...
@@ -13,15 +13,16 @@ from mock import patch, Mock
TEST_CC_PROCESSOR
=
{
TEST_CC_PROCESSOR
=
{
'CyberSource'
:
{
'CyberSource'
:
{
'SHARED_SECRET'
:
'secret'
,
'SHARED_SECRET'
:
'secret'
,
'MERCHANT_ID'
:
'edx_test'
,
'MERCHANT_ID'
:
'edx_test'
,
'SERIAL_NUMBER'
:
'12345'
,
'SERIAL_NUMBER'
:
'12345'
,
'ORDERPAGE_VERSION'
:
'7'
,
'ORDERPAGE_VERSION'
:
'7'
,
'PURCHASE_ENDPOINT'
:
''
,
'PURCHASE_ENDPOINT'
:
''
,
}
}
}
}
@override_settings
(
CC_PROCESSOR
=
TEST_CC_PROCESSOR
)
@override_settings
(
CC_PROCESSOR
=
TEST_CC_PROCESSOR
)
class
CyberSourceTests
(
TestCase
):
class
CyberSourceTests
(
TestCase
):
...
@@ -36,8 +37,8 @@ class CyberSourceTests(TestCase):
...
@@ -36,8 +37,8 @@ class CyberSourceTests(TestCase):
"""
"""
Tests the hash function. Basically just hardcodes the answer.
Tests the hash function. Basically just hardcodes the answer.
"""
"""
self
.
assertEqual
(
hash
(
'test'
),
'GqNJWF7X7L07nEhqMAZ+OVyks1Y='
)
self
.
assertEqual
(
processor_
hash
(
'test'
),
'GqNJWF7X7L07nEhqMAZ+OVyks1Y='
)
self
.
assertEqual
(
hash
(
'edx '
),
'/KowheysqM2PFYuxVKg0P8Flfk4='
)
self
.
assertEqual
(
processor_
hash
(
'edx '
),
'/KowheysqM2PFYuxVKg0P8Flfk4='
)
def
test_sign_then_verify
(
self
):
def
test_sign_then_verify
(
self
):
"""
"""
...
@@ -76,7 +77,7 @@ class CyberSourceTests(TestCase):
...
@@ -76,7 +77,7 @@ class CyberSourceTests(TestCase):
"""
"""
DECISION
=
'REJECT'
DECISION
=
'REJECT'
for
code
,
reason
in
REASONCODE_MAP
.
iteritems
():
for
code
,
reason
in
REASONCODE_MAP
.
iteritems
():
params
=
{
params
=
{
'decision'
:
DECISION
,
'decision'
:
DECISION
,
'reasonCode'
:
code
,
'reasonCode'
:
code
,
}
}
...
@@ -109,8 +110,8 @@ class CyberSourceTests(TestCase):
...
@@ -109,8 +110,8 @@ class CyberSourceTests(TestCase):
student1
.
save
()
student1
.
save
()
student2
=
UserFactory
()
student2
=
UserFactory
()
student2
.
save
()
student2
.
save
()
params_cc
=
{
'card_accountNumber'
:
'1234'
,
'card_cardType'
:
'001'
,
'billTo_firstName'
:
student1
.
first_name
}
params_cc
=
{
'card_accountNumber'
:
'1234'
,
'card_cardType'
:
'001'
,
'billTo_firstName'
:
student1
.
first_name
}
params_nocc
=
{
'card_accountNumber'
:
''
,
'card_cardType'
:
'002'
,
'billTo_firstName'
:
student2
.
first_name
}
params_nocc
=
{
'card_accountNumber'
:
''
,
'card_cardType'
:
'002'
,
'billTo_firstName'
:
student2
.
first_name
}
order1
=
Order
.
get_cart_for_user
(
student1
)
order1
=
Order
.
get_cart_for_user
(
student1
)
order2
=
Order
.
get_cart_for_user
(
student2
)
order2
=
Order
.
get_cart_for_user
(
student2
)
record_purchase
(
params_cc
,
order1
)
record_purchase
(
params_cc
,
order1
)
...
@@ -173,7 +174,7 @@ class CyberSourceTests(TestCase):
...
@@ -173,7 +174,7 @@ class CyberSourceTests(TestCase):
# tests for an order number that doesn't match up
# tests for an order number that doesn't match up
params_bad_ordernum
=
params
.
copy
()
params_bad_ordernum
=
params
.
copy
()
params_bad_ordernum
[
'orderNumber'
]
=
str
(
order1
.
id
+
10
)
params_bad_ordernum
[
'orderNumber'
]
=
str
(
order1
.
id
+
10
)
with
self
.
assertRaises
(
CCProcessorDataException
):
with
self
.
assertRaises
(
CCProcessorDataException
):
payment_accepted
(
params_bad_ordernum
)
payment_accepted
(
params_bad_ordernum
)
...
@@ -215,7 +216,7 @@ class CyberSourceTests(TestCase):
...
@@ -215,7 +216,7 @@ class CyberSourceTests(TestCase):
self
.
assertDictContainsSubset
({
'amount'
:
'1.00'
,
self
.
assertDictContainsSubset
({
'amount'
:
'1.00'
,
'currency'
:
'usd'
,
'currency'
:
'usd'
,
'orderPage_transactionType'
:
'sale'
,
'orderPage_transactionType'
:
'sale'
,
'orderNumber'
:
str
(
order1
.
id
)},
'orderNumber'
:
str
(
order1
.
id
)},
context
[
'params'
])
context
[
'params'
])
def
test_process_postpay_exception
(
self
):
def
test_process_postpay_exception
(
self
):
...
@@ -257,7 +258,7 @@ class CyberSourceTests(TestCase):
...
@@ -257,7 +258,7 @@ class CyberSourceTests(TestCase):
result
=
process_postpay_callback
(
params
)
result
=
process_postpay_callback
(
params
)
self
.
assertTrue
(
result
[
'success'
])
self
.
assertTrue
(
result
[
'success'
])
self
.
assertEqual
(
result
[
'order'
],
order1
)
self
.
assertEqual
(
result
[
'order'
],
order1
)
order1
=
Order
.
objects
.
get
(
id
=
order1
.
id
)
# reload from DB to capture side-effect of process_postpay_callback
order1
=
Order
.
objects
.
get
(
id
=
order1
.
id
)
# reload from DB to capture side-effect of process_postpay_callback
self
.
assertEqual
(
order1
.
status
,
'purchased'
)
self
.
assertEqual
(
order1
.
status
,
'purchased'
)
self
.
assertFalse
(
result
[
'error_html'
])
self
.
assertFalse
(
result
[
'error_html'
])
...
@@ -284,4 +285,4 @@ class CyberSourceTests(TestCase):
...
@@ -284,4 +285,4 @@ class CyberSourceTests(TestCase):
self
.
assertFalse
(
result
[
'success'
])
self
.
assertFalse
(
result
[
'success'
])
self
.
assertEqual
(
result
[
'order'
],
order1
)
self
.
assertEqual
(
result
[
'order'
],
order1
)
self
.
assertEqual
(
order1
.
status
,
'cart'
)
self
.
assertEqual
(
order1
.
status
,
'cart'
)
self
.
assertIn
(
REASONCODE_MAP
[
'207'
],
result
[
'error_html'
])
self
.
assertIn
(
REASONCODE_MAP
[
'207'
],
result
[
'error_html'
])
\ No newline at end of file
lms/djangoapps/shoppingcart/views.py
View file @
b475ac36
...
@@ -12,6 +12,7 @@ from .processors import process_postpay_callback, render_purchase_form_html
...
@@ -12,6 +12,7 @@ from .processors import process_postpay_callback, render_purchase_form_html
log
=
logging
.
getLogger
(
"shoppingcart"
)
log
=
logging
.
getLogger
(
"shoppingcart"
)
def
test
(
request
,
course_id
):
def
test
(
request
,
course_id
):
item1
=
PaidCourseRegistration
(
course_id
,
200
)
item1
=
PaidCourseRegistration
(
course_id
,
200
)
item1
.
purchased_callback
(
request
.
user
.
id
)
item1
.
purchased_callback
(
request
.
user
.
id
)
...
@@ -41,6 +42,7 @@ def register_for_verified_cert(request, course_id):
...
@@ -41,6 +42,7 @@ def register_for_verified_cert(request, course_id):
CertificateItem
.
add_to_order
(
cart
,
course_id
,
30
,
'verified'
)
CertificateItem
.
add_to_order
(
cart
,
course_id
,
30
,
'verified'
)
return
HttpResponse
(
"Added"
)
return
HttpResponse
(
"Added"
)
@login_required
@login_required
def
show_cart
(
request
):
def
show_cart
(
request
):
cart
=
Order
.
get_cart_for_user
(
request
.
user
)
cart
=
Order
.
get_cart_for_user
(
request
.
user
)
...
@@ -54,12 +56,14 @@ def show_cart(request):
...
@@ -54,12 +56,14 @@ def show_cart(request):
'form_html'
:
form_html
,
'form_html'
:
form_html
,
})
})
@login_required
@login_required
def
clear_cart
(
request
):
def
clear_cart
(
request
):
cart
=
Order
.
get_cart_for_user
(
request
.
user
)
cart
=
Order
.
get_cart_for_user
(
request
.
user
)
cart
.
clear
()
cart
.
clear
()
return
HttpResponse
(
'Cleared'
)
return
HttpResponse
(
'Cleared'
)
@login_required
@login_required
def
remove_item
(
request
):
def
remove_item
(
request
):
item_id
=
request
.
REQUEST
.
get
(
'id'
,
'-1'
)
item_id
=
request
.
REQUEST
.
get
(
'id'
,
'-1'
)
...
@@ -71,6 +75,7 @@ def remove_item(request):
...
@@ -71,6 +75,7 @@ def remove_item(request):
log
.
exception
(
'Cannot remove cart OrderItem id={0}. DoesNotExist or item is already purchased'
.
format
(
item_id
))
log
.
exception
(
'Cannot remove cart OrderItem id={0}. DoesNotExist or item is already purchased'
.
format
(
item_id
))
return
HttpResponse
(
'OK'
)
return
HttpResponse
(
'OK'
)
@csrf_exempt
@csrf_exempt
def
postpay_callback
(
request
):
def
postpay_callback
(
request
):
"""
"""
...
@@ -87,9 +92,10 @@ def postpay_callback(request):
...
@@ -87,9 +92,10 @@ def postpay_callback(request):
if
result
[
'success'
]:
if
result
[
'success'
]:
return
HttpResponseRedirect
(
reverse
(
'shoppingcart.views.show_receipt'
,
args
=
[
result
[
'order'
]
.
id
]))
return
HttpResponseRedirect
(
reverse
(
'shoppingcart.views.show_receipt'
,
args
=
[
result
[
'order'
]
.
id
]))
else
:
else
:
return
render_to_response
(
'shoppingcart/error.html'
,
{
'order'
:
result
[
'order'
],
return
render_to_response
(
'shoppingcart/error.html'
,
{
'order'
:
result
[
'order'
],
'error_html'
:
result
[
'error_html'
]})
'error_html'
:
result
[
'error_html'
]})
@login_required
@login_required
def
show_receipt
(
request
,
ordernum
):
def
show_receipt
(
request
,
ordernum
):
"""
"""
...
...
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