diff --git a/ecommerce/extensions/payment/tests/processors/test_webhooks.py b/ecommerce/extensions/payment/tests/processors/test_webhooks.py index 3203bc82350..ebb90e13974 100644 --- a/ecommerce/extensions/payment/tests/processors/test_webhooks.py +++ b/ecommerce/extensions/payment/tests/processors/test_webhooks.py @@ -5,15 +5,19 @@ import mock from django.test import RequestFactory from oscar.core.loading import get_class, get_model +from oscar.test.factories import RangeFactory +from ecommerce.core.constants import SEAT_PRODUCT_CLASS_NAME from ecommerce.extensions.basket.utils import basket_add_payment_intent_id_attribute from ecommerce.extensions.payment.processors.webhooks import StripeWebhooksProcessor from ecommerce.extensions.payment.tests.processors.mixins import PaymentProcessorTestCaseMixin +from ecommerce.extensions.test.factories import create_basket, prepare_voucher from ecommerce.tests.factories import UserFactory from ecommerce.tests.testcases import TestCase log = logging.getLogger(__name__) +Applicator = get_class('offer.applicator', 'Applicator') BillingAddress = get_model('order', 'BillingAddress') Country = get_model('address', 'Country') OrderNumberGenerator = get_class('order.utils', 'OrderNumberGenerator') @@ -38,17 +42,20 @@ def _get_order_number_from_basket(self, basket): return OrderNumberGenerator().order_number(basket) def _build_payment_intent_data(self, basket, payment_intent_status=None): + # Apply coupon to basket so that the mocked return from Stripe has the discounted amount + Applicator().apply(basket, self.request.user, self.request) + return { "id": "pi_3OzUOMH4caH7G0X114tkIL0X", "object": "payment_intent", "status": "succeeded", - "amount": 14900, + "amount": basket.total_incl_tax, "charges": { "object": "list", "data": [{ "id": "py_3OzUOMH4caH7G0X11OOKbfIk", "object": "charge", - "amount": 14900, + "amount": basket.total_incl_tax, "billing_details": { "address": { "city": "Beverly Hills", @@ -175,3 +182,52 @@ def test_handle_webhooks_payment(self, mock_track, mock_retrieve, mock_handle_po self.basket.site, self.basket.owner, 'Payment Processor Response', properties ) mock_handle_post_order.assert_called_once() + + @mock.patch('ecommerce.extensions.checkout.mixins.EdxOrderPlacementMixin.handle_post_order') + @mock.patch('stripe.PaymentIntent.retrieve') + @mock.patch('ecommerce.extensions.payment.processors.webhooks.track_segment_event') + def test_handle_webhooks_payment_with_voucher(self, mock_track, mock_retrieve, mock_handle_post_order): + """ + Verify a payment received via Stripe webhooks is processed, an order is created and fulfilled with a coupon. + """ + # Create a basket, add a voucher without applying + basket = create_basket(product_class=SEAT_PRODUCT_CLASS_NAME) + voucher, product = prepare_voucher( + _range=RangeFactory(includes_all_products=True), + benefit_value=10, + code='test101' + ) + basket.vouchers.add(voucher) + basket.add_product(product) + + succeeded_payment_intent = self._build_payment_intent_data(basket, payment_intent_status='succeeded') + + # Need to associate the Payment Intent to the Basket + basket_add_payment_intent_id_attribute(basket, succeeded_payment_intent['id']) + + mock_retrieve.return_value = { + 'id': succeeded_payment_intent['id'], + 'client_secret': succeeded_payment_intent['client_secret'], + 'payment_method': { + 'id': succeeded_payment_intent['payment_method'], + 'object': 'payment_method', + 'billing_details': succeeded_payment_intent['charges']['data'][0]['billing_details'], + 'type': succeeded_payment_intent['charges']['data'][0]['payment_method_details']['type'] + }, + 'amount': succeeded_payment_intent['amount'] + } + self.processor_class(self.site).handle_webhooks_payment( + self.request, succeeded_payment_intent, 'affirm' + ) + properties = { + 'basket_id': basket.id, + 'processor_name': 'stripe', + 'stripe_enabled': True, + 'total': basket.total_incl_tax, + 'success': True, + 'payment_method': succeeded_payment_intent['charges']['data'][0]['payment_method_details']['type'], + } + mock_track.assert_called_once_with( + basket.site, basket.owner, 'Payment Processor Response', properties + ) + mock_handle_post_order.assert_called_once()