Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
ecommerce
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
ecommerce
Commits
a5c9b891
Commit
a5c9b891
authored
May 05, 2015
by
jsa
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Report course_key and certificate_type to cybersource.
XCOM-274
parent
23bf2cd4
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
77 additions
and
2 deletions
+77
-2
ecommerce/extensions/payment/processors.py
+22
-1
ecommerce/extensions/payment/tests/test_processors.py
+55
-1
No files found.
ecommerce/extensions/payment/processors.py
View file @
a5c9b891
...
@@ -21,6 +21,7 @@ logger = logging.getLogger(__name__)
...
@@ -21,6 +21,7 @@ logger = logging.getLogger(__name__)
PaymentEvent
=
get_model
(
'order'
,
'PaymentEvent'
)
PaymentEvent
=
get_model
(
'order'
,
'PaymentEvent'
)
PaymentEventType
=
get_model
(
'order'
,
'PaymentEventType'
)
PaymentEventType
=
get_model
(
'order'
,
'PaymentEventType'
)
PaymentProcessorResponse
=
get_model
(
'payment'
,
'PaymentProcessorResponse'
)
PaymentProcessorResponse
=
get_model
(
'payment'
,
'PaymentProcessorResponse'
)
ProductClass
=
get_model
(
'catalogue'
,
'ProductClass'
)
Source
=
get_model
(
'payment'
,
'Source'
)
Source
=
get_model
(
'payment'
,
'Source'
)
SourceType
=
get_model
(
'payment'
,
'SourceType'
)
SourceType
=
get_model
(
'payment'
,
'SourceType'
)
...
@@ -149,7 +150,13 @@ class Cybersource(BasePaymentProcessor):
...
@@ -149,7 +150,13 @@ class Cybersource(BasePaymentProcessor):
u'override_custom_cancel_page'
:
self
.
cancel_page_url
,
u'override_custom_cancel_page'
:
self
.
cancel_page_url
,
}
}
# TODO Include edX-specific data (e.g. course_id, seat type)
# XCOM-274: when internal reporting across all processors is
# operational, these custom fields will no longer be needed and should
# be removed.
single_seat
=
self
.
get_single_seat
(
basket
)
if
single_seat
:
parameters
[
u'merchant_defined_data1'
]
=
single_seat
.
attr
.
course_key
parameters
[
u'merchant_defined_data2'
]
=
single_seat
.
attr
.
certificate_type
# Sign all fields
# Sign all fields
signed_field_names
=
parameters
.
keys
()
signed_field_names
=
parameters
.
keys
()
...
@@ -158,6 +165,20 @@ class Cybersource(BasePaymentProcessor):
...
@@ -158,6 +165,20 @@ class Cybersource(BasePaymentProcessor):
return
parameters
return
parameters
@staticmethod
def
get_single_seat
(
basket
):
"""
Return the first product encountered in the basket with the product
class of 'seat'. Return None if no such products were found.
"""
try
:
seat_class
=
ProductClass
.
objects
.
get
(
slug
=
'seat'
)
except
ProductClass
.
DoesNotExist
:
# this occurs in test configurations where the seat product class is not in use
return
None
products
=
[
line
.
product
for
line
in
basket
.
lines
.
all
()
if
line
.
product
.
product_class
==
seat_class
]
return
products
[
0
]
if
products
else
None
def
handle_processor_response
(
self
,
response
,
basket
=
None
):
def
handle_processor_response
(
self
,
response
,
basket
=
None
):
"""
"""
Handle a response from the payment processor.
Handle a response from the payment processor.
...
...
ecommerce/extensions/payment/tests/test_processors.py
View file @
a5c9b891
...
@@ -31,10 +31,26 @@ class PaymentProcessorTestCaseMixin(PaymentEventsMixin):
...
@@ -31,10 +31,26 @@ class PaymentProcessorTestCaseMixin(PaymentEventsMixin):
# This value is used to test the NAME attribute on the processor.
# This value is used to test the NAME attribute on the processor.
processor_name
=
None
processor_name
=
None
COURSE_KEY
=
'test-course-key'
CERTIFICATE_TYPE
=
'test-certificate-type'
def
setUp
(
self
):
def
setUp
(
self
):
super
(
PaymentProcessorTestCaseMixin
,
self
)
.
setUp
()
super
(
PaymentProcessorTestCaseMixin
,
self
)
.
setUp
()
# certain logic and tests expect to find products with class 'seat' in
# baskets, so that's done explicitly in this setup.
self
.
product_class
=
factories
.
ProductClassFactory
(
slug
=
'seat'
,
requires_shipping
=
False
,
track_stock
=
False
)
self
.
product_class
.
save
()
factories
.
ProductAttributeFactory
(
code
=
'course_key'
,
product_class
=
self
.
product_class
,
type
=
"text"
)
.
save
()
factories
.
ProductAttributeFactory
(
code
=
'certificate_type'
,
product_class
=
self
.
product_class
,
type
=
"text"
)
.
save
()
self
.
product
=
factories
.
ProductFactory
(
upc
=
'dummy-upc'
,
title
=
'dummy-title'
,
product_class
=
self
.
product_class
)
self
.
product
.
attr
.
course_key
=
self
.
COURSE_KEY
self
.
product
.
attr
.
certificate_type
=
self
.
CERTIFICATE_TYPE
self
.
product
.
save
()
self
.
processor
=
self
.
processor_class
()
# pylint: disable=not-callable
self
.
processor
=
self
.
processor_class
()
# pylint: disable=not-callable
self
.
basket
=
factories
.
create_basket
()
self
.
basket
=
factories
.
create_basket
(
empty
=
True
)
self
.
basket
.
add_product
(
self
.
product
)
self
.
basket
.
owner
=
factories
.
UserFactory
()
self
.
basket
.
owner
=
factories
.
UserFactory
()
self
.
basket
.
save
()
self
.
basket
.
save
()
...
@@ -94,6 +110,8 @@ class CybersourceTests(CybersourceMixin, PaymentProcessorTestCaseMixin, TestCase
...
@@ -94,6 +110,8 @@ class CybersourceTests(CybersourceMixin, PaymentProcessorTestCaseMixin, TestCase
u'consumer_id'
:
self
.
basket
.
owner
.
username
,
u'consumer_id'
:
self
.
basket
.
owner
.
username
,
u'override_custom_receipt_page'
:
u'{}?basket_id={}'
.
format
(
self
.
processor
.
receipt_page_url
,
self
.
basket
.
id
),
u'override_custom_receipt_page'
:
u'{}?basket_id={}'
.
format
(
self
.
processor
.
receipt_page_url
,
self
.
basket
.
id
),
u'override_custom_cancel_page'
:
self
.
processor
.
cancel_page_url
,
u'override_custom_cancel_page'
:
self
.
processor
.
cancel_page_url
,
u'merchant_defined_data1'
:
self
.
COURSE_KEY
,
u'merchant_defined_data2'
:
self
.
CERTIFICATE_TYPE
,
}
}
signed_field_names
=
expected
.
keys
()
+
[
u'transaction_uuid'
]
signed_field_names
=
expected
.
keys
()
+
[
u'transaction_uuid'
]
...
@@ -173,3 +191,39 @@ class CybersourceTests(CybersourceMixin, PaymentProcessorTestCaseMixin, TestCase
...
@@ -173,3 +191,39 @@ class CybersourceTests(CybersourceMixin, PaymentProcessorTestCaseMixin, TestCase
response
=
self
.
generate_notification
(
self
.
processor
.
secret_key
,
self
.
basket
,
auth_amount
=
u'0.00'
)
response
=
self
.
generate_notification
(
self
.
processor
.
secret_key
,
self
.
basket
,
auth_amount
=
u'0.00'
)
self
.
assertRaises
(
PartialAuthorizationError
,
self
.
processor
.
handle_processor_response
,
response
,
self
.
assertRaises
(
PartialAuthorizationError
,
self
.
processor
.
handle_processor_response
,
response
,
basket
=
self
.
basket
)
basket
=
self
.
basket
)
def
test_get_single_seat
(
self
):
"""
The single-seat helper for cybersource reporting should correctly
and return the first 'seat' product encountered in a basket.
"""
get_single_seat
=
processors
.
Cybersource
.
get_single_seat
# finds the seat when it's the only product in the basket.
self
.
assertEqual
(
get_single_seat
(
self
.
basket
),
self
.
product
)
# finds the first seat added, when there's more than one.
basket
=
factories
.
create_basket
(
empty
=
True
)
other_seat
=
factories
.
ProductFactory
(
upc
=
'other-upc'
,
title
=
'other-title'
,
product_class
=
self
.
product_class
)
other_seat
.
save
()
basket
.
add_product
(
self
.
product
)
basket
.
add_product
(
other_seat
)
self
.
assertEqual
(
get_single_seat
(
basket
),
self
.
product
)
# finds the seat when there's a mixture of product classes.
basket
=
factories
.
create_basket
(
empty
=
True
)
other_product
=
factories
.
ProductFactory
(
upc
=
'not-a-seat'
,
title
=
'not-a-seat'
)
other_product
.
save
()
basket
.
add_product
(
other_product
)
basket
.
add_product
(
self
.
product
)
self
.
assertEqual
(
get_single_seat
(
basket
),
self
.
product
)
self
.
assertNotEqual
(
get_single_seat
(
basket
),
other_product
)
# returns None when there's no seats.
basket
=
factories
.
create_basket
(
empty
=
True
)
basket
.
add_product
(
other_product
)
self
.
assertIsNone
(
get_single_seat
(
basket
))
# returns None for an empty basket.
basket
=
factories
.
create_basket
(
empty
=
True
)
self
.
assertIsNone
(
get_single_seat
(
basket
))
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