Ejemplo n.º 1
0
class RestSubscriptionTest(APITestCase):
    """
    Test the REST api for subscriptions.
    """
    def setUp(self):
        self.url = reverse("rest_djstripe:subscription")
        self.user = get_user_model().objects.create_user(
            username="******",
            email="*****@*****.**",
            password="******"
        )

        self.assertTrue(self.client.login(username="******", password="******"))

    @patch("djstripe.models.Customer.subscribe", autospec=True)
    @patch("djstripe.models.Customer.update_card", autospec=True)
    @patch("stripe.Customer.create", return_value=PropertyMock(id="cus_xxx1234567890"))
    def test_create_subscription(self, stripe_customer_mock, update_card_mock, subscribe_mock):
        self.assertEqual(0, Customer.objects.count())
        data = {
            "plan": "test0",
            "stripe_token": "cake",
        }
        response = self.client.post(self.url, data)
        self.assertEqual(1, Customer.objects.count())
        update_card_mock.assert_called_once_with(self.user.customer, "cake")
        subscribe_mock.assert_called_once_with(self.user.customer, "test0")
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(response.data, data)

    @patch("djstripe.models.Customer.subscribe", autospec=True)
    @patch("djstripe.models.Customer.update_card", autospec=True)
    @patch("stripe.Customer.create", return_value=PropertyMock(id="cus_xxx1234567890"))
    def test_create_subscription_exception(self, stripe_customer_mock, update_card_mock, subscribe_mock):
        e = Exception
        subscribe_mock.side_effect = e
        data = {
            "plan": "test0",
            "stripe_token": "cake",
        }
        response = self.client.post(self.url, data)
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

    def test_get_no_content_for_subscription(self):
        response = self.client.get(self.url)
        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)

    def test_get_subscription(self):
        fake_customer = Customer.objects.create(
            stripe_id="cus_xxx1234567890",
            subscriber=self.user
        )
        CurrentSubscription.objects.create(
            customer=fake_customer,
            plan="test",
            quantity=1,
            start=timezone.now(),
            amount=Decimal(25.00),
            status="active",
        )

        response = self.client.get(self.url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data["plan"], "test")
        self.assertEqual(response.data['status'], 'active')
        self.assertEqual(response.data['cancel_at_period_end'], False)

    @patch("djstripe.models.Customer.cancel_subscription", return_value=CurrentSubscription(status=CurrentSubscription.STATUS_ACTIVE))
    @patch("djstripe.models.Customer.current_subscription", new_callable=PropertyMock, return_value=CurrentSubscription(plan="test", amount=Decimal(25.00), status="active"))
    @patch("djstripe.models.Customer.subscribe", autospec=True)
    def test_cancel_subscription(self, subscribe_mock, stripe_create_customer_mock, cancel_subscription_mock):
        fake_customer = Customer.objects.create(
            stripe_id="cus_xxx1234567890",
            subscriber=self.user
        )
        CurrentSubscription.objects.create(
            customer=fake_customer,
            plan="test",
            quantity=1,
            start=timezone.now(),
            amount=Decimal(25.00),
            status="active",
        )
        self.assertEqual(1, CurrentSubscription.objects.count())

        response = self.client.delete(self.url)
        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
        # Cancelled means flagged as cancelled, so it should still be there
        self.assertEqual(1, CurrentSubscription.objects.count())

        cancel_subscription_mock.assert_called_once_with(
            at_period_end=djstripe_settings.CANCELLATION_AT_PERIOD_END
        )
        self.assertTrue(self.user.is_authenticated())

    def test_cancel_subscription_exception(self):
        response = self.client.delete(self.url)
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

    def test_create_subscription_incorrect_data(self):
        self.assertEqual(0, Customer.objects.count())
        data = {
            "foo": "bar",
        }
        response = self.client.post(self.url, data)
        self.assertEqual(0, Customer.objects.count())
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
Ejemplo n.º 2
0
class ChangePlanViewTest(TestCase):
    @patch("stripe.Customer.create",
           return_value=PropertyMock(id="cus_xxx1234567890_01"))
    def setUp(self, stripe_customer_mock):
        self.url = reverse("djstripe:change_plan")
        self.user1 = get_user_model().objects.create_user(
            username="******", email="*****@*****.**", password="******")
        self.user2 = get_user_model().objects.create_user(
            username="******", email="*****@*****.**", password="******")

        Customer.get_or_create(subscriber=self.user1)

    @patch("stripe.Customer.create",
           return_value=PropertyMock(id="cus_xxx1234567890"))
    def test_post_form_invalid(self, stripe_customer_mock):
        self.assertTrue(self.client.login(username="******",
                                          password="******"))
        response = self.client.post(self.url)
        self.assertEqual(200, response.status_code)
        self.assertIn("plan", response.context["form"].errors)
        self.assertIn("This field is required.",
                      response.context["form"].errors["plan"])

    @patch("stripe.Customer.create",
           return_value=PropertyMock(id="cus_xxx1234567890_02"))
    def test_post_new_sub_no_proration(self, stripe_customer_mock):
        self.assertTrue(self.client.login(username="******",
                                          password="******"))
        response = self.client.post(self.url, {"plan": "test0"})
        self.assertEqual(200, response.status_code)
        self.assertIn("form", response.context)
        self.assertIn(
            "You must already be subscribed to a plan before you can change it.",
            response.context["form"].errors["__all__"])

    @patch("djstripe.models.Customer.current_subscription",
           new_callable=PropertyMock,
           return_value=CurrentSubscription(plan="test",
                                            amount=Decimal(25.00)))
    @patch("djstripe.models.Customer.subscribe", autospec=True)
    def test_change_sub_no_proration(self, subscribe_mock,
                                     current_subscription_mock):
        self.assertTrue(self.client.login(username="******",
                                          password="******"))
        response = self.client.post(self.url, {"plan": "test0"})
        self.assertRedirects(response, reverse("djstripe:history"))

        subscribe_mock.assert_called_once_with(self.user1.customer, "test0")

    @patch("djstripe.views.PRORATION_POLICY_FOR_UPGRADES", return_value=True)
    @patch("djstripe.models.Customer.current_subscription",
           new_callable=PropertyMock,
           return_value=CurrentSubscription(plan="test",
                                            amount=Decimal(25.00)))
    @patch("djstripe.models.Customer.subscribe", autospec=True)
    def test_change_sub_with_proration_downgrade(self, subscribe_mock,
                                                 current_subscription_mock,
                                                 proration_policy_mock):
        self.assertTrue(self.client.login(username="******",
                                          password="******"))
        response = self.client.post(self.url, {"plan": "test0"})
        self.assertRedirects(response, reverse("djstripe:history"))

        subscribe_mock.assert_called_once_with(self.user1.customer, "test0")

    @patch("djstripe.views.PRORATION_POLICY_FOR_UPGRADES", return_value=True)
    @patch("djstripe.models.Customer.current_subscription",
           new_callable=PropertyMock,
           return_value=CurrentSubscription(plan="test",
                                            amount=Decimal(25.00)))
    @patch("djstripe.models.Customer.subscribe", autospec=True)
    def test_change_sub_with_proration_upgrade(self, subscribe_mock,
                                               current_subscription_mock,
                                               proration_policy_mock):
        self.assertTrue(self.client.login(username="******",
                                          password="******"))

        response = self.client.post(self.url, {"plan": "test2"})
        self.assertRedirects(response, reverse("djstripe:history"))

        subscribe_mock.assert_called_once_with(self.user1.customer,
                                               "test2",
                                               prorate=True)

    @patch("djstripe.views.PRORATION_POLICY_FOR_UPGRADES", return_value=True)
    @patch("djstripe.models.Customer.current_subscription",
           new_callable=PropertyMock,
           return_value=CurrentSubscription(plan="test",
                                            amount=Decimal(25.00)))
    @patch("djstripe.models.Customer.subscribe", autospec=True)
    def test_change_sub_with_proration_same_plan(self, subscribe_mock,
                                                 current_subscription_mock,
                                                 proration_policy_mock):
        self.assertTrue(self.client.login(username="******",
                                          password="******"))
        response = self.client.post(self.url, {"plan": "test"})
        self.assertRedirects(response, reverse("djstripe:history"))

        subscribe_mock.assert_called_once_with(self.user1.customer, "test")

    @patch("djstripe.models.Customer.current_subscription",
           new_callable=PropertyMock,
           return_value=CurrentSubscription(plan="test",
                                            amount=Decimal(25.00)))
    @patch("djstripe.models.Customer.subscribe", autospec=True)
    def test_change_sub_same_plan(self, subscribe_mock,
                                  current_subscription_mock):
        self.assertTrue(self.client.login(username="******",
                                          password="******"))
        response = self.client.post(self.url, {"plan": "test"})
        self.assertRedirects(response, reverse("djstripe:history"))

        subscribe_mock.assert_called_once_with(self.user1.customer, "test")

    @patch("djstripe.models.Customer.subscribe", autospec=True)
    def test_change_sub_stripe_error(self, subscribe_mock):
        subscribe_mock.side_effect = stripe.StripeError(
            "No such plan: test_id_3")

        self.assertTrue(self.client.login(username="******",
                                          password="******"))

        response = self.client.post(self.url, {"plan": "test_deletion"})
        self.assertEqual(200, response.status_code)
        self.assertIn("form", response.context)
        self.assertIn("No such plan: test_id_3",
                      response.context["form"].errors["__all__"])
