add Validation function for cybersource receipt POST

This commit is contained in:
Jason Bau
2013-08-08 21:55:08 -07:00
committed by Diana Huang
parent 3f9c52cd1c
commit ff5ca76aa6
3 changed files with 48 additions and 15 deletions

View File

@@ -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'),
) )

View File

@@ -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):
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):
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):
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
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

View File

@@ -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>