add Validation function for cybersource receipt POST
This commit is contained in:
@@ -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'),
|
||||||
)
|
)
|
||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user