def test_retrieve_error(self): self.retrieve_product_mock.side_effect = InvalidRequestError( "message", param="prod_id" ) with self.assertRaises(InvalidRequestError): vendor.retrieve_stripe_product("prod_test1")
def get_subscription_type(self, new_product: Dict[str, Any], previous_product: Dict[str, Any]) -> str: """ Determine if new product is an upgrade or downgrade :param new_product: :param previous_product: :return: """ logger.debug("get sub meta", new_product=new_product, previous_product=previous_product) new_product_metadata = new_product.get("metadata", None) new_product_set_order = new_product_metadata.get("productSetOrder", 0) previous_product_metadata = previous_product.get("metadata", None) previous_product_set_order = previous_product_metadata.get( "productSetOrder", 0) logger.debug( "get subscription type", new_product_set_order=new_product_set_order, previous_product_set_order=previous_product_set_order, ) if new_product_set_order > previous_product_set_order: return "customer.subscription.upgrade" elif previous_product_set_order > new_product_set_order: return "customer.subscription.downgrade" else: raise InvalidRequestError(message="Not valid subscription change", param="invalid_change")
def test_get_user_id_fetch_error(self): self.mock_customer.side_effect = InvalidRequestError( message="invalid data", param="bad data") with self.assertRaises(InvalidRequestError): StripeCustomerSubscriptionDeleted( self.subscription_deleted_event).get_user_id("cust_123")
def run(self): try: logger.info("customer source expiring") customer_id = self.payload.data.object.customer updated_customer = stripe.Customer.retrieve(id=customer_id) email = updated_customer.email nicknames = list() for subs in updated_customer.subscriptions["data"]: if subs["status"] in ["active", "trialing"]: nicknames.append(subs["plan"]["nickname"]) data = self.create_data( email=email, nickname=nicknames[0], customer_id=self.payload.data.object.customer, last4=self.payload.data.object.last4, brand=self.payload.data.object.brand, exp_month=self.payload.data.object.exp_month, exp_year=self.payload.data.object.exp_year, ) routes = [StaticRoutes.SALESFORCE_ROUTE] self.send_to_routes(routes, json.dumps(data)) except InvalidRequestError as e: logger.error("Unable to find customer", error=e) raise InvalidRequestError(message="Unable to find customer", param=str(e))
def run(self): logger.info("payment intent succeeded", payload=self.payload) try: invoice_id = self.payload.data.object.invoice invoice = stripe.Invoice.retrieve(id=invoice_id) subscription_id = invoice.subscription period_start = invoice.period_start period_end = invoice.period_end logger.info("subscription id", subscription_id=subscription_id) charges = self.payload.data.object.charges data = self.create_data( subscription_id=subscription_id, period_end=period_end, period_start=period_start, brand=charges.data[0].payment_method_details.card.brand, last4=charges.data[0].payment_method_details.card.last4, exp_month=charges.data[0].payment_method_details.card. exp_month, exp_year=charges.data[0].payment_method_details.card.exp_year, charge_id=charges.data[0].id, invoice_id=self.payload.data.object.invoice, customer_id=self.payload.data.object.customer, amount_paid=sum( [p.amount - p.amount_refunded for p in charges.data]), created=self.payload.data.object.created, currency=self.payload.data.object.currency, ) routes = [StaticRoutes.SALESFORCE_ROUTE] self.send_to_routes(routes, json.dumps(data)) except InvalidRequestError as e: logger.error("Unable to find invoice", error=e) raise InvalidRequestError(message="Unable to find invoice", param=str(e))
def test_run_customer_not_found(self): self.mock_customer.side_effect = InvalidRequestError( message="message", param="param" ) self.mock_run_pipeline = None with self.assertRaises(InvalidRequestError): StripeCustomerSourceExpiring(self.source_expiring_event).run()
def run(self): logger.info("customer subscription deleted", payload=self.payload) try: customer_id = self.payload.data.object.customer updated_customer = stripe.Customer.retrieve(id=customer_id) user_id = updated_customer.metadata.get("userid") except InvalidRequestError as e: logger.error("Unable to find customer", error=e) raise InvalidRequestError(message="Unable to find customer", param=str(e)) if user_id: data = dict( active=self.is_active_or_trialing, subscriptionId=self.payload.data.object.id, productName=self.payload.data.object.plan.nickname, eventId=self.payload.id, # required by FxA event_id=self.payload.id, # required by SubHub eventCreatedAt=self.payload.created, messageCreatedAt=int(time.time()), ) logger.info("customer subscription deleted", data=data) routes = [StaticRoutes.FIREFOX_ROUTE] self.send_to_routes(routes, json.dumps(data)) else: logger.error( "customer subscription deleted no userid", error=self.payload.object.customer, user_id=user_id, ) raise ClientError( f"userid is None for customer {self.payload.object.customer}")
def test_sync_fail(self, stripe_customer_create_mock, api_retrieve_mock, _sync_mock): _sync_mock.side_effect = InvalidRequestError("No such customer:", "blah") with capture_stdout() as stdout: sync_subscriber(self.user) self.assertEqual("ERROR: No such customer:", stdout.getvalue().strip())
def test_cancel_already_canceled( self, subscription_retrieve_mock, product_retrieve_mock, subscription_delete_mock, ): subscription_delete_mock.side_effect = InvalidRequestError( "No such subscription: sub_xxxx", "blah") subscription_fake = deepcopy(FAKE_SUBSCRIPTION) subscription = Subscription.sync_from_stripe_data(subscription_fake) self.assertEqual( Subscription.objects.filter(status="canceled").count(), 0) subscription.cancel(at_period_end=False) self.assertEqual( Subscription.objects.filter(status="canceled").count(), 1) self.assert_fks( subscription, expected_blank_fks={ "djstripe.Customer.coupon", "djstripe.Subscription.pending_setup_intent", }, )
def test_create_payload_charge_fetch_error(self): self.mock_invoice.return_value = self.invoice self.mock_charge.side_effect = InvalidRequestError( message="invalid data", param="bad data") with self.assertRaises(InvalidRequestError): StripeCustomerSubscriptionCreated( self.subscription_created_event).create_payload("cust_123")
def test_retry_unpaid_invoices_unexpected_exception(self, invoice_retry_mock, invoice_list_mock, charge_retrieve_mock, customer_retrieve_mock, subscription_retrive_mock, default_account_mock): default_account_mock.return_value = self.account invoice_retry_mock.side_effect = InvalidRequestError("This should fail!", "blah") with self.assertRaisesMessage(InvalidRequestError, "This should fail!"): self.customer.retry_unpaid_invoices()
def test_customer_delete_raises_unexpected_exception(self, customer_retrieve_mock): customer_retrieve_mock.side_effect = InvalidRequestError("Unexpected Exception", "blah") with self.assertRaisesMessage(InvalidRequestError, "Unexpected Exception"): self.customer.purge() customer_retrieve_mock.assert_called_once_with(id=self.customer.stripe_id, api_key=settings.STRIPE_SECRET_KEY, expand=['default_source'])
def test_send_invoice_failure(self, invoice_create_mock): invoice_create_mock.side_effect = InvalidRequestError( "Invoice creation failed.", "blah") return_status = self.customer.send_invoice() self.assertFalse(return_status) invoice_create_mock.assert_called_once_with(api_key=STRIPE_SECRET_KEY, customer=self.customer.id)
def _validate_origin_system(origin_system: str): """ This function validates a request's origin_system to validate that is permitted. This allows us to ensure that callers are permitted by configuration to interact with this application. :param origin_system: The originating system in Mozilla """ if origin_system not in CFG.ALLOWED_ORIGIN_SYSTEMS: msg = f"origin_system={origin_system} not one of allowed origin system values, please contact a system administrator in the #subscription-platform channel." raise InvalidRequestError(message=msg, param=str(origin_system))
def test_cancel_error_in_cancel(self, subscription_delete_mock): subscription_delete_mock.side_effect = InvalidRequestError( "Unexpected error", "blah") subscription_fake = deepcopy(FAKE_SUBSCRIPTION) subscription = Subscription.sync_from_stripe_data(subscription_fake) with self.assertRaises(InvalidRequestError): subscription.cancel()
def test_detach(self): original_detach = PaymentMethodDict.detach def mocked_detach(*args, **kwargs): return original_detach(*args, **kwargs) with patch( "stripe.PaymentMethod.retrieve", return_value=deepcopy(FAKE_PAYMENT_METHOD_I), autospec=True, ): PaymentMethod.sync_from_stripe_data( deepcopy(FAKE_PAYMENT_METHOD_I)) self.assertEqual(1, self.customer.payment_methods.count()) payment_method = self.customer.payment_methods.first() with patch("tests.PaymentMethodDict.detach", side_effect=mocked_detach, autospec=True) as mock_detach, patch( "stripe.PaymentMethod.retrieve", return_value=deepcopy(FAKE_PAYMENT_METHOD_I), autospec=True, ): self.assertTrue(payment_method.detach()) self.assertEqual(0, self.customer.payment_methods.count()) self.assertIsNone(self.customer.default_payment_method) self.assertIsNone(payment_method.customer) if sys.version_info >= (3, 6): # this mock isn't working on py34, py35, but it's not strictly necessary # for the test mock_detach.assert_called() self.assert_fks(payment_method, expected_blank_fks={"djstripe.PaymentMethod.customer"}) with patch( "tests.PaymentMethodDict.detach", side_effect=InvalidRequestError( message="A source must be attached to a customer to be used " "as a `payment_method`", param="payment_method", ), autospec=True, ) as mock_detach, patch( "stripe.PaymentMethod.retrieve", return_value=deepcopy(FAKE_PAYMENT_METHOD_I), autospec=True, ) as payment_method_retrieve_mock: payment_method_retrieve_mock.return_value["customer"] = None self.assertFalse(payment_method.detach(), "Second call to detach should return false")
def _validate_origin_system(origin_system: str): """ This function validates a request's origin_system to validate that is permitted. This allows us to ensure that callers are permitted by configuration to interact with this application. :param origin_system: The originating system in Mozilla """ if origin_system not in CFG.ALLOWED_ORIGIN_SYSTEMS: msg = f"origin_system={origin_system} not one of {CFG.ALLOWED_ORIGIN_SYSTEMS}" raise InvalidRequestError(message=msg, param=str(origin_system))
def test_create_payload_error(self): self.mock_product.side_effect = InvalidRequestError( message="invalid data", param="bad data") with self.assertRaises(InvalidRequestError): StripeCustomerSubscriptionUpdated( self.subscription_updated_event_no_match).create_payload( event_type="event.type", user_id="user_123", previous_plan=None)
def test_retry_unpaid_invoices_expected_exception(self, invoice_retry_mock, invoice_list_mock, charge_retrieve_mock, customer_retrieve_mock, subscription_retrive_mock, default_account_mock): default_account_mock.return_value = self.account invoice_retry_mock.side_effect = InvalidRequestError("Invoice is already paid", "blah") try: self.customer.retry_unpaid_invoices() except: self.fail("Exception was unexpectedly raised.")
def test_find_product_not_found(self): self.mock_stripe_retrieve_product.side_effect = InvalidRequestError( "message", param="prod_id", http_status=404) with self.assertRaises(EntityNotFoundError) as e: find_stripe_product("prod_test1") error = e.exception assert error.status_code == 404 assert error.to_dict() == dict(message="Product not found", errno=4002)
def create_customer( subhub_account: SubHubAccount, user_id: str, email: str, source_token: str, origin_system: str, display_name: str, ) -> Customer: _validate_origin_system(origin_system) # First search Stripe to ensure we don't have an unlinked Stripe record # already in Stripe customer = None customers = Customer.list(email=email) for possible_customer in customers.data: if possible_customer.email == email: # If the userid doesn't match, the system is damaged. if possible_customer.metadata.get("userid") != user_id: raise ServerError("customer email exists but userid mismatch") customer = possible_customer # If we have a mis-match on the source_token, overwrite with the # new one. if customer.default_source != source_token: Customer.modify(customer.id, source=source_token) break # No existing Stripe customer, create one. if not customer: try: customer = Customer.create( source=source_token, email=email, description=user_id, name=display_name, metadata={"userid": user_id}, ) except InvalidRequestError as e: logger.error("create customer error", error=e) raise InvalidRequestError( message="Unable to create customer.", param=str(e) ) # Link the Stripe customer to the origin system id db_account = subhub_account.new_user( uid=user_id, origin_system=origin_system, cust_id=customer.id ) if not subhub_account.save_user(db_account): # Clean-up the Stripe customer record since we can't link it Customer.delete(customer.id) e = IntermittentError("error saving db record") logger.error("unable to save user or link it", error=e) raise e return customer
def test_sync_customers_with_test_customer(self, SyncChargesMock, SyncInvoicesMock, SyncMock, RetrieveMock): user2 = get_user_model().objects.create_user(username="******") get_user_model().objects.create_user(username="******") Customer.objects.create(stripe_id="cus_XXXXX", user=self.user) Customer.objects.create(stripe_id="cus_YYYYY", user=user2) SyncMock.side_effect = InvalidRequestError('Unknown customer', None, http_status=404) management.call_command("sync_customers") self.assertEqual(SyncChargesMock.call_count, 0) self.assertEqual(SyncInvoicesMock.call_count, 0) self.assertEqual(SyncMock.call_count, 2)
def test_remove_unexpected_exception(self, customer_retrieve_mock, card_delete_mock): stripe_card = Card._api_create(customer=self.customer, source=FAKE_CARD["id"]) Card.sync_from_stripe_data(stripe_card) card_delete_mock.side_effect = InvalidRequestError("Unexpected Exception", "blah") self.assertEqual(1, self.customer.sources.count()) card = self.customer.sources.all()[0] with self.assertRaisesMessage(InvalidRequestError, "Unexpected Exception"): card.remove()
def test_cancel_error_in_cancel(self, subscription_delete_mock): subscription_delete_mock.side_effect = InvalidRequestError("Unexpected error", "blah") subscription_fake = deepcopy(FAKE_SUBSCRIPTION) subscription = Subscription.sync_from_stripe_data(subscription_fake) with self.assertRaises(InvalidRequestError): subscription.cancel(at_period_end=False) self.assert_fks( subscription, expected_blank_fks={"djstripe.Customer.coupon", "djstripe.Plan.product"}, )
def test_cancel_already_canceled(self, subscription_retrieve_mock, subscription_delete_mock): subscription_delete_mock.side_effect = InvalidRequestError( "No such subscription: sub_xxxx", "blah") subscription_fake = deepcopy(FAKE_SUBSCRIPTION) subscription = Subscription.sync_from_stripe_data(subscription_fake) self.assertEqual( Subscription.objects.filter(status="canceled").count(), 0) subscription.cancel() self.assertEqual( Subscription.objects.filter(status="canceled").count(), 1)
def test_cancel_error_in_cancel(self, product_retrieve_mock, subscription_delete_mock): subscription_delete_mock.side_effect = InvalidRequestError( "Unexpected error", "blah") subscription_fake = deepcopy(FAKE_SUBSCRIPTION) subscription = Subscription.sync_from_stripe_data(subscription_fake) with self.assertRaises(InvalidRequestError): subscription.cancel(at_period_end=False) self.assert_fks(subscription, expected_blank_fks=self.default_expected_blank_fks)
def test_remove_no_such_customer(self, customer_retrieve_mock, card_delete_mock): stripe_card = Card._api_create(customer=self.customer, source=FAKE_CARD["id"]) Card.sync_from_stripe_data(stripe_card) card_delete_mock.side_effect = InvalidRequestError("No such customer:", "blah") self.assertEqual(1, self.customer.sources.count()) card = self.customer.sources.all()[0] card.remove() self.assertEqual(0, self.customer.sources.count()) self.assertTrue(card_delete_mock.called)
def test_customer_purge_raises_customer_exception(self, customer_retrieve_mock): customer_retrieve_mock.side_effect = InvalidRequestError("No such customer:", "blah") self.customer.purge() customer = Customer.objects.get(stripe_id=self.customer.stripe_id) self.assertTrue(customer.subscriber is None) self.assertTrue(customer.default_source is None) self.assertTrue(not customer.sources.all()) self.assertTrue(get_user_model().objects.filter(pk=self.user.pk).exists()) customer_retrieve_mock.assert_called_with(id=self.customer.stripe_id, api_key=settings.STRIPE_SECRET_KEY, expand=['default_source']) self.assertEqual(3, customer_retrieve_mock.call_count)
def subscribe_customer(customer: Customer, plan_id: str) -> Subscription: """ Subscribe Customer to Plan :param customer: :param plan: :return: Subscription Object """ try: sub = Subscription.create(customer=customer, items=[{"plan": plan_id}]) return sub except Exception as e: logger.error("sub error", error=e) raise InvalidRequestError("Unable to create plan", param=plan_id)
def test_plan_manager_error(plan_create_mock): plan_create_mock.side_effect = InvalidRequestError(param="id", message="no!") plan = models.Plan( name="Gold Plan", amount=100, interval=models.Plan.MONTHLY, name_on_invoice="Gold Plan Subscription", statement_descriptor="GOLDPLANSUB", ) with pytest.raises(ValidationError) as err: plan.save() assert err.value.message_dict == {"id": ["no!"]}