Ejemplo n.º 3
0
class EventTest(TestCase):
    message = {
        "created": 1363911708,
        "data": {
            "object": {
                "account_balance": 0,
                "active_card": None,
                "created": 1363911708,
                "delinquent": False,
                "description": None,
                "discount": None,
                "email": "*****@*****.**",
                "id": "cus_yyyyyyyyyyyyyyyyyyyy",
                "customer": "cus_xxxxxxxxxxxxxxx",
                "livemode": True,
                "object": "customer",
                "subscription": None
            }
        },
        "id": "evt_xxxxxxxxxxxxx",
        "livemode": True,
        "object": "event",
        "pending_webhooks": 1,
        "type": "ping"
    }

    fake_current_subscription = CurrentSubscription(plan="test",
                                                    quantity=1,
                                                    start=timezone.now(),
                                                    amount=Decimal(25.00))

    def setUp(self):
        self.message["data"]["object"][
            "customer"] = "cus_xxxxxxxxxxxxxxx"  # Yes, this is intentional.

        self.user = get_user_model().objects.create_user(
            username="******", email="*****@*****.**")
        self.customer = Customer.objects.create(
            stripe_id=self.message["data"]["object"]["customer"],
            subscriber=self.user)

    def test_tostring(self):
        event = Event.objects.create(stripe_id=self.message["id"],
                                     kind="eventkind",
                                     webhook_message=self.message,
                                     validated_message=self.message,
                                     valid=True)
        self.assertEquals("<eventkind, stripe_id=evt_xxxxxxxxxxxxx>",
                          str(event))

    def test_link_customer_customer_created(self):
        msg = {
            "created": 1363911708,
            "data": {
                "object": {
                    "account_balance": 0,
                    "active_card": None,
                    "created": 1363911708,
                    "delinquent": False,
                    "description": None,
                    "discount": None,
                    "email": "*****@*****.**",
                    "id": "cus_xxxxxxxxxxxxxxx",
                    "livemode": True,
                    "object": "customer",
                    "subscription": None
                }
            },
            "id": "evt_xxxxxxxxxxxxx",
            "livemode": True,
            "object": "event",
            "pending_webhooks": 1,
            "type": "customer.created"
        }
        event = Event.objects.create(
            stripe_id=msg["id"],
            kind="customer.created",
            livemode=True,
            webhook_message=msg,
            validated_message=msg,
            valid=True,
        )
        event.process()
        self.assertEquals(event.customer, self.customer)

    def test_link_customer_customer_updated(self):
        msg = {
            "created": 1346855599,
            "data": {
                "object": {
                    "account_balance": 0,
                    "active_card": {
                        "address_city": None,
                        "address_country": None,
                        "address_line1": None,
                        "address_line1_check": None,
                        "address_line2": None,
                        "address_state": None,
                        "address_zip": None,
                        "address_zip_check": None,
                        "country": "MX",
                        "cvc_check": "pass",
                        "exp_month": 1,
                        "exp_year": 2014,
                        "fingerprint": "XXXXXXXXXXX",
                        "last4": "7992",
                        "name": None,
                        "object": "card",
                        "type": "MasterCard"
                    },
                    "created": 1346855596,
                    "delinquent": False,
                    "description": None,
                    "discount": None,
                    "email": "*****@*****.**",
                    "id": "cus_xxxxxxxxxxxxxxx",
                    "livemode": True,
                    "object": "customer",
                    "subscription": None
                },
                "previous_attributes": {
                    "active_card": None
                }
            },
            "id": "evt_xxxxxxxxxxxxx",
            "livemode": True,
            "object": "event",
            "pending_webhooks": 1,
            "type": "customer.updated"
        }
        event = Event.objects.create(
            stripe_id=msg["id"],
            kind="customer.updated",
            livemode=True,
            webhook_message=msg,
            validated_message=msg,
            valid=True,
        )
        event.process()
        self.assertEquals(event.customer, self.customer)

    def test_link_customer_customer_deleted(self):
        msg = {
            "created": 1348286560,
            "data": {
                "object": {
                    "account_balance": 0,
                    "active_card": None,
                    "created": 1348286302,
                    "delinquent": False,
                    "description": None,
                    "discount": None,
                    "email": "*****@*****.**",
                    "id": "cus_xxxxxxxxxxxxxxx",
                    "livemode": True,
                    "object": "customer",
                    "subscription": None
                }
            },
            "id": "evt_xxxxxxxxxxxxx",
            "livemode": True,
            "object": "event",
            "pending_webhooks": 1,
            "type": "customer.deleted"
        }
        event = Event.objects.create(
            stripe_id=msg["id"],
            kind="customer.deleted",
            livemode=True,
            webhook_message=msg,
            validated_message=msg,
            valid=True,
        )
        event.process()
        self.assertEquals(event.customer, self.customer)

    @patch('stripe.Event.retrieve',
           return_value=convert_to_fake_stripe_object({
               "data": message["data"],
               "zebra": True,
               "alpha": False
           }))
    def test_validate_true(self, event_retrieve_mock):
        event = Event.objects.create(stripe_id=self.message["id"],
                                     kind="ping",
                                     webhook_message=self.message,
                                     validated_message=self.message)

        self.assertEqual(None, event.valid)
        event.validate()
        event_retrieve_mock.assert_called_once_with(self.message["id"])
        self.assertEqual(True, event.valid)

    @patch('stripe.Event.retrieve',
           return_value=convert_to_fake_stripe_object({
               "data": {
                   "object": {
                       "flavor": "chocolate"
                   }
               },
               "zebra": True,
               "alpha": False
           }))
    def test_validate_false(self, event_retrieve_mock):
        event = Event.objects.create(stripe_id=self.message["id"],
                                     kind="ping",
                                     webhook_message=self.message,
                                     validated_message=self.message)

        self.assertEqual(None, event.valid)
        event.validate()
        event_retrieve_mock.assert_called_once_with(self.message["id"])
        self.assertEqual(False, event.valid)

    def test_process_exit_immediately(self):
        event = Event.objects.create(stripe_id=self.message["id"],
                                     kind="ping",
                                     webhook_message=self.message,
                                     validated_message=self.message,
                                     valid=False)

        event.process()
        self.assertFalse(event.processed)

    @patch('djstripe.models.Customer.objects.get')
    @patch('stripe.Invoice.retrieve')
    @patch('djstripe.models.Invoice.sync_from_stripe_data')
    def test_process_invoice_event(self, stripe_sync_mock, retrieve_mock,
                                   customer_get):
        event = Event.objects.create(stripe_id=self.message["id"],
                                     kind="invoice.created",
                                     webhook_message=self.message,
                                     validated_message=self.message,
                                     valid=True)
        customer_get.return_value = self.customer
        retrieve_mock.return_value = self.message['object']
        event.process()
        customer_get.assert_called_once_with(stripe_id=self.customer.stripe_id)
        stripe_sync_mock.assert_called_once_with(self.message['object'],
                                                 send_receipt=True)
        self.assertTrue(event.processed)

    @patch('djstripe.models.Customer.objects.get')
    @patch('stripe.Invoice.retrieve')
    @patch('djstripe.models.Invoice.sync_from_stripe_data')
    def test_process_invoice_event_ignored(self, stripe_sync_mock,
                                           retrieve_mock, customer_get):
        event = Event.objects.create(stripe_id=self.message["id"],
                                     kind="invoice.notanevent",
                                     webhook_message=self.message,
                                     validated_message=self.message,
                                     valid=True)
        customer_get.return_value = self.customer
        retrieve_mock.return_value = self.message['object']
        event.process()
        self.assertFalse(stripe_sync_mock.called)
        self.assertTrue(event.processed)

    @patch('djstripe.models.Customer.objects.get')
    @patch('stripe.Invoice.retrieve')
    @patch('djstripe.models.Invoice.sync_from_stripe_data')
    def test_process_invoice_event_badcustomer(self, stripe_sync_mock,
                                               retrieve_mock, customer_get):
        event = Event.objects.create(stripe_id=self.message["id"],
                                     kind="invoice.created",
                                     webhook_message=self.message,
                                     validated_message=self.message,
                                     valid=True)
        customer_get.side_effect = Customer.DoesNotExist()
        retrieve_mock.return_value = self.message['object']
        event.process()
        customer_get.assert_called_once_with(stripe_id=self.customer.stripe_id)
        stripe_sync_mock.assert_called_once_with(self.message['object'],
                                                 send_receipt=True)
        self.assertTrue(event.processed)

    @patch('stripe.Charge.retrieve', return_value='hello')
    @patch('djstripe.models.Charge.sync_from_stripe_data')
    def test_process_charge_event(self, record_charge_mock, retrieve_mock):
        event = Event.objects.create(stripe_id=self.message["id"],
                                     kind="charge.created",
                                     webhook_message=self.message,
                                     validated_message=self.message,
                                     valid=True)

        event.process()
        self.assertEqual(event.customer, self.customer)
        retrieve_mock.assert_called_once_with(
            self.message["data"]["object"]["id"])
        record_charge_mock.assert_called_once_with("hello")
        self.assertTrue(event.processed)

    @patch('djstripe.models.Customer.sync_current_subscription')
    def test_customer_subscription_event(self, sync_current_subscription_mock):
        event = Event.objects.create(stripe_id=self.message["id"],
                                     kind="customer.subscription.created",
                                     webhook_message=self.message,
                                     validated_message=self.message,
                                     valid=True)

        event.process()
        sync_current_subscription_mock.assert_called_once_with()
        self.assertTrue(event.processed)

    @patch('djstripe.models.Customer.sync_current_subscription')
    def test_customer_subscription_event_no_customer(
            self, sync_current_subscription_mock):
        self.message["data"]["object"]["customer"] = None
        event = Event.objects.create(stripe_id=self.message["id"],
                                     kind="customer.subscription.created",
                                     webhook_message=self.message,
                                     validated_message=self.message,
                                     valid=True)

        event.process()
        self.assertFalse(sync_current_subscription_mock.called)
        self.assertTrue(event.processed)

    @patch("djstripe.models.Customer.current_subscription",
           new_callable=PropertyMock,
           return_value=fake_current_subscription)
    def test_customer_subscription_deleted_event(self,
                                                 current_subscription_mock):
        event = Event.objects.create(stripe_id=self.message["id"],
                                     kind="customer.subscription.deleted",
                                     webhook_message=self.message,
                                     validated_message=self.message,
                                     valid=True)

        event.process()
        self.assertTrue(current_subscription_mock.status,
                        CurrentSubscription.STATUS_CANCELLED)
        self.assertTrue(event.processed)

    @patch("stripe.Customer.retrieve")
    def test_process_customer_deleted(self, customer_retrieve_mock):
        msg = {
            "created": 1348286560,
            "data": {
                "object": {
                    "account_balance": 0,
                    "active_card": None,
                    "created": 1348286302,
                    "delinquent": False,
                    "description": None,
                    "discount": None,
                    "email": "*****@*****.**",
                    "id": "cus_xxxxxxxxxxxxxxx",
                    "livemode": True,
                    "object": "customer",
                    "subscription": None
                }
            },
            "id": "evt_xxxxxxxxxxxxx",
            "livemode": True,
            "object": "event",
            "pending_webhooks": 1,
            "type": "customer.deleted"
        }
        event = Event.objects.create(stripe_id=msg["id"],
                                     kind="customer.deleted",
                                     livemode=True,
                                     webhook_message=msg,
                                     validated_message=msg,
                                     valid=True)
        event.process()
        self.assertEquals(event.customer, self.customer)
        self.assertEquals(event.customer.subscriber, None)
        self.assertTrue(event.processed)

    def test_invalid_event_kind(self):
        """Should just fail silently and not do anything."""
        event = Event.objects.create(stripe_id=self.message["id"],
                                     kind="fake.event.kind",
                                     webhook_message=self.message,
                                     validated_message=self.message,
                                     valid=True)

        event.process()
        self.assertTrue(event.processed)

    @patch('djstripe.models.EventProcessingException.log')
    @patch('djstripe.models.Event.send_signal',
           side_effect=stripe.StripeError())
    def test_stripe_error(self, send_signal_mock, event_exception_log):
        event = Event.objects.create(stripe_id=self.message["id"],
                                     kind="fake.event.kind",
                                     webhook_message=self.message,
                                     validated_message=self.message,
                                     valid=True)

        event.process()
        self.assertTrue(event_exception_log.called)
        self.assertFalse(event.processed)
