Commit 1f6bdca6 by Jason Bau Committed by Diana Huang

add Validation function for cybersource receipt POST

parent ed4e7f54
...@@ -7,4 +7,5 @@ urlpatterns = patterns('shoppingcart.views', # nopep8 ...@@ -7,4 +7,5 @@ urlpatterns = patterns('shoppingcart.views', # nopep8
url(r'^clear/$','clear_cart'), url(r'^clear/$','clear_cart'),
url(r'^remove_item/$', 'remove_item'), url(r'^remove_item/$', 'remove_item'),
url(r'^purchased/$', 'purchased'), url(r'^purchased/$', 'purchased'),
url(r'^receipt/$', 'receipt'),
) )
\ No newline at end of file
...@@ -4,16 +4,22 @@ import time ...@@ -4,16 +4,22 @@ import time
import hmac import hmac
import binascii import binascii
from hashlib import sha1 from hashlib import sha1
from collections import OrderedDict
from django.conf import settings from django.conf import settings
from collections import OrderedDict
from django.http import HttpResponse, HttpResponseRedirect from django.http import HttpResponse, HttpResponseRedirect
from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from mitxmako.shortcuts import render_to_response from mitxmako.shortcuts import render_to_response
from .models import * from .models import *
log = logging.getLogger("shoppingcart") log = logging.getLogger("shoppingcart")
shared_secret = settings.CYBERSOURCE.get('SHARED_SECRET','')
merchant_id = settings.CYBERSOURCE.get('MERCHANT_ID','')
serial_number = settings.CYBERSOURCE.get('SERIAL_NUMBER','')
orderPage_version = settings.CYBERSOURCE.get('ORDERPAGE_VERSION','7')
def test(request, course_id): def test(request, course_id):
item1 = PaidCourseRegistration(course_id, 200) item1 = PaidCourseRegistration(course_id, 200)
...@@ -39,9 +45,11 @@ def add_course_to_cart(request, course_id): ...@@ -39,9 +45,11 @@ def add_course_to_cart(request, course_id):
def show_cart(request): def show_cart(request):
cart = Order.get_cart_for_user(request.user) cart = Order.get_cart_for_user(request.user)
total_cost = cart.total_cost total_cost = cart.total_cost
amount = "{0:0.2f}".format(total_cost)
cart_items = cart.orderitem_set.all() cart_items = cart.orderitem_set.all()
params = OrderedDict() params = OrderedDict()
params['amount'] = total_cost params['comment'] = 'Stanford OpenEdX Purchase'
params['amount'] = amount
params['currency'] = 'usd' params['currency'] = 'usd'
params['orderPage_transactionType'] = 'sale' params['orderPage_transactionType'] = 'sale'
params['orderNumber'] = "{0:d}".format(cart.id) params['orderNumber'] = "{0:d}".format(cart.id)
...@@ -57,7 +65,7 @@ def show_cart(request): ...@@ -57,7 +65,7 @@ def show_cart(request):
signed_param_dict = cybersource_sign(params) signed_param_dict = cybersource_sign(params)
return render_to_response("shoppingcart/list.html", return render_to_response("shoppingcart/list.html",
{'shoppingcart_items': cart_items, {'shoppingcart_items': cart_items,
'total_cost': total_cost, 'amount': amount,
'params': signed_param_dict, 'params': signed_param_dict,
}) })
...@@ -78,27 +86,50 @@ def remove_item(request): ...@@ -78,27 +86,50 @@ 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
def receipt(request):
"""
Receives the POST-back from Cybersource and performs the validation and displays a receipt
and does some other stuff
"""
if cybersource_verify(request.POST):
return HttpResponse("Validated")
else:
return HttpResponse("Not Validated")
def cybersource_hash(value):
"""
Performs the base64(HMAC_SHA1(key, value)) used by CyberSource Hosted Order Page
"""
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
def cybersource_sign(params): def cybersource_sign(params):
""" """
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
""" """
shared_secret = settings.CYBERSOURCE.get('SHARED_SECRET','')
merchant_id = settings.CYBERSOURCE.get('MERCHANT_ID','')
serial_number = settings.CYBERSOURCE.get('SERIAL_NUMBER','')
orderPage_version = settings.CYBERSOURCE.get('ORDERPAGE_VERSION','7')
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'] = orderPage_version params['orderPage_version'] = orderPage_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_hash_obj = hmac.new(shared_secret, fields, sha1) fields_sig = cybersource_hash(fields)
fields_sig = binascii.b2a_base64(fields_hash_obj.digest())[:-1] # last character is a '\n', which we don't want
values += ",signedFieldsPublicSignature=" + fields_sig values += ",signedFieldsPublicSignature=" + fields_sig
values_hash_obj = hmac.new(shared_secret, values, sha1) params['orderPage_signaturePublic'] = cybersource_hash(values)
params['orderPage_signaturePublic'] = binascii.b2a_base64(values_hash_obj.digest())[:-1]
params['orderPage_signedFields'] = fields params['orderPage_signedFields'] = fields
return params return params
\ No newline at end of file
def cybersource_verify(params):
signed_fields = params.get('signedFields', '').split(',')
data = ",".join(["{0}={1}".format(k, params.get(k, '')) for k in signed_fields])
signed_fields_sig = cybersource_hash(params.get('signedFields', ''))
data += ",signedFieldsPublicSignature=" + signed_fields_sig
returned_sig = params.get('signedDataPublicSignature','')
if not returned_sig:
return False
return cybersource_hash(data) == returned_sig
...@@ -14,11 +14,12 @@ ...@@ -14,11 +14,12 @@
</thead> </thead>
<tbody> <tbody>
% for item in shoppingcart_items: % for item in shoppingcart_items:
<tr><td>${item.qty}</td><td>${item.line_desc}</td><td>${item.unit_cost}</td><td>${item.line_cost}</td> <tr><td>${item.qty}</td><td>${item.line_desc}</td>
<td>${"{0:0.2f}".format(item.unit_cost)}</td><td>${"{0:0.2f}".format(item.line_cost)}</td>
<td><a data-item-id="${item.id}" class='remove_line_item' href='#'>[x]</a></td></tr> <td><a data-item-id="${item.id}" class='remove_line_item' href='#'>[x]</a></td></tr>
% endfor % endfor
<tr><td></td><td></td><td></td><td>Total Cost</td></tr> <tr><td></td><td></td><td></td><td>Total Amount</td></tr>
<tr><td></td><td></td><td></td><td>${total_cost}</td></tr> <tr><td></td><td></td><td></td><td>${amount}</td></tr>
</tbody> </tbody>
</table> </table>
......
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