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
a4f5f4e4
Commit
a4f5f4e4
authored
Aug 13, 2013
by
Jason Bau
Committed by
Diana Huang
Aug 21, 2013
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
about page changes, refactor processor reply handling
parent
0b8f4144
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
163 additions
and
138 deletions
+163
-138
common/lib/xmodule/xmodule/course_module.py
+1
-0
common/static/js/capa/spec/jsinput_spec.js
+0
-70
lms/djangoapps/shoppingcart/models.py
+40
-14
lms/djangoapps/shoppingcart/processors/CyberSource.py
+50
-2
lms/djangoapps/shoppingcart/processors/__init__.py
+24
-8
lms/djangoapps/shoppingcart/processors/exceptions.py
+2
-1
lms/djangoapps/shoppingcart/urls.py
+2
-2
lms/djangoapps/shoppingcart/views.py
+33
-38
lms/templates/courseware/course_about.html
+1
-1
lms/templates/shoppingcart/list.html
+5
-1
lms/templates/shoppingcart/receipt.html
+5
-1
No files found.
common/lib/xmodule/xmodule/course_module.py
View file @
a4f5f4e4
...
@@ -338,6 +338,7 @@ class CourseFields(object):
...
@@ -338,6 +338,7 @@ class CourseFields(object):
show_timezone
=
Boolean
(
help
=
"True if timezones should be shown on dates in the courseware"
,
scope
=
Scope
.
settings
,
default
=
True
)
show_timezone
=
Boolean
(
help
=
"True if timezones should be shown on dates in the courseware"
,
scope
=
Scope
.
settings
,
default
=
True
)
enrollment_domain
=
String
(
help
=
"External login method associated with user accounts allowed to register in course"
,
enrollment_domain
=
String
(
help
=
"External login method associated with user accounts allowed to register in course"
,
scope
=
Scope
.
settings
)
scope
=
Scope
.
settings
)
enrollment_cost
=
Dict
(
scope
=
Scope
.
settings
,
default
=
{
'currency'
:
'usd'
,
'cost'
:
0
})
# An extra property is used rather than the wiki_slug/number because
# An extra property is used rather than the wiki_slug/number because
# there are courses that change the number for different runs. This allows
# there are courses that change the number for different runs. This allows
...
...
common/static/js/capa/spec/jsinput_spec.js
deleted
100644 → 0
View file @
0b8f4144
xdescribe
(
"A jsinput has:"
,
function
()
{
beforeEach
(
function
()
{
$
(
'#fixture'
).
remove
();
$
.
ajax
({
async
:
false
,
url
:
'mainfixture.html'
,
success
:
function
(
data
)
{
$
(
'body'
).
append
(
$
(
data
));
}
});
});
describe
(
"The jsinput constructor"
,
function
(){
var
iframe1
=
$
(
document
).
find
(
'iframe'
)[
0
];
var
testJsElem
=
jsinputConstructor
({
id
:
1
,
elem
:
iframe1
,
passive
:
false
});
it
(
"Returns an object"
,
function
(){
expect
(
typeof
(
testJsElem
)).
toEqual
(
'object'
);
});
it
(
"Adds the object to the jsinput array"
,
function
()
{
expect
(
jsinput
.
exists
(
1
)).
toBe
(
true
);
});
describe
(
"The returned object"
,
function
()
{
it
(
"Has a public 'update' method"
,
function
(){
expect
(
testJsElem
.
update
).
toBeDefined
();
});
it
(
"Returns an 'update' that is idempotent"
,
function
(){
var
orig
=
testJsElem
.
update
();
for
(
var
i
=
0
;
i
++
;
i
<
5
)
{
expect
(
testJsElem
.
update
()).
toEqual
(
orig
);
}
});
it
(
"Changes the parent's inputfield"
,
function
()
{
testJsElem
.
update
();
});
});
});
describe
(
"The walkDOM functions"
,
function
()
{
walkDOM
();
it
(
"Creates (at least) one object per iframe"
,
function
()
{
jsinput
.
arr
.
length
>=
2
;
});
it
(
"Does not create multiple objects with the same id"
,
function
()
{
while
(
jsinput
.
arr
.
length
>
0
)
{
var
elem
=
jsinput
.
arr
.
pop
();
expect
(
jsinput
.
exists
(
elem
.
id
)).
toBe
(
false
);
}
});
});
})
lms/djangoapps/shoppingcart/models.py
View file @
a4f5f4e4
...
@@ -123,11 +123,13 @@ class OrderItem(models.Model):
...
@@ -123,11 +123,13 @@ class OrderItem(models.Model):
Unfortunately the QuerySet used determines the class to be OrderItem, and not its most specific
Unfortunately the QuerySet used determines the class to be OrderItem, and not its most specific
subclasses. That means this parent class implementation of purchased_callback needs to act as
subclasses. That means this parent class implementation of purchased_callback needs to act as
a dispatcher to call the callback the proper subclasses, and as such it needs to know about all subclasses.
a dispatcher to call the callback the proper subclasses, and as such it needs to know about all
So please add
possible subclasses.
So keep ORDER_ITEM_SUBTYPES up-to-date
"""
"""
for
cl
assname
,
lc_classname
in
ORDER_ITEM_SUBTYPES
:
for
cl
s
,
lc_classname
in
ORDER_ITEM_SUBTYPES
.
iteritems
()
:
try
:
try
:
#Uses https://docs.djangoproject.com/en/1.4/topics/db/models/#multi-table-inheritance to test subclass
sub_instance
=
getattr
(
self
,
lc_classname
)
sub_instance
=
getattr
(
self
,
lc_classname
)
sub_instance
.
purchased_callback
()
sub_instance
.
purchased_callback
()
except
(
ObjectDoesNotExist
,
AttributeError
):
except
(
ObjectDoesNotExist
,
AttributeError
):
...
@@ -135,13 +137,18 @@ class OrderItem(models.Model):
...
@@ -135,13 +137,18 @@ class OrderItem(models.Model):
.
format
(
lc_classname
))
.
format
(
lc_classname
))
pass
pass
# Each entry is a tuple of ('ModelName', 'lower_case_model_name')
def
is_of_subtype
(
self
,
cls
):
# See https://docs.djangoproject.com/en/1.4/topics/db/models/#multi-table-inheritance for
"""
# PLEASE KEEP THIS LIST UP_TO_DATE WITH THE SUBCLASSES OF OrderItem
Checks if self is also a type of cls, in addition to being an OrderItem
ORDER_ITEM_SUBTYPES
=
[
Uses https://docs.djangoproject.com/en/1.4/topics/db/models/#multi-table-inheritance to test for subclass
(
'PaidCourseRegistration'
,
'paidcourseregistration'
)
"""
]
if
cls
not
in
ORDER_ITEM_SUBTYPES
:
return
False
try
:
getattr
(
self
,
ORDER_ITEM_SUBTYPES
[
cls
])
return
True
except
(
ObjectDoesNotExist
,
AttributeError
):
return
False
class
PaidCourseRegistration
(
OrderItem
):
class
PaidCourseRegistration
(
OrderItem
):
...
@@ -151,7 +158,16 @@ class PaidCourseRegistration(OrderItem):
...
@@ -151,7 +158,16 @@ class PaidCourseRegistration(OrderItem):
course_id
=
models
.
CharField
(
max_length
=
128
,
db_index
=
True
)
course_id
=
models
.
CharField
(
max_length
=
128
,
db_index
=
True
)
@classmethod
@classmethod
def
add_to_order
(
cls
,
order
,
course_id
,
cost
,
currency
=
'usd'
):
def
part_of_order
(
cls
,
order
,
course_id
):
"""
Is the course defined by course_id in the order?
"""
return
course_id
in
[
item
.
paidcourseregistration
.
course_id
for
item
in
order
.
orderitem_set
.
all
()
if
item
.
is_of_subtype
(
PaidCourseRegistration
)]
@classmethod
def
add_to_order
(
cls
,
order
,
course_id
,
cost
=
None
,
currency
=
None
):
"""
"""
A standardized way to create these objects, with sensible defaults filled in.
A standardized way to create these objects, with sensible defaults filled in.
Will update the cost if called on an order that already carries the course.
Will update the cost if called on an order that already carries the course.
...
@@ -164,6 +180,10 @@ class PaidCourseRegistration(OrderItem):
...
@@ -164,6 +180,10 @@ class PaidCourseRegistration(OrderItem):
item
,
created
=
cls
.
objects
.
get_or_create
(
order
=
order
,
user
=
order
.
user
,
course_id
=
course_id
)
item
,
created
=
cls
.
objects
.
get_or_create
(
order
=
order
,
user
=
order
.
user
,
course_id
=
course_id
)
item
.
status
=
order
.
status
item
.
status
=
order
.
status
item
.
qty
=
1
item
.
qty
=
1
if
cost
is
None
:
cost
=
course
.
enrollment_cost
[
'cost'
]
if
currency
is
None
:
currency
=
course
.
enrollment_cost
[
'currency'
]
item
.
unit_cost
=
cost
item
.
unit_cost
=
cost
item
.
line_cost
=
cost
item
.
line_cost
=
cost
item
.
line_desc
=
'Registration for Course: {0}'
.
format
(
get_course_about_section
(
course
,
"title"
))
item
.
line_desc
=
'Registration for Course: {0}'
.
format
(
get_course_about_section
(
course
,
"title"
))
...
@@ -182,9 +202,6 @@ class PaidCourseRegistration(OrderItem):
...
@@ -182,9 +202,6 @@ class PaidCourseRegistration(OrderItem):
# throw errors if it doesn't
# throw errors if it doesn't
# use get_or_create here to gracefully handle case where the user is already enrolled in the course, for
# use get_or_create here to gracefully handle case where the user is already enrolled in the course, for
# whatever reason.
# whatever reason.
# Don't really need to create CourseEnrollmentAllowed object, but doing it for bookkeeping and consistency
# with rest of codebase.
CourseEnrollmentAllowed
.
objects
.
get_or_create
(
email
=
self
.
user
.
email
,
course_id
=
self
.
course_id
,
auto_enroll
=
True
)
CourseEnrollment
.
objects
.
get_or_create
(
user
=
self
.
user
,
course_id
=
self
.
course_id
)
CourseEnrollment
.
objects
.
get_or_create
(
user
=
self
.
user
,
course_id
=
self
.
course_id
)
log
.
info
(
"Enrolled {0} in paid course {1}, paid ${2}"
.
format
(
self
.
user
.
email
,
self
.
course_id
,
self
.
line_cost
))
log
.
info
(
"Enrolled {0} in paid course {1}, paid ${2}"
.
format
(
self
.
user
.
email
,
self
.
course_id
,
self
.
line_cost
))
...
@@ -193,3 +210,11 @@ class PaidCourseRegistration(OrderItem):
...
@@ -193,3 +210,11 @@ class PaidCourseRegistration(OrderItem):
tags
=
[
"org:{0}"
.
format
(
org
),
tags
=
[
"org:{0}"
.
format
(
org
),
"course:{0}"
.
format
(
course_num
),
"course:{0}"
.
format
(
course_num
),
"run:{0}"
.
format
(
run
)])
"run:{0}"
.
format
(
run
)])
# Each entry is a dictionary of ModelName: 'lower_case_model_name'
# See https://docs.djangoproject.com/en/1.4/topics/db/models/#multi-table-inheritance for
# PLEASE KEEP THIS LIST UP_TO_DATE WITH THE SUBCLASSES OF OrderItem
ORDER_ITEM_SUBTYPES
=
{
PaidCourseRegistration
:
'paidcourseregistration'
,
}
\ No newline at end of file
lms/djangoapps/shoppingcart/processors/CyberSource.py
View file @
a4f5f4e4
...
@@ -13,7 +13,7 @@ from django.conf import settings
...
@@ -13,7 +13,7 @@ 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
CCProcessorDataException
,
CCProcessorWrongAmountException
from
.exceptions
import
CCProcessor
Exception
,
CCProcessor
DataException
,
CCProcessorWrongAmountException
shared_secret
=
settings
.
CC_PROCESSOR
[
'CyberSource'
]
.
get
(
'SHARED_SECRET'
,
''
)
shared_secret
=
settings
.
CC_PROCESSOR
[
'CyberSource'
]
.
get
(
'SHARED_SECRET'
,
''
)
merchant_id
=
settings
.
CC_PROCESSOR
[
'CyberSource'
]
.
get
(
'MERCHANT_ID'
,
''
)
merchant_id
=
settings
.
CC_PROCESSOR
[
'CyberSource'
]
.
get
(
'MERCHANT_ID'
,
''
)
...
@@ -21,6 +21,42 @@ serial_number = settings.CC_PROCESSOR['CyberSource'].get('SERIAL_NUMBER','')
...
@@ -21,6 +21,42 @@ serial_number = settings.CC_PROCESSOR['CyberSource'].get('SERIAL_NUMBER','')
orderPage_version
=
settings
.
CC_PROCESSOR
[
'CyberSource'
]
.
get
(
'ORDERPAGE_VERSION'
,
'7'
)
orderPage_version
=
settings
.
CC_PROCESSOR
[
'CyberSource'
]
.
get
(
'ORDERPAGE_VERSION'
,
'7'
)
purchase_endpoint
=
settings
.
CC_PROCESSOR
[
'CyberSource'
]
.
get
(
'PURCHASE_ENDPOINT'
,
''
)
purchase_endpoint
=
settings
.
CC_PROCESSOR
[
'CyberSource'
]
.
get
(
'PURCHASE_ENDPOINT'
,
''
)
def
process_postpay_callback
(
request
):
"""
The top level call to this module, basically
This function is handed the callback request after the customer has entered the CC info and clicked "buy"
on the external Hosted Order Page.
It is expected to verify the callback and determine if the payment was successful.
It returns {'success':bool, 'order':Order, 'error_html':str}
If successful this function must have the side effect of marking the order purchased and calling the
purchased_callbacks of the cart items.
If unsuccessful this function should not have those side effects but should try to figure out why and
return a helpful-enough error message in error_html.
"""
params
=
request
.
POST
.
dict
()
if
verify_signatures
(
params
):
try
:
result
=
payment_accepted
(
params
)
if
result
[
'accepted'
]:
# SUCCESS CASE first, rest are some sort of oddity
record_purchase
(
params
,
result
[
'order'
])
return
{
'success'
:
True
,
'order'
:
result
[
'order'
],
'error_html'
:
''
}
else
:
return
{
'success'
:
False
,
'order'
:
result
[
'order'
],
'error_html'
:
get_processor_error_html
(
params
)}
except
CCProcessorException
as
e
:
return
{
'success'
:
False
,
'order'
:
None
,
#due to exception we may not have the order
'error_html'
:
get_exception_html
(
params
,
e
)}
else
:
return
{
'success'
:
False
,
'order'
:
None
,
'error_html'
:
get_signature_error_html
(
params
)}
def
hash
(
value
):
def
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
...
@@ -48,7 +84,7 @@ def sign(params):
...
@@ -48,7 +84,7 @@ def sign(params):
return
params
return
params
def
verify
(
params
):
def
verify
_signatures
(
params
):
"""
"""
Verify the signatures accompanying the POST back from Cybersource Hosted Order Page
Verify the signatures accompanying the POST back from Cybersource Hosted Order Page
"""
"""
...
@@ -161,6 +197,18 @@ def record_purchase(params, order):
...
@@ -161,6 +197,18 @@ def record_purchase(params, order):
processor_reply_dump
=
json
.
dumps
(
params
)
processor_reply_dump
=
json
.
dumps
(
params
)
)
)
def
get_processor_error_html
(
params
):
"""Have to parse through the error codes for all the other cases"""
return
"<p>ERROR!</p>"
def
get_exception_html
(
params
,
exp
):
"""Return error HTML associated with exception"""
return
"<p>EXCEPTION!</p>"
def
get_signature_error_html
(
params
):
"""Return error HTML associated with signature failure"""
return
"<p>EXCEPTION!</p>"
CARDTYPE_MAP
=
defaultdict
(
lambda
:
"UNKNOWN"
)
CARDTYPE_MAP
=
defaultdict
(
lambda
:
"UNKNOWN"
)
CARDTYPE_MAP
.
update
(
CARDTYPE_MAP
.
update
(
...
...
lms/djangoapps/shoppingcart/processors/__init__.py
View file @
a4f5f4e4
...
@@ -8,8 +8,32 @@ module = __import__('shoppingcart.processors.' + processor_name,
...
@@ -8,8 +8,32 @@ module = __import__('shoppingcart.processors.' + processor_name,
'render_purchase_form_html'
'render_purchase_form_html'
'payment_accepted'
,
'payment_accepted'
,
'record_purchase'
,
'record_purchase'
,
'process_postpay_callback'
,
])
])
def
render_purchase_form_html
(
*
args
,
**
kwargs
):
"""
The top level call to this module to begin the purchase.
Given a shopping cart,
Renders the HTML form for display on user's browser, which POSTS to Hosted Processors
Returns the HTML as a string
"""
return
module
.
render_purchase_form_html
(
*
args
,
**
kwargs
)
def
process_postpay_callback
(
*
args
,
**
kwargs
):
"""
The top level call to this module after the purchase.
This function is handed the callback request after the customer has entered the CC info and clicked "buy"
on the external payment page.
It is expected to verify the callback and determine if the payment was successful.
It returns {'success':bool, 'order':Order, 'error_html':str}
If successful this function must have the side effect of marking the order purchased and calling the
purchased_callbacks of the cart items.
If unsuccessful this function should not have those side effects but should try to figure out why and
return a helpful-enough error message in error_html.
"""
return
module
.
process_postpay_callback
(
*
args
,
**
kwargs
)
def
sign
(
*
args
,
**
kwargs
):
def
sign
(
*
args
,
**
kwargs
):
"""
"""
Given a dict (or OrderedDict) of parameters to send to the
Given a dict (or OrderedDict) of parameters to send to the
...
@@ -30,14 +54,6 @@ def verify(*args, **kwargs):
...
@@ -30,14 +54,6 @@ def verify(*args, **kwargs):
"""
"""
return
module
.
sign
(
*
args
,
**
kwargs
)
return
module
.
sign
(
*
args
,
**
kwargs
)
def
render_purchase_form_html
(
*
args
,
**
kwargs
):
"""
Given a shopping cart,
Renders the HTML form for display on user's browser, which POSTS to Hosted Processors
Returns the HTML as a string
"""
return
module
.
render_purchase_form_html
(
*
args
,
**
kwargs
)
def
payment_accepted
(
*
args
,
**
kwargs
):
def
payment_accepted
(
*
args
,
**
kwargs
):
"""
"""
Given params returned by the CC processor, check that processor has accepted the payment
Given params returned by the CC processor, check that processor has accepted the payment
...
...
lms/djangoapps/shoppingcart/processors/exceptions.py
View file @
a4f5f4e4
...
@@ -7,5 +7,5 @@ class CCProcessorException(PaymentException):
...
@@ -7,5 +7,5 @@ class CCProcessorException(PaymentException):
class
CCProcessorDataException
(
CCProcessorException
):
class
CCProcessorDataException
(
CCProcessorException
):
pass
pass
class
CCProcessorWrongAmountException
(
Payment
Exception
):
class
CCProcessorWrongAmountException
(
CCProcessor
Exception
):
pass
pass
\ No newline at end of file
lms/djangoapps/shoppingcart/urls.py
View file @
a4f5f4e4
...
@@ -6,7 +6,6 @@ urlpatterns = patterns('shoppingcart.views', # nopep8
...
@@ -6,7 +6,6 @@ urlpatterns = patterns('shoppingcart.views', # nopep8
url
(
r'^add/course/(?P<course_id>[^/]+/[^/]+/[^/]+)/$'
,
'add_course_to_cart'
),
url
(
r'^add/course/(?P<course_id>[^/]+/[^/]+/[^/]+)/$'
,
'add_course_to_cart'
),
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'^postpay_callback/$'
,
'postpay_callback'
),
#Both the ~accept and ~reject callback pages are handled here
url
(
r'^postpay_accept_callback/$'
,
'postpay_accept_callback'
),
url
(
r'^receipt/(?P<ordernum>[0-9]*)/$'
,
'show_receipt'
),
url
(
r'^receipt/(?P<ordernum>[0-9]*)/$'
,
'show_receipt'
),
)
)
\ No newline at end of file
lms/djangoapps/shoppingcart/views.py
View file @
a4f5f4e4
import
logging
import
logging
from
django.http
import
HttpResponse
,
HttpResponseRedirect
,
HttpResponseNotFound
,
HttpResponseForbidden
,
Http404
from
django.
http
import
HttpResponse
,
HttpResponseRedirect
,
Http404
from
django.
utils.translation
import
ugettext
as
_
from
django.core.urlresolvers
import
reverse
from
django.core.urlresolvers
import
reverse
from
django.views.decorators.csrf
import
csrf_exempt
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
student.models
import
CourseEnrollment
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
from
mitxmako.shortcuts
import
render_to_response
from
mitxmako.shortcuts
import
render_to_response
from
.models
import
*
from
.models
import
*
from
.processors
import
verify
,
payment_accepted
,
render_purchase_form_html
,
record_purchase
from
.processors
import
process_postpay_callback
,
render_purchase_form_html
from
.processors.exceptions
import
CCProcessorDataException
,
CCProcessorWrongAmountException
log
=
logging
.
getLogger
(
"shoppingcart"
)
log
=
logging
.
getLogger
(
"shoppingcart"
)
...
@@ -16,20 +17,22 @@ def test(request, course_id):
...
@@ -16,20 +17,22 @@ def test(request, course_id):
item1
.
purchased_callback
(
request
.
user
.
id
)
item1
.
purchased_callback
(
request
.
user
.
id
)
return
HttpResponse
(
'OK'
)
return
HttpResponse
(
'OK'
)
@login_required
def
purchased
(
request
):
#verify() -- signatures, total cost match up, etc. Need error handling code (
# If verify fails probaly need to display a contact email/number)
cart
=
Order
.
get_cart_for_user
(
request
.
user
)
cart
.
purchase
()
return
HttpResponseRedirect
(
'/'
)
@login_required
def
add_course_to_cart
(
request
,
course_id
):
def
add_course_to_cart
(
request
,
course_id
):
if
not
request
.
user
.
is_authenticated
():
return
HttpResponseForbidden
(
_
(
'You must be logged-in to add to a shopping cart'
))
cart
=
Order
.
get_cart_for_user
(
request
.
user
)
cart
=
Order
.
get_cart_for_user
(
request
.
user
)
# TODO: Catch 500 here for course that does not exist, period
if
PaidCourseRegistration
.
part_of_order
(
cart
,
course_id
):
PaidCourseRegistration
.
add_to_order
(
cart
,
course_id
,
200
)
return
HttpResponseNotFound
(
_
(
'The course {0} is already in your cart.'
.
format
(
course_id
)))
return
HttpResponse
(
"Added"
)
if
CourseEnrollment
.
objects
.
filter
(
user
=
request
.
user
,
course_id
=
course_id
)
.
exists
():
return
HttpResponseNotFound
(
_
(
'You are already registered in course {0}.'
.
format
(
course_id
)))
try
:
PaidCourseRegistration
.
add_to_order
(
cart
,
course_id
)
except
ItemNotFoundError
:
return
HttpResponseNotFound
(
_
(
'The course you requested does not exist.'
))
if
request
.
method
==
'GET'
:
return
HttpResponseRedirect
(
reverse
(
'shoppingcart.views.show_cart'
))
return
HttpResponse
(
_
(
"Course added to cart."
))
@login_required
@login_required
def
show_cart
(
request
):
def
show_cart
(
request
):
...
@@ -62,31 +65,23 @@ def remove_item(request):
...
@@ -62,31 +65,23 @@ def remove_item(request):
return
HttpResponse
(
'OK'
)
return
HttpResponse
(
'OK'
)
@csrf_exempt
@csrf_exempt
def
postpay_
accept_
callback
(
request
):
def
postpay_callback
(
request
):
"""
"""
Receives the POST-back from processor and performs the validation and displays a receipt
Receives the POST-back from processor.
and does some other stuff
Mainly this calls the processor-specific code to check if the payment was accepted, and to record the order
if it was, and to generate an error page.
HANDLES THE ACCEPT AND REVIEW CASES
If successful this function should have the side effect of changing the "cart" into a full "order" in the DB.
The cart can then render a success page which links to receipt pages.
If unsuccessful the order will be left untouched and HTML messages giving more detailed error info will be
returned.
"""
"""
# TODO: Templates and logic for all error cases and the REVIEW CASE
result
=
process_postpay_callback
(
request
)
params
=
request
.
POST
.
dict
()
if
result
[
'success'
]:
if
verify
(
params
):
return
HttpResponseRedirect
(
reverse
(
'shoppingcart.views.show_receipt'
,
args
=
[
result
[
'order'
]
.
id
]))
try
:
result
=
payment_accepted
(
params
)
if
result
[
'accepted'
]:
# ACCEPTED CASE first
record_purchase
(
params
,
result
[
'order'
])
#render_receipt
return
HttpResponseRedirect
(
reverse
(
'shoppingcart.views.show_receipt'
,
args
=
[
result
[
'order'
]
.
id
]))
else
:
return
HttpResponse
(
"CC Processor has not accepted the payment."
)
except
CCProcessorWrongAmountException
:
return
HttpResponse
(
"Charged the wrong amount, contact our user support"
)
except
CCProcessorDataException
:
return
HttpResponse
(
"Exception: the processor returned invalid data"
)
else
:
else
:
return
HttpResponse
(
"There has been a communication problem blah blah. Not Validated"
)
return
render_to_response
(
'shoppingcart.processor_error.html'
,
{
'order'
:
result
[
'order'
],
'error_html'
:
result
[
'error_html'
]})
def
show_receipt
(
request
,
ordernum
):
def
show_receipt
(
request
,
ordernum
):
"""
"""
...
@@ -107,7 +102,7 @@ def show_receipt(request, ordernum):
...
@@ -107,7 +102,7 @@ def show_receipt(request, ordernum):
'order_items'
:
order_items
,
'order_items'
:
order_items
,
'any_refunds'
:
any_refunds
})
'any_refunds'
:
any_refunds
})
def
show_orders
(
request
):
#
def show_orders(request):
"""
"""
Displays all orders of a user
Displays all orders of a user
"""
"""
lms/templates/courseware/course_about.html
View file @
a4f5f4e4
...
@@ -59,7 +59,6 @@
...
@@ -59,7 +59,6 @@
%
endif
%
endif
})(
this
)
})(
this
)
</script>
</script>
...
@@ -93,6 +92,7 @@
...
@@ -93,6 +92,7 @@
<strong>
${_("View Courseware")}
</strong>
<strong>
${_("View Courseware")}
</strong>
</a>
</a>
%endif
%endif
%else:
%else:
<a
href=
"#"
class=
"register"
>
${_("Register for {course.display_number_with_default}").format(course=course) | h}
</a>
<a
href=
"#"
class=
"register"
>
${_("Register for {course.display_number_with_default}").format(course=course) | h}
</a>
...
...
lms/templates/shoppingcart/list.html
View file @
a4f5f4e4
...
@@ -25,7 +25,7 @@
...
@@ -25,7 +25,7 @@
</tbody>
</tbody>
</table>
</table>
<!-- <input id="back_input" type="submit" value="Return" /> -->
${form_html}
${form_html}
% else:
% else:
<p>
${_("You have selected no items for purchase.")}
</p>
<p>
${_("You have selected no items for purchase.")}
</p>
...
@@ -44,6 +44,10 @@
...
@@ -44,6 +44,10 @@
location
.
reload
(
true
);
location
.
reload
(
true
);
});
});
});
});
$
(
'#back_input'
).
click
(
function
(){
history
.
back
();
});
});
});
</script>
</script>
lms/templates/shoppingcart/receipt.html
View file @
a4f5f4e4
...
@@ -6,7 +6,11 @@
...
@@ -6,7 +6,11 @@
<
%
block
name=
"title"
><title>
${_("Receipt for Order")} ${order.id}
</title></
%
block>
<
%
block
name=
"title"
><title>
${_("Receipt for Order")} ${order.id}
</title></
%
block>
% if notification is not UNDEFINED:
<section
class=
"notification"
>
${notification}
</section>
% endif
<section
class=
"container cart-list"
>
<section
class=
"container cart-list"
>
<p><h1>
${_(settings.PLATFORM_NAME + " (" + settings.SITE_NAME + ")" + " Electronic Receipt")}
</h1></p>
<p><h1>
${_(settings.PLATFORM_NAME + " (" + settings.SITE_NAME + ")" + " Electronic Receipt")}
</h1></p>
...
...
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