Ejemplo n.º 4
0
class ConfirmFormViewTest(TestCase):
    fake_stripe_customer_id = "cus_xxx1234567890"

    def setUp(self):
        self.plan = "test0"
        self.url = reverse("djstripe:confirm", kwargs={'plan': self.plan})
        self.user = get_user_model().objects.create_user(
            username="******", email="*****@*****.**", password="******")
        self.assertTrue(self.client.login(username="******", password="******"))

    @patch("djstripe.models.Customer.current_subscription",
           new_callable=PropertyMock,
           return_value=CurrentSubscription(plan="something-else"))
    @patch("stripe.Customer.create",
           return_value=PropertyMock(id=fake_stripe_customer_id))
    def test_get_form_valid(self, djstripe_customer_customer_subscription_mock,
                            stripe_create_customer_mock):
        response = self.client.get(self.url)
        self.assertEqual(200, response.status_code)

    @patch("djstripe.models.Customer.current_subscription",
           new_callable=PropertyMock,
           return_value=CurrentSubscription(plan="test0"))
    @patch("stripe.Customer.create",
           return_value=PropertyMock(id=fake_stripe_customer_id))
    def test_get_form_unknown(self,
                              djstripe_customer_customer_subscription_mock,
                              stripe_create_customer_mock):
        response = self.client.get(
            reverse("djstripe:confirm", kwargs={'plan': 'does-not-exist'}))
        self.assertRedirects(response, reverse("djstripe:subscribe"))

    @patch("djstripe.models.Customer.current_subscription",
           new_callable=PropertyMock,
           return_value=CurrentSubscription(plan="test0"))
    @patch("stripe.Customer.create",
           return_value=PropertyMock(id=fake_stripe_customer_id))
    def test_get_form_invalid(self,
                              djstripe_customer_customer_subscription_mock,
                              stripe_create_customer_mock):
        response = self.client.get(self.url)
        self.assertRedirects(response, reverse("djstripe:subscribe"))

    @patch("djstripe.models.Customer.subscribe", autospec=True)
    @patch("djstripe.models.Customer.update_card", autospec=True)
    @patch("stripe.Customer.create",
           return_value=PropertyMock(id="cus_xxx1234567890"))
    def test_post_valid(self, stripe_customer_mock, update_card_mock,
                        subscribe_mock):
        self.assertEqual(0, Customer.objects.count())
        response = self.client.post(self.url, {
            "plan": self.plan,
            "stripe_token": "cake"
        })
        self.assertEqual(1, Customer.objects.count())
        update_card_mock.assert_called_once_with(self.user.customer, "cake")
        subscribe_mock.assert_called_once_with(self.user.customer, self.plan)

        self.assertRedirects(response, reverse("djstripe:history"))

    @patch("djstripe.models.Customer.subscribe", autospec=True)
    @patch("djstripe.models.Customer.update_card", autospec=True)
    @patch("stripe.Customer.create",
           return_value=PropertyMock(id="cus_xxx1234567890"))
    def test_post_no_card(self, stripe_customer_mock, update_card_mock,
                          subscribe_mock):
        update_card_mock.side_effect = stripe.StripeError(
            "Invalid source object:")

        response = self.client.post(self.url, {"plan": self.plan})
        self.assertEqual(200, response.status_code)
        self.assertIn("form", response.context)
        self.assertIn("Invalid source object:",
                      response.context["form"].errors["__all__"])

    @patch("stripe.Customer.create",
           return_value=PropertyMock(id="cus_xxx1234567890"))
    def test_post_form_invalid(self, stripe_customer_mock):
        response = self.client.post(self.url)
        self.assertEqual(200, response.status_code)
        self.assertIn("plan", response.context["form"].errors)
        self.assertIn("This field is required.",
                      response.context["form"].errors["plan"])
