Commit 2a29483a by Renzo Lucioni

Merge pull request #137 from edx/renzo/relay-order-id

Relay order number to payment processors
parents 6a514304 e2aa047f
...@@ -9,7 +9,6 @@ Product = get_model('catalogue', 'Product') ...@@ -9,7 +9,6 @@ Product = get_model('catalogue', 'Product')
Selector = get_class('partner.strategy', 'Selector') Selector = get_class('partner.strategy', 'Selector')
NoShippingRequired = get_class('shipping.methods', 'NoShippingRequired') NoShippingRequired = get_class('shipping.methods', 'NoShippingRequired')
OrderNumberGenerator = get_class('order.utils', 'OrderNumberGenerator')
OrderTotalCalculator = get_class('checkout.calculators', 'OrderTotalCalculator') OrderTotalCalculator = get_class('checkout.calculators', 'OrderTotalCalculator')
...@@ -55,13 +54,12 @@ def get_order_metadata(basket): ...@@ -55,13 +54,12 @@ def get_order_metadata(basket):
dict: Containing an order number, a shipping method, a shipping charge, dict: Containing an order number, a shipping method, a shipping charge,
and a Price object representing the order total. and a Price object representing the order total.
""" """
number = OrderNumberGenerator().order_number(basket)
shipping_method = NoShippingRequired() shipping_method = NoShippingRequired()
shipping_charge = shipping_method.calculate(basket) shipping_charge = shipping_method.calculate(basket)
total = OrderTotalCalculator().calculate(basket, shipping_charge) total = OrderTotalCalculator().calculate(basket, shipping_charge)
metadata = { metadata = {
AC.KEYS.ORDER_NUMBER: number, AC.KEYS.ORDER_NUMBER: basket.order_number,
AC.KEYS.SHIPPING_METHOD: shipping_method, AC.KEYS.SHIPPING_METHOD: shipping_method,
AC.KEYS.SHIPPING_CHARGE: shipping_charge, AC.KEYS.SHIPPING_CHARGE: shipping_charge,
AC.KEYS.ORDER_TOTAL: total, AC.KEYS.ORDER_TOTAL: total,
......
default_app_config = 'ecommerce.extensions.basket.config.BasketConfig' # pragma: no cover
from oscar.apps.basket.admin import * # noqa pylint: disable=wildcard-import,unused-wildcard-import
from oscar.apps.basket import config
class BasketConfig(config.BasketConfig):
name = 'ecommerce.extensions.basket'
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
]
operations = [
migrations.CreateModel(
name='Basket',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('status', models.CharField(default='Open', max_length=128, verbose_name='Status', choices=[('Open', 'Open - currently active'), ('Merged', 'Merged - superceded by another basket'), ('Saved', 'Saved - for items to be purchased later'), ('Frozen', 'Frozen - the basket cannot be modified'), ('Submitted', 'Submitted - has been ordered at the checkout')])),
('date_created', models.DateTimeField(auto_now_add=True, verbose_name='Date created')),
('date_merged', models.DateTimeField(blank=True, verbose_name='Date merged', null=True)),
('date_submitted', models.DateTimeField(blank=True, verbose_name='Date submitted', null=True)),
],
options={
'verbose_name_plural': 'Baskets',
'verbose_name': 'Basket',
'abstract': False,
},
bases=(models.Model,),
),
migrations.CreateModel(
name='Line',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('line_reference', models.SlugField(max_length=128, verbose_name='Line Reference')),
('quantity', models.PositiveIntegerField(default=1, verbose_name='Quantity')),
('price_currency', models.CharField(default='GBP', max_length=12, verbose_name='Currency')),
('price_excl_tax', models.DecimalField(max_digits=12, decimal_places=2, verbose_name='Price excl. Tax', null=True)),
('price_incl_tax', models.DecimalField(max_digits=12, decimal_places=2, verbose_name='Price incl. Tax', null=True)),
('date_created', models.DateTimeField(auto_now_add=True, verbose_name='Date Created')),
],
options={
'verbose_name_plural': 'Basket lines',
'verbose_name': 'Basket line',
'abstract': False,
},
bases=(models.Model,),
),
migrations.CreateModel(
name='LineAttribute',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('value', models.CharField(max_length=255, verbose_name='Value')),
('line', models.ForeignKey(verbose_name='Line', related_name='attributes', to='basket.Line')),
],
options={
'verbose_name_plural': 'Line attributes',
'verbose_name': 'Line attribute',
'abstract': False,
},
bases=(models.Model,),
),
]
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
('partner', '0001_initial'),
('catalogue', '0001_initial'),
('basket', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AddField(
model_name='lineattribute',
name='option',
field=models.ForeignKey(verbose_name='Option', to='catalogue.Option'),
preserve_default=True,
),
migrations.AddField(
model_name='line',
name='basket',
field=models.ForeignKey(verbose_name='Basket', related_name='lines', to='basket.Basket'),
preserve_default=True,
),
migrations.AddField(
model_name='line',
name='product',
field=models.ForeignKey(verbose_name='Product', related_name='basket_lines', to='catalogue.Product'),
preserve_default=True,
),
migrations.AddField(
model_name='line',
name='stockrecord',
field=models.ForeignKey(related_name='basket_lines', to='partner.StockRecord'),
preserve_default=True,
),
migrations.AlterUniqueTogether(
name='line',
unique_together=set([('basket', 'line_reference')]),
),
migrations.AddField(
model_name='basket',
name='owner',
field=models.ForeignKey(verbose_name='Owner', related_name='baskets', to=settings.AUTH_USER_MODEL, null=True),
preserve_default=True,
),
]
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('voucher', '0001_initial'),
('basket', '0002_auto_20140827_1705'),
]
operations = [
migrations.AddField(
model_name='basket',
name='vouchers',
field=models.ManyToManyField(blank=True, verbose_name='Vouchers', to='voucher.Voucher', null=True),
preserve_default=True,
),
]
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
import oscar.core.utils
class Migration(migrations.Migration):
dependencies = [
('basket', '0003_basket_vouchers'),
]
operations = [
migrations.AlterField(
model_name='line',
name='price_currency',
field=models.CharField(default=oscar.core.utils.get_default_currency, max_length=12, verbose_name='Currency'),
),
]
from oscar.apps.basket.abstract_models import AbstractBasket
from oscar.core.loading import get_class
OrderNumberGenerator = get_class('order.utils', 'OrderNumberGenerator')
class Basket(AbstractBasket):
@property
def order_number(self):
return OrderNumberGenerator().order_number(self)
from oscar.apps.basket.models import * # noqa pylint: disable=wildcard-import,unused-wildcard-import
from django.test import TestCase
from oscar.core.loading import get_class
from oscar.test import factories
OrderNumberGenerator = get_class('order.utils', 'OrderNumberGenerator')
class BasketTests(TestCase):
def setUp(self):
super(BasketTests, self).setUp()
self.basket = factories.create_basket()
def test_order_number_generation(self):
"""Verify that an instance of Basket can generate its own order number."""
expected = OrderNumberGenerator().order_number(self.basket)
self.assertEqual(self.basket.order_number, expected)
"""Test Order Utility classes """ """Test Order Utility classes """
from unittest import TestCase from django.test import TestCase, override_settings
from django.test import override_settings
from oscar.test.newfactories import BasketFactory from oscar.test.newfactories import BasketFactory
from ecommerce.extensions.order.utils import OrderNumberGenerator from ecommerce.extensions.order.utils import OrderNumberGenerator
...@@ -9,15 +8,26 @@ from ecommerce.extensions.order.utils import OrderNumberGenerator ...@@ -9,15 +8,26 @@ from ecommerce.extensions.order.utils import OrderNumberGenerator
class UtilsTest(TestCase): class UtilsTest(TestCase):
"""Unit tests for the order utility functions and classes. """ """Unit tests for the order utility functions and classes. """
ORDER_NUMBER_PREFIX = "Zoidberg" ORDER_NUMBER_PREFIX = 'FOO'
@override_settings(ORDER_NUMBER_PREFIX=ORDER_NUMBER_PREFIX) @override_settings(ORDER_NUMBER_PREFIX=ORDER_NUMBER_PREFIX)
def create_order_number(self): def test_order_number_generation(self):
"""Test creating order numbers""" """
basket = BasketFactory() Verify that order numbers are generated correctly, and that they can
next_basket = BasketFactory() be converted back into basket IDs when necessary.
new_order_number = OrderNumberGenerator.order_number(basket) """
next_order_number = OrderNumberGenerator.order_number(next_basket) first_basket = BasketFactory()
self.assertIn(self.ORDER_NUMBER_PREFIX, new_order_number) second_basket = BasketFactory()
self.assertIn(self.ORDER_NUMBER_PREFIX, next_order_number)
self.assertNotEqual(new_order_number, next_order_number) first_order_number = OrderNumberGenerator().order_number(first_basket)
second_order_number = OrderNumberGenerator().order_number(second_basket)
self.assertIn(self.ORDER_NUMBER_PREFIX, first_order_number)
self.assertIn(self.ORDER_NUMBER_PREFIX, second_order_number)
self.assertNotEqual(first_order_number, second_order_number)
first_basket_id = OrderNumberGenerator().basket_id(first_order_number)
second_basket_id = OrderNumberGenerator().basket_id(second_order_number)
self.assertEqual(first_basket_id, first_basket.id)
self.assertEqual(second_basket_id, second_basket.id)
...@@ -7,22 +7,38 @@ class OrderNumberGenerator(object): ...@@ -7,22 +7,38 @@ class OrderNumberGenerator(object):
We need this as the order number is often required for payment We need this as the order number is often required for payment
which takes place before the order model has been created. which takes place before the order model has been created.
""" """
OFFSET = 100000
@staticmethod def order_number(self, basket):
def order_number(basket):
"""Create an order number with a configured prefix. """Create an order number with a configured prefix.
Creates a unique order number with a configured prefix. Creates a unique order number with a configured prefix.
Args: Arguments:
basket (Basket): Used to construct the order ID. basket (Basket): Used to construct the order ID.
Returns: Returns:
String: representation of the order 'number' with a configured prefix. str: Representation of the order 'number' with a configured prefix.
""" """
prefix = getattr(settings, 'ORDER_NUMBER_PREFIX', 'OSCR') order_id = basket.id + self.OFFSET
order_id = str(100000 + basket.id) order_number = u'{prefix}-{order_id}'.format(prefix=settings.ORDER_NUMBER_PREFIX, order_id=order_id)
return u"{prefix}-{order_id}".format(prefix=prefix, order_id=order_id)
return order_number
def basket_id(self, order_number):
"""Inverse of order number generation.
Given an order number, returns the basket ID used when generating it.
Arguments:
order_number (str): An order number.
Returns:
int: The basket ID used to generate the provided order number.
"""
order_id = int(order_number.lstrip(u'{prefix}-'.format(prefix=settings.ORDER_NUMBER_PREFIX)))
basket_id = order_id - self.OFFSET
return basket_id
""" CyberSource payment processing. """ """ CyberSource payment processing. """
import datetime import datetime
from decimal import Decimal from decimal import Decimal
import logging import logging
...@@ -19,6 +18,7 @@ from ecommerce.extensions.payment.exceptions import (InvalidSignatureError, Inva ...@@ -19,6 +18,7 @@ from ecommerce.extensions.payment.exceptions import (InvalidSignatureError, Inva
from ecommerce.extensions.payment.helpers import sign from ecommerce.extensions.payment.helpers import sign
from ecommerce.extensions.payment.processors import BasePaymentProcessor from ecommerce.extensions.payment.processors import BasePaymentProcessor
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
PaymentEvent = get_model('order', 'PaymentEvent') PaymentEvent = get_model('order', 'PaymentEvent')
...@@ -82,10 +82,11 @@ class Cybersource(BasePaymentProcessor): ...@@ -82,10 +82,11 @@ class Cybersource(BasePaymentProcessor):
u'signed_date_time': datetime.datetime.utcnow().strftime(ISO_8601_FORMAT), u'signed_date_time': datetime.datetime.utcnow().strftime(ISO_8601_FORMAT),
u'locale': self.language_code, u'locale': self.language_code,
u'transaction_type': u'sale', u'transaction_type': u'sale',
u'reference_number': unicode(basket.id), u'reference_number': basket.order_number,
u'amount': unicode(basket.total_incl_tax), u'amount': unicode(basket.total_incl_tax),
u'currency': basket.currency, u'currency': basket.currency,
u'consumer_id': basket.owner.username, u'consumer_id': basket.owner.username,
# TODO: Update once LMS receipt page is able to look up orders by order number.
u'override_custom_receipt_page': u'{}?basket_id={}'.format(self.receipt_page_url, basket.id), u'override_custom_receipt_page': u'{}?basket_id={}'.format(self.receipt_page_url, basket.id),
u'override_custom_cancel_page': self.cancel_page_url, u'override_custom_cancel_page': self.cancel_page_url,
} }
...@@ -244,7 +245,7 @@ class Cybersource(BasePaymentProcessor): ...@@ -244,7 +245,7 @@ class Cybersource(BasePaymentProcessor):
purchase_totals.currency = currency purchase_totals.currency = currency
purchase_totals.grandTotalAmount = unicode(amount) purchase_totals.grandTotalAmount = unicode(amount)
response = client.service.runTransaction(merchantID=self.merchant_id, merchantReferenceCode=order.basket.id, response = client.service.runTransaction(merchantID=self.merchant_id, merchantReferenceCode=order.number,
orderRequestToken=order_request_token, orderRequestToken=order_request_token,
ccCreditService=credit_service, ccCreditService=credit_service,
purchaseTotals=purchase_totals) purchaseTotals=purchase_totals)
......
""" PayPal payment processing. """ """ PayPal payment processing. """
from decimal import Decimal from decimal import Decimal
import logging import logging
from urlparse import urljoin from urlparse import urljoin
...@@ -13,6 +12,7 @@ import paypalrestsdk ...@@ -13,6 +12,7 @@ import paypalrestsdk
from ecommerce.extensions.order.constants import PaymentEventTypeName from ecommerce.extensions.order.constants import PaymentEventTypeName
from ecommerce.extensions.payment.processors import BasePaymentProcessor from ecommerce.extensions.payment.processors import BasePaymentProcessor
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
PaymentEvent = get_model('order', 'PaymentEvent') PaymentEvent = get_model('order', 'PaymentEvent')
...@@ -90,7 +90,7 @@ class Paypal(BasePaymentProcessor): ...@@ -90,7 +90,7 @@ class Paypal(BasePaymentProcessor):
for line in basket.all_lines() for line in basket.all_lines()
], ],
}, },
'invoice_number': unicode(basket.id), 'invoice_number': basket.order_number,
}], }],
} }
......
...@@ -12,6 +12,7 @@ from suds.sudsobject import Factory ...@@ -12,6 +12,7 @@ from suds.sudsobject import Factory
from ecommerce.extensions.payment.constants import CARD_TYPES from ecommerce.extensions.payment.constants import CARD_TYPES
from ecommerce.extensions.payment.helpers import sign from ecommerce.extensions.payment.helpers import sign
Order = get_model('order', 'Order') Order = get_model('order', 'Order')
PaymentProcessorResponse = get_model('payment', 'PaymentProcessorResponse') PaymentProcessorResponse = get_model('payment', 'PaymentProcessorResponse')
...@@ -99,7 +100,7 @@ class CybersourceMixin(object): ...@@ -99,7 +100,7 @@ class CybersourceMixin(object):
**kwargs): **kwargs):
""" Generates a dict containing the API reply fields expected to be received from CyberSource. """ """ Generates a dict containing the API reply fields expected to be received from CyberSource. """
req_reference_number = kwargs.get('req_reference_number', unicode(basket.id)) req_reference_number = kwargs.get('req_reference_number', basket.order_number)
total = unicode(basket.total_incl_tax) total = unicode(basket.total_incl_tax)
auth_amount = auth_amount or total auth_amount = auth_amount or total
notification = { notification = {
...@@ -206,7 +207,6 @@ class PaypalMixin(object): ...@@ -206,7 +207,6 @@ class PaypalMixin(object):
def mock_payment_creation_response(self, basket, state=PAYMENT_CREATION_STATE, approval_url=APPROVAL_URL, def mock_payment_creation_response(self, basket, state=PAYMENT_CREATION_STATE, approval_url=APPROVAL_URL,
find=False): find=False):
total = unicode(basket.total_incl_tax) total = unicode(basket.total_incl_tax)
payment_creation_response = { payment_creation_response = {
u'create_time': u'2015-05-04T18:18:27Z', u'create_time': u'2015-05-04T18:18:27Z',
u'id': self.PAYMENT_ID, u'id': self.PAYMENT_ID,
...@@ -249,7 +249,7 @@ class PaypalMixin(object): ...@@ -249,7 +249,7 @@ class PaypalMixin(object):
for line in basket.all_lines() for line in basket.all_lines()
], ],
}, },
u'invoice_number': unicode(basket.id), u'invoice_number': basket.order_number,
u'related_resources': [] u'related_resources': []
}], }],
u'update_time': u'2015-05-04T18:18:27Z' u'update_time': u'2015-05-04T18:18:27Z'
...@@ -275,7 +275,6 @@ class PaypalMixin(object): ...@@ -275,7 +275,6 @@ class PaypalMixin(object):
def mock_payment_execution_response(self, basket, state=PAYMENT_EXECUTION_STATE): def mock_payment_execution_response(self, basket, state=PAYMENT_EXECUTION_STATE):
total = unicode(basket.total_incl_tax) total = unicode(basket.total_incl_tax)
payment_execution_response = { payment_execution_response = {
u'create_time': u'2015-05-04T15:55:27Z', u'create_time': u'2015-05-04T15:55:27Z',
u'id': self.PAYMENT_ID, u'id': self.PAYMENT_ID,
...@@ -324,7 +323,7 @@ class PaypalMixin(object): ...@@ -324,7 +323,7 @@ class PaypalMixin(object):
for line in basket.all_lines() for line in basket.all_lines()
], ],
}, },
u'invoice_number': unicode(basket.id), u'invoice_number': basket.order_number,
u'related_resources': [{ u'related_resources': [{
u'sale': { u'sale': {
u'amount': { u'amount': {
...@@ -381,4 +380,4 @@ class PaypalMixin(object): ...@@ -381,4 +380,4 @@ class PaypalMixin(object):
mode = settings.PAYMENT_PROCESSOR_CONFIG['paypal']['mode'] mode = settings.PAYMENT_PROCESSOR_CONFIG['paypal']['mode']
root = u'https://api.sandbox.paypal.com' if mode == 'sandbox' else u'https://api.paypal.com' root = u'https://api.sandbox.paypal.com' if mode == 'sandbox' else u'https://api.paypal.com'
return root + path return urljoin(root, path)
...@@ -29,6 +29,7 @@ from ecommerce.extensions.payment.processors.paypal import Paypal ...@@ -29,6 +29,7 @@ from ecommerce.extensions.payment.processors.paypal import Paypal
from ecommerce.extensions.payment.tests.mixins import PaymentEventsMixin, CybersourceMixin, PaypalMixin from ecommerce.extensions.payment.tests.mixins import PaymentEventsMixin, CybersourceMixin, PaypalMixin
from ecommerce.extensions.refund.tests.mixins import RefundTestMixin from ecommerce.extensions.refund.tests.mixins import RefundTestMixin
PaymentEvent = get_model('order', 'PaymentEvent') PaymentEvent = get_model('order', 'PaymentEvent')
PaymentEventType = get_model('order', 'PaymentEventType') PaymentEventType = get_model('order', 'PaymentEventType')
Source = get_model('payment', 'Source') Source = get_model('payment', 'Source')
...@@ -114,7 +115,7 @@ class CybersourceTests(CybersourceMixin, PaymentProcessorTestCaseMixin, TestCase ...@@ -114,7 +115,7 @@ class CybersourceTests(CybersourceMixin, PaymentProcessorTestCaseMixin, TestCase
u'signed_date_time': self.PI_DAY.strftime(ISO_8601_FORMAT), u'signed_date_time': self.PI_DAY.strftime(ISO_8601_FORMAT),
u'locale': settings.LANGUAGE_CODE, u'locale': settings.LANGUAGE_CODE,
u'transaction_type': u'sale', u'transaction_type': u'sale',
u'reference_number': unicode(self.basket.id), u'reference_number': self.basket.order_number,
u'amount': unicode(self.basket.total_incl_tax), u'amount': unicode(self.basket.total_incl_tax),
u'currency': self.basket.currency, u'currency': self.basket.currency,
u'consumer_id': self.basket.owner.username, u'consumer_id': self.basket.owner.username,
......
""" Tests of the Payment Views. """ """ Tests of the Payment Views. """
import ddt import ddt
from django.conf import settings
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.test import TestCase from django.test import TestCase
from django.test.client import RequestFactory from django.test.client import RequestFactory
...@@ -9,7 +9,7 @@ import httpretty ...@@ -9,7 +9,7 @@ import httpretty
import mock import mock
from oscar.apps.order.exceptions import UnableToPlaceOrder from oscar.apps.order.exceptions import UnableToPlaceOrder
from oscar.apps.payment.exceptions import PaymentError from oscar.apps.payment.exceptions import PaymentError
from oscar.core.loading import get_model, get_class from oscar.core.loading import get_class, get_model
from oscar.test import factories from oscar.test import factories
from oscar.test.contextmanagers import mock_signal_receiver from oscar.test.contextmanagers import mock_signal_receiver
...@@ -19,6 +19,7 @@ from ecommerce.extensions.payment.processors.paypal import Paypal ...@@ -19,6 +19,7 @@ from ecommerce.extensions.payment.processors.paypal import Paypal
from ecommerce.extensions.payment.tests.mixins import PaymentEventsMixin, CybersourceMixin, PaypalMixin from ecommerce.extensions.payment.tests.mixins import PaymentEventsMixin, CybersourceMixin, PaypalMixin
from ecommerce.extensions.payment.views import CybersourceNotifyView, PaypalPaymentExecutionView from ecommerce.extensions.payment.views import CybersourceNotifyView, PaypalPaymentExecutionView
Basket = get_model('basket', 'Basket') Basket = get_model('basket', 'Basket')
Order = get_model('order', 'Order') Order = get_model('order', 'Order')
PaymentEvent = get_model('order', 'PaymentEvent') PaymentEvent = get_model('order', 'PaymentEvent')
...@@ -148,15 +149,15 @@ class CybersourceNotifyViewTests(CybersourceMixin, PaymentEventsMixin, TestCase) ...@@ -148,15 +149,15 @@ class CybersourceNotifyViewTests(CybersourceMixin, PaymentEventsMixin, TestCase)
self.assert_processor_response_recorded(self.processor_name, notification[u'transaction_id'], notification, self.assert_processor_response_recorded(self.processor_name, notification[u'transaction_id'], notification,
basket=self.basket) basket=self.basket)
@ddt.data('abc', '1986', '') def test_invalid_basket(self):
def test_invalid_basket(self, basket_id):
""" When payment is accepted for a non-existent basket, log an error and record the response. """ """ When payment is accepted for a non-existent basket, log an error and record the response. """
order_number = '{}-{}'.format(settings.ORDER_NUMBER_PREFIX, 101986)
notification = self.generate_notification( notification = self.generate_notification(
self.processor.secret_key, self.processor.secret_key,
self.basket, self.basket,
billing_address=self.billing_address, billing_address=self.billing_address,
req_reference_number=basket_id, req_reference_number=order_number,
) )
response = self.client.post(reverse('cybersource_notify'), notification) response = self.client.post(reverse('cybersource_notify'), notification)
......
...@@ -17,12 +17,14 @@ from ecommerce.extensions.payment.exceptions import InvalidSignatureError ...@@ -17,12 +17,14 @@ from ecommerce.extensions.payment.exceptions import InvalidSignatureError
from ecommerce.extensions.payment.processors.cybersource import Cybersource from ecommerce.extensions.payment.processors.cybersource import Cybersource
from ecommerce.extensions.payment.processors.paypal import Paypal from ecommerce.extensions.payment.processors.paypal import Paypal
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
Basket = get_model('basket', 'Basket') Basket = get_model('basket', 'Basket')
BillingAddress = get_model('order', 'BillingAddress') BillingAddress = get_model('order', 'BillingAddress')
Country = get_model('address', 'Country') Country = get_model('address', 'Country')
NoShippingRequired = get_class('shipping.methods', 'NoShippingRequired') NoShippingRequired = get_class('shipping.methods', 'NoShippingRequired')
OrderNumberGenerator = get_class('order.utils', 'OrderNumberGenerator')
OrderTotalCalculator = get_class('checkout.calculators', 'OrderTotalCalculator') OrderTotalCalculator = get_class('checkout.calculators', 'OrderTotalCalculator')
PaymentProcessorResponse = get_model('payment', 'PaymentProcessorResponse') PaymentProcessorResponse = get_model('payment', 'PaymentProcessorResponse')
...@@ -76,7 +78,8 @@ class CybersourceNotifyView(EdxOrderPlacementMixin, View): ...@@ -76,7 +78,8 @@ class CybersourceNotifyView(EdxOrderPlacementMixin, View):
try: try:
transaction_id = cybersource_response.get('transaction_id') transaction_id = cybersource_response.get('transaction_id')
basket_id = cybersource_response.get('req_reference_number') order_number = cybersource_response.get('req_reference_number')
basket_id = OrderNumberGenerator().basket_id(order_number)
basket = self._get_basket(basket_id) basket = self._get_basket(basket_id)
...@@ -113,7 +116,6 @@ class CybersourceNotifyView(EdxOrderPlacementMixin, View): ...@@ -113,7 +116,6 @@ class CybersourceNotifyView(EdxOrderPlacementMixin, View):
try: try:
user = basket.owner user = basket.owner
order_number = self.generate_order_number(basket)
self.handle_order_placement(order_number, user, basket, None, shipping_method, shipping_charge, self.handle_order_placement(order_number, user, basket, None, shipping_method, shipping_charge,
billing_address, order_total) billing_address, order_total)
except UnableToPlaceOrder: except UnableToPlaceOrder:
...@@ -160,7 +162,10 @@ class PaypalPaymentExecutionView(EdxOrderPlacementMixin, View): ...@@ -160,7 +162,10 @@ class PaypalPaymentExecutionView(EdxOrderPlacementMixin, View):
try: try:
user = basket.owner user = basket.owner
order_number = self.generate_order_number(basket) # Given a basket, order number generation is idempotent. Although we've already
# generated this order number once before, it's faster to generate it again
# than to retrieve an invoice number from PayPal.
order_number = basket.order_number
self.handle_order_placement( self.handle_order_placement(
order_number=order_number, order_number=order_number,
......
...@@ -24,6 +24,7 @@ OSCAR_APPS = [ ...@@ -24,6 +24,7 @@ OSCAR_APPS = [
'ecommerce.extensions.refund', 'ecommerce.extensions.refund',
] + get_core_apps([ ] + get_core_apps([
'ecommerce.extensions.analytics', 'ecommerce.extensions.analytics',
'ecommerce.extensions.basket',
'ecommerce.extensions.catalogue', 'ecommerce.extensions.catalogue',
'ecommerce.extensions.checkout', 'ecommerce.extensions.checkout',
'ecommerce.extensions.dashboard', 'ecommerce.extensions.dashboard',
......
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