Ejemplo n.º 5
0
class TestCustomer(TestCase):
    fake_current_subscription = CurrentSubscription(
        plan="test_plan",
        quantity=1,
        start=timezone.now(),
        amount=decimal.Decimal(25.00))

    def setUp(self):
        self.user = get_user_model().objects.create_user(
            username="******", email="*****@*****.**")
        self.customer = Customer.objects.create(
            subscriber=self.user,
            stripe_id="cus_xxxxxxxxxxxxxxx",
            card_fingerprint="YYYYYYYY",
            card_last_4="2342",
            card_kind="Visa")

    def test_tostring(self):
        self.assertEquals("<patrick, stripe_id=cus_xxxxxxxxxxxxxxx>",
                          str(self.customer))

    @patch("stripe.Customer.retrieve")
    def test_customer_purge_leaves_customer_record(self,
                                                   customer_retrieve_fake):
        self.customer.purge()
        customer = Customer.objects.get(stripe_id=self.customer.stripe_id)
        self.assertTrue(customer.subscriber is None)
        self.assertTrue(customer.card_fingerprint == "")
        self.assertTrue(customer.card_last_4 == "")
        self.assertTrue(customer.card_kind == "")
        self.assertTrue(
            get_user_model().objects.filter(pk=self.user.pk).exists())

    @patch("stripe.Customer.retrieve")
    def test_customer_delete_same_as_purge(self, customer_retrieve_fake):
        self.customer.delete()
        customer = Customer.objects.get(stripe_id=self.customer.stripe_id)
        self.assertTrue(customer.subscriber is None)
        self.assertTrue(customer.card_fingerprint == "")
        self.assertTrue(customer.card_last_4 == "")
        self.assertTrue(customer.card_kind == "")
        self.assertTrue(
            get_user_model().objects.filter(pk=self.user.pk).exists())

    @patch("stripe.Customer.retrieve")
    def test_customer_purge_raises_customer_exception(self,
                                                      customer_retrieve_mock):
        customer_retrieve_mock.side_effect = stripe.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.card_fingerprint == "")
        self.assertTrue(customer.card_last_4 == "")
        self.assertTrue(customer.card_kind == "")
        self.assertTrue(
            get_user_model().objects.filter(pk=self.user.pk).exists())

        customer_retrieve_mock.assert_called_once_with(self.customer.stripe_id)

    @patch("stripe.Customer.retrieve")
    def test_customer_delete_raises_unexpected_exception(
            self, customer_retrieve_mock):
        customer_retrieve_mock.side_effect = stripe.InvalidRequestError(
            "Unexpected Exception", "blah")

        with self.assertRaisesMessage(stripe.InvalidRequestError,
                                      "Unexpected Exception"):
            self.customer.purge()

        customer_retrieve_mock.assert_called_once_with(self.customer.stripe_id)

    def test_change_charge(self):
        self.assertTrue(self.customer.can_charge())

    @patch("stripe.Customer.retrieve")
    def test_cannot_charge(self, customer_retrieve_fake):
        self.customer.delete()
        self.assertFalse(self.customer.can_charge())

    def test_charge_accepts_only_decimals(self):
        with self.assertRaises(ValueError):
            self.customer.charge(10)

    @patch("stripe.Charge.retrieve")
    def test_record_charge(self, charge_retrieve_mock):
        charge_retrieve_mock.return_value = {
            "id": "ch_XXXXXX",
            "card": {
                "last4": "4323",
                "type": "Visa"
            },
            "amount": 1000,
            "paid": True,
            "refunded": False,
            "captured": True,
            "fee": 499,
            "dispute": None,
            "created": 1363911708,
            "customer": "cus_xxxxxxxxxxxxxxx"
        }
        obj = self.customer.record_charge("ch_XXXXXX")
        self.assertEquals(Charge.objects.get(stripe_id="ch_XXXXXX").pk, obj.pk)
        self.assertEquals(obj.paid, True)
        self.assertEquals(obj.disputed, False)
        self.assertEquals(obj.refunded, False)
        self.assertEquals(obj.amount_refunded, None)

    @patch("stripe.Charge.retrieve")
    def test_refund_charge(self, charge_retrieve_mock):
        charge = Charge.objects.create(stripe_id="ch_XXXXXX",
                                       customer=self.customer,
                                       card_last_4="4323",
                                       card_kind="Visa",
                                       amount=decimal.Decimal("10.00"),
                                       paid=True,
                                       refunded=False,
                                       fee=decimal.Decimal("4.99"),
                                       disputed=False)
        charge_retrieve_mock.return_value.refund.return_value = {
            "id": "ch_XXXXXX",
            "card": {
                "last4": "4323",
                "type": "Visa"
            },
            "amount": 1000,
            "paid": True,
            "refunded": True,
            "captured": True,
            "amount_refunded": 1000,
            "fee": 499,
            "dispute": None,
            "created": 1363911708,
            "customer": "cus_xxxxxxxxxxxxxxx"
        }
        charge.refund()
        charge2 = Charge.objects.get(stripe_id="ch_XXXXXX")
        self.assertEquals(charge2.refunded, True)
        self.assertEquals(charge2.amount_refunded, decimal.Decimal("10.00"))

    @patch("stripe.Charge.retrieve")
    def test_capture_charge(self, charge_retrieve_mock):
        charge = Charge.objects.create(stripe_id="ch_XXXXXX",
                                       customer=self.customer,
                                       card_last_4="4323",
                                       card_kind="Visa",
                                       amount=decimal.Decimal("10.00"),
                                       paid=True,
                                       refunded=False,
                                       captured=False,
                                       fee=decimal.Decimal("4.99"),
                                       disputed=False)
        charge_retrieve_mock.return_value.capture.return_value = {
            "id": "ch_XXXXXX",
            "card": {
                "last4": "4323",
                "type": "Visa"
            },
            "amount": 1000,
            "paid": True,
            "refunded": True,
            "captured": True,
            "amount_refunded": 1000,
            "fee": 499,
            "dispute": None,
            "created": 1363911708,
            "customer": "cus_xxxxxxxxxxxxxxx"
        }
        charge2 = charge.capture()
        self.assertEquals(charge2.captured, True)

    @patch("stripe.Charge.retrieve")
    def test_refund_charge_object_returned(self, charge_retrieve_mock):
        charge = Charge.objects.create(stripe_id="ch_XXXXXX",
                                       customer=self.customer,
                                       card_last_4="4323",
                                       card_kind="Visa",
                                       amount=decimal.Decimal("10.00"),
                                       paid=True,
                                       refunded=False,
                                       fee=decimal.Decimal("4.99"),
                                       disputed=False)
        charge_retrieve_mock.return_value.refund.return_value = {
            "id": "ch_XXXXXX",
            "card": {
                "last4": "4323",
                "type": "Visa"
            },
            "amount": 1000,
            "paid": True,
            "refunded": True,
            "captured": True,
            "amount_refunded": 1000,
            "fee": 499,
            "dispute": None,
            "created": 1363911708,
            "customer": "cus_xxxxxxxxxxxxxxx"
        }
        charge2 = charge.refund()
        self.assertEquals(charge2.refunded, True)
        self.assertEquals(charge2.amount_refunded, decimal.Decimal("10.00"))

    def test_calculate_refund_amount_full_refund(self):
        charge = Charge(stripe_id="ch_111111",
                        customer=self.customer,
                        amount=decimal.Decimal("500.00"))
        self.assertEquals(charge.calculate_refund_amount(), 50000)

    def test_calculate_refund_amount_partial_refund(self):
        charge = Charge(stripe_id="ch_111111",
                        customer=self.customer,
                        amount=decimal.Decimal("500.00"))
        self.assertEquals(
            charge.calculate_refund_amount(amount=decimal.Decimal("300.00")),
            30000)

    def test_calculate_refund_above_max_refund(self):
        charge = Charge(stripe_id="ch_111111",
                        customer=self.customer,
                        amount=decimal.Decimal("500.00"))
        self.assertEquals(
            charge.calculate_refund_amount(amount=decimal.Decimal("600.00")),
            50000)

    @patch("stripe.Charge.retrieve")
    @patch("stripe.Charge.create")
    def test_charge_converts_dollars_into_cents(self, charge_create_mock,
                                                charge_retrieve_mock):
        charge_create_mock.return_value.id = "ch_XXXXX"
        charge_retrieve_mock.return_value = {
            "id": "ch_XXXXXX",
            "card": {
                "last4": "4323",
                "type": "Visa"
            },
            "amount": 1000,
            "paid": True,
            "refunded": False,
            "captured": True,
            "fee": 499,
            "dispute": None,
            "created": 1363911708,
            "customer": "cus_xxxxxxxxxxxxxxx"
        }
        self.customer.charge(amount=decimal.Decimal("10.00"))
        _, kwargs = charge_create_mock.call_args
        self.assertEquals(kwargs["amount"], 1000)

    @patch("stripe.Charge.retrieve")
    @patch("stripe.Charge.create")
    def test_charge_passes_extra_arguments(self, charge_create_mock,
                                           charge_retrieve_mock):
        charge_create_mock.return_value.id = "ch_XXXXX"
        charge_retrieve_mock.return_value = {
            "id": "ch_XXXXXX",
            "card": {
                "last4": "4323",
                "type": "Visa"
            },
            "amount": 1000,
            "paid": True,
            "refunded": False,
            "captured": True,
            "fee": 499,
            "dispute": None,
            "created": 1363911708,
            "customer": "cus_xxxxxxxxxxxxxxx"
        }
        self.customer.charge(amount=decimal.Decimal("10.00"),
                             capture=True,
                             destination='a_stripe_client_id')
        _, kwargs = charge_create_mock.call_args
        self.assertEquals(kwargs["capture"], True)
        self.assertEquals(kwargs["destination"], 'a_stripe_client_id')

    @patch(
        "djstripe.models.djstripe_settings.trial_period_for_subscriber_callback",
        return_value="donkey")
    @patch("stripe.Customer.create",
           return_value=PropertyMock(id="cus_xxx1234567890"))
    def test_create_trial_callback(self, customer_create_mock, callback_mock):
        user = get_user_model().objects.create_user(username="******",
                                                    email="*****@*****.**")
        Customer.create(user)

        customer_create_mock.assert_called_once_with(email=user.email)
        callback_mock.assert_called_once_with(user)

    @patch("djstripe.models.Customer.subscribe")
    @patch("djstripe.models.djstripe_settings.DEFAULT_PLAN",
           new_callable=PropertyMock,
           return_value="schreck")
    @patch(
        "djstripe.models.djstripe_settings.trial_period_for_subscriber_callback",
        return_value="donkey")
    @patch("stripe.Customer.create",
           return_value=PropertyMock(id="cus_xxx1234567890"))
    def test_create_default_plan(self, customer_create_mock, callback_mock,
                                 default_plan_fake, subscribe_mock):
        user = get_user_model().objects.create_user(username="******",
                                                    email="*****@*****.**")
        Customer.create(user)

        customer_create_mock.assert_called_once_with(email=user.email)
        callback_mock.assert_called_once_with(user)
        subscribe_mock.assert_called_once_with(plan=default_plan_fake,
                                               trial_days="donkey")

    @patch("djstripe.models.Customer.stripe_customer",
           new_callable=PropertyMock)
    def test_update_card(self, customer_stripe_customer_mock):
        customer_stripe_customer_mock.return_value = PropertyMock(
            active_card=PropertyMock(fingerprint="test_fingerprint",
                                     last4="1234",
                                     type="test_type",
                                     exp_month=12,
                                     exp_year=2020))

        self.customer.update_card("test")

        self.assertEqual("test_fingerprint", self.customer.card_fingerprint)
        self.assertEqual("1234", self.customer.card_last_4)
        self.assertEqual("test_type", self.customer.card_kind)
        self.assertEqual(12, self.customer.card_exp_month)
        self.assertEqual(2020, self.customer.card_exp_year)

    @patch(
        "djstripe.models.Customer.invoices",
        new_callable=PropertyMock,
        return_value=PropertyMock(
            name="filter",
            filter=MagicMock(return_value=[
                MagicMock(name="inv",
                          retry=MagicMock(name="retry", return_value="test"))
            ])))
    @patch("djstripe.models.Customer.sync_invoices")
    def test_retry_unpaid_invoices(self, sync_invoices_mock, invoices_mock):
        self.customer.retry_unpaid_invoices()

        sync_invoices_mock.assert_called_once_with()
        # TODO: Figure out how to assert on filter and retry mocks

    @patch("djstripe.models.Customer.invoices",
           new_callable=PropertyMock,
           return_value=PropertyMock(
               name="filter",
               filter=MagicMock(return_value=[
                   MagicMock(name="inv",
                             retry=MagicMock(
                                 name="retry",
                                 return_value="test",
                                 side_effect=stripe.InvalidRequestError(
                                     "Invoice is already paid", "blah")))
               ])))
    @patch("djstripe.models.Customer.sync_invoices")
    def test_retry_unpaid_invoices_expected_exception(self, sync_invoices_mock,
                                                      invoices_mock):
        try:
            self.customer.retry_unpaid_invoices()
        except:
            self.fail("Exception was unexpectedly raise.")

    @patch("djstripe.models.Customer.invoices",
           new_callable=PropertyMock,
           return_value=PropertyMock(
               name="filter",
               filter=MagicMock(return_value=[
                   MagicMock(name="inv",
                             retry=MagicMock(
                                 name="retry",
                                 return_value="test",
                                 side_effect=stripe.InvalidRequestError(
                                     "This should fail!", "blah")))
               ])))
    @patch("djstripe.models.Customer.sync_invoices")
    def test_retry_unpaid_invoices_unexpected_exception(
            self, sync_invoices_mock, invoices_mock):
        with self.assertRaisesMessage(stripe.InvalidRequestError,
                                      "This should fail!"):
            self.customer.retry_unpaid_invoices()

    @patch("stripe.Invoice.create")
    def test_send_invoice_success(self, invoice_create_mock):
        return_status = self.customer.send_invoice()
        self.assertTrue(return_status)

        invoice_create_mock.assert_called_once_with(
            customer=self.customer.stripe_id)

    @patch("stripe.Invoice.create")
    def test_send_invoice_failure(self, invoice_create_mock):
        invoice_create_mock.side_effect = stripe.InvalidRequestError(
            "Invoice creation failed.", "blah")

        return_status = self.customer.send_invoice()
        self.assertFalse(return_status)

        invoice_create_mock.assert_called_once_with(
            customer=self.customer.stripe_id)

    @patch("djstripe.models.Customer.stripe_customer",
           new_callable=PropertyMock)
    def test_sync_active_card(self, stripe_customer_mock):
        stripe_customer_mock.return_value = PropertyMock(
            active_card=PropertyMock(
                fingerprint="cherry",
                last4="4429",
                type="apple",
                exp_month=12,
                exp_year=2020,
            ))

        self.customer.sync()
        self.assertEqual("cherry", self.customer.card_fingerprint)
        self.assertEqual("4429", self.customer.card_last_4)
        self.assertEqual("apple", self.customer.card_kind)
        self.assertEqual(12, self.customer.card_exp_month)
        self.assertEqual(2020, self.customer.card_exp_year)

    @patch("djstripe.models.Customer.stripe_customer",
           new_callable=PropertyMock,
           return_value=PropertyMock(active_card=None))
    def test_sync_no_card(self, stripe_customer_mock):
        self.customer.sync()
        self.assertEqual("YYYYYYYY", self.customer.card_fingerprint)
        self.assertEqual("2342", self.customer.card_last_4)
        self.assertEqual("Visa", self.customer.card_kind)

    @patch("djstripe.models.Invoice.sync_from_stripe_data")
    @patch("djstripe.models.Customer.stripe_customer",
           new_callable=PropertyMock,
           return_value=PropertyMock(invoices=MagicMock(
               return_value=PropertyMock(data=["apple", "orange", "pear"]))))
    def test_sync_invoices(self, stripe_customer_mock,
                           sync_from_stripe_data_mock):
        self.customer.sync_invoices()

        sync_from_stripe_data_mock.assert_any_call("apple", send_receipt=False)
        sync_from_stripe_data_mock.assert_any_call("orange",
                                                   send_receipt=False)
        sync_from_stripe_data_mock.assert_any_call("pear", send_receipt=False)

        self.assertEqual(3, sync_from_stripe_data_mock.call_count)

    @patch("djstripe.models.Invoice.sync_from_stripe_data")
    @patch("djstripe.models.Customer.stripe_customer",
           new_callable=PropertyMock,
           return_value=PropertyMock(invoices=MagicMock(
               return_value=PropertyMock(data=[]))))
    def test_sync_invoices_none(self, stripe_customer_mock,
                                sync_from_stripe_data_mock):
        self.customer.sync_invoices()

        self.assertFalse(sync_from_stripe_data_mock.called)

    @patch("djstripe.models.Customer.record_charge")
    @patch("djstripe.models.Customer.stripe_customer",
           new_callable=PropertyMock,
           return_value=PropertyMock(charges=MagicMock(
               return_value=PropertyMock(data=[
                   PropertyMock(id="herbst"),
                   PropertyMock(id="winter"),
                   PropertyMock(id="fruehling"),
                   PropertyMock(id="sommer")
               ]))))
    def test_sync_charges(self, stripe_customer_mock, record_charge_mock):
        self.customer.sync_charges()

        record_charge_mock.assert_any_call("herbst")
        record_charge_mock.assert_any_call("winter")
        record_charge_mock.assert_any_call("fruehling")
        record_charge_mock.assert_any_call("sommer")

        self.assertEqual(4, record_charge_mock.call_count)

    @patch("djstripe.models.Customer.record_charge")
    @patch("djstripe.models.Customer.stripe_customer",
           new_callable=PropertyMock,
           return_value=PropertyMock(charges=MagicMock(
               return_value=PropertyMock(data=[]))))
    def test_sync_charges_none(self, stripe_customer_mock, record_charge_mock):
        self.customer.sync_charges()

        self.assertFalse(record_charge_mock.called)

    @patch("djstripe.models.Customer.stripe_customer",
           new_callable=PropertyMock,
           return_value=PropertyMock(subscription=None))
    def test_sync_current_subscription_no_stripe_subscription(
            self, stripe_customer_mock):
        self.assertEqual(None, self.customer.sync_current_subscription())

    @patch("djstripe.models.djstripe_settings.plan_from_stripe_id",
           return_value="test_plan")
    @patch("djstripe.models.convert_tstamp",
           return_value=timezone.make_aware(datetime.datetime(2015, 6, 19)))
    @patch("djstripe.models.Customer.current_subscription",
           new_callable=PropertyMock,
           return_value=fake_current_subscription)
    @patch("djstripe.models.Customer.stripe_customer",
           new_callable=PropertyMock,
           return_value=PropertyMock(subscription=PropertyMock(
               plan=PropertyMock(id="fish", amount=5000),
               quantity=5,
               trial_start=False,
               trial_end=False,
               cancel_at_period_end=False,
               status="tree")))
    def test_sync_current_subscription_update_no_trial(
            self, stripe_customer_mock, customer_subscription_mock,
            convert_tstamp_fake, plan_getter_mock):
        tz_test_time = timezone.make_aware(datetime.datetime(2015, 6, 19))

        self.customer.sync_current_subscription()

        plan_getter_mock.assert_called_with("fish")

        self.assertEqual("test_plan", self.fake_current_subscription.plan)
        self.assertEqual(decimal.Decimal("50.00"),
                         self.fake_current_subscription.amount)
        self.assertEqual("tree", self.fake_current_subscription.status)
        self.assertEqual(5, self.fake_current_subscription.quantity)
        self.assertEqual(False,
                         self.fake_current_subscription.cancel_at_period_end)
        self.assertEqual(tz_test_time,
                         self.fake_current_subscription.canceled_at)
        self.assertEqual(tz_test_time, self.fake_current_subscription.start)
        self.assertEqual(tz_test_time,
                         self.fake_current_subscription.current_period_start)
        self.assertEqual(tz_test_time,
                         self.fake_current_subscription.current_period_end)
        self.assertEqual(None, self.fake_current_subscription.trial_start)
        self.assertEqual(None, self.fake_current_subscription.trial_end)

    @patch("djstripe.models.Customer.send_invoice")
    @patch("djstripe.models.Customer.sync_current_subscription")
    @patch("djstripe.models.Customer.stripe_customer.update_subscription")
    @patch("djstripe.models.Customer.stripe_customer",
           new_callable=PropertyMock,
           return_value=PropertyMock())
    def test_subscribe_trial_plan(self, stripe_customer_mock,
                                  update_subscription_mock,
                                  sync_subscription_mock, send_invoice_mock):
        trial_days = 7  # From settings

        self.customer.subscribe(plan="test_trial")
        sync_subscription_mock.assert_called_once_with()
        send_invoice_mock.assert_called_once_with()

        _, call_kwargs = update_subscription_mock.call_args

        self.assertIn("trial_end", call_kwargs)
        self.assertLess(call_kwargs["trial_end"],
                        timezone.now() + datetime.timedelta(days=trial_days))

    @patch("djstripe.models.Customer.send_invoice")
    @patch("djstripe.models.Customer.sync_current_subscription")
    @patch("djstripe.models.Customer.stripe_customer.update_subscription")
    @patch("djstripe.models.Customer.stripe_customer",
           new_callable=PropertyMock,
           return_value=PropertyMock())
    def test_subscribe_trial_days_kwarg(self, stripe_customer_mock,
                                        update_subscription_mock,
                                        sync_subscription_mock,
                                        send_invoice_mock):
        trial_days = 9

        self.customer.subscribe(plan="test", trial_days=trial_days)
        sync_subscription_mock.assert_called_once_with()
        send_invoice_mock.assert_called_once_with()

        _, call_kwargs = update_subscription_mock.call_args

        self.assertIn("trial_end", call_kwargs)
        self.assertLess(call_kwargs["trial_end"],
                        timezone.now() + datetime.timedelta(days=trial_days))

    @patch("djstripe.models.Customer.send_invoice")
    @patch("djstripe.models.Customer.sync_current_subscription")
    @patch("djstripe.models.Customer.current_subscription",
           new_callable=PropertyMock,
           return_value=fake_current_subscription)
    @patch("djstripe.models.Customer.stripe_customer",
           new_callable=PropertyMock,
           return_value=PropertyMock())
    def test_subscribe_not_charge_immediately(self, stripe_customer_mock,
                                              customer_subscription_mock,
                                              sync_subscription_mock,
                                              send_invoice_mock):
        self.customer.subscribe(plan="test", charge_immediately=False)
        sync_subscription_mock.assert_called_once_with()
        self.assertFalse(send_invoice_mock.called)

    @patch("djstripe.models.Charge.send_receipt")
    @patch("djstripe.models.Customer.record_charge", return_value=Charge())
    @patch("stripe.Charge.create", return_value={"id": "test_charge_id"})
    def test_charge_not_send_receipt(self, charge_create_mock,
                                     record_charge_mock, send_receipt_mock):

        self.customer.charge(amount=decimal.Decimal("50.00"),
                             send_receipt=False)
        self.assertTrue(charge_create_mock.called)
        record_charge_mock.assert_called_once_with("test_charge_id")
        self.assertFalse(send_receipt_mock.called)

    @patch("stripe.InvoiceItem.create")
    def test_add_invoice_item(self, invoice_item_create_mock):
        self.customer.add_invoice_item(amount=decimal.Decimal("50.00"),
                                       currency="eur",
                                       invoice_id=77,
                                       description="test")

        invoice_item_create_mock.assert_called_once_with(
            amount=5000,
            currency="eur",
            invoice=77,
            description="test",
            customer=self.customer.stripe_id)

    def test_add_invoice_item_bad_decimal(self):
        with self.assertRaisesMessage(
                ValueError,
                "You must supply a decimal value representing dollars."):
            self.customer.add_invoice_item(amount=5000)