Example #1
0
    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 test_attach_obj(self, attach_mock):
        pm = PaymentMethod.sync_from_stripe_data(FAKE_PAYMENT_METHOD_I)

        payment_method = PaymentMethod.attach(pm, customer=self.customer)

        self.assert_fks(
            payment_method,
            expected_blank_fks={
                "djstripe.Customer.coupon",
                "djstripe.Customer.default_payment_method",
            },
        )
Example #3
0
    def test_attach_synced(self, attach_mock):
        fake_payment_method = deepcopy(FAKE_PAYMENT_METHOD_I)
        fake_payment_method["customer"] = None

        payment_method = PaymentMethod.sync_from_stripe_data(
            fake_payment_method)

        self.assert_fks(payment_method,
                        expected_blank_fks={"djstripe.PaymentMethod.customer"})

        payment_method = PaymentMethod.attach(payment_method.id,
                                              stripe_customer=FAKE_CUSTOMER)

        self.assert_fks(payment_method,
                        expected_blank_fks={"djstripe.Customer.coupon"})
    def test_sync_null_customer(self):
        payment_method = PaymentMethod.sync_from_stripe_data(
            deepcopy(FAKE_PAYMENT_METHOD_I))

        self.assertIsNotNone(payment_method.customer)

        # simulate remote detach
        fake_payment_method_no_customer = deepcopy(FAKE_PAYMENT_METHOD_I)
        fake_payment_method_no_customer["customer"] = None

        payment_method = PaymentMethod.sync_from_stripe_data(
            fake_payment_method_no_customer)

        self.assertIsNone(payment_method.customer)

        self.assert_fks(payment_method,
                        expected_blank_fks={"djstripe.PaymentMethod.customer"})
Example #5
0
    def setUp(self):
        self.user = get_user_model().objects.create_user(username="******", email="*****@*****.**")
        self.customer = FAKE_CUSTOMER.create_for_user(self.user)

        self.payment_method, _ = PaymentMethod._get_or_create_source(FAKE_CARD, "card")
        self.card = self.payment_method.resolve()

        self.customer.default_source = self.payment_method
        self.customer.save()

        self.account = Account.objects.create()
    def test_attach(self, attach_mock):
        payment_method = PaymentMethod.attach(FAKE_PAYMENT_METHOD_I["id"],
                                              customer=FAKE_CUSTOMER["id"])

        self.assert_fks(
            payment_method,
            expected_blank_fks={
                "djstripe.Customer.coupon",
                "djstripe.Customer.default_payment_method",
            },
        )
Example #7
0
def new_customer(request):
    body = json.loads(request.body)
    # Retrieve payment method from Stripe data
    payment_method = stripe.PaymentMethod.retrieve(body['payment_method'])
    PaymentMethod.sync_from_stripe_data(payment_method)
    user = NewUser.objects.get(username=body['user'])
    if not getattr(user, 'owner'):
        # If customer doesn't exist, create new Customer object
        customer = stripe.Customer.create(
            email=getattr(user, 'email'),
            payment_method=payment_method,
            invoice_settings={"default_payment_method": payment_method},
        )
    else:
        # Attach new payment method to customer
        customer = stripe.Customer.retrieve(getattr(user, 'owner').id)
        stripe.PaymentMethod.attach(payment_method, customer=customer.id)
        stripe.Customer.modify(
            customer.id,
            invoice_settings={"default_payment_method": payment_method},
        )

    # Create new Subscription and attach to customer
    subscription = stripe.Subscription.create(
        customer=customer.id,
        items=[{
            'price': body['price_id']
        }],
        expand=['latest_invoice.payment_intent'],
        trial_period_days=7)

    # Save in Django DB
    django_customer = Customer.sync_from_stripe_data(customer)
    user.owner = django_customer
    django_subscription = Subscription.sync_from_stripe_data(subscription)
    user.subscription = django_subscription
    user.save()
    return JsonResponse(data={
        'customer': customer,
        'subscription': subscription
    })
Example #8
0
    def test_detach_card(self):
        original_detach = PaymentMethodDict.detach

        # "card_" payment methods are deleted after detach
        deleted_card_exception = InvalidRequestError(
            message="No such payment_method: card_xxxx",
            param="payment_method",
            code="resource_missing",
        )

        def mocked_detach(*args, **kwargs):
            return original_detach(*args, **kwargs)

        with patch(
                "stripe.PaymentMethod.retrieve",
                return_value=deepcopy(FAKE_CARD_AS_PAYMENT_METHOD),
                autospec=True,
        ):
            PaymentMethod.sync_from_stripe_data(
                deepcopy(FAKE_CARD_AS_PAYMENT_METHOD))

        self.assertEqual(1, self.customer.payment_methods.count())

        payment_method = self.customer.payment_methods.first()

        self.assertTrue(payment_method.id.startswith("card_"),
                        "We expect this to be a 'card_'")

        with patch("tests.PaymentMethodDict.detach",
                   side_effect=mocked_detach,
                   autospec=True) as mock_detach, patch(
                       "stripe.PaymentMethod.retrieve",
                       return_value=deepcopy(FAKE_CARD_AS_PAYMENT_METHOD),
                       autospec=True,
                   ):
            self.assertTrue(payment_method.detach())

        self.assertEqual(0, self.customer.payment_methods.count())
        self.assertIsNone(self.customer.default_payment_method)

        self.assertEqual(
            PaymentMethod.objects.filter(id=payment_method.id).count(),
            0,
            "We expect PaymentMethod id = card_* to be deleted",
        )

        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()

        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",
                side_effect=deleted_card_exception,
                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")
Example #9
0
    def post(self, request: HttpRequest, account_name: str) -> HttpResponse:
        self.request_permissions_guard(request, account_name)

        data = json.loads(request.body)

        customer_id = data.get("customer_id")

        coupon_code = data.get("coupon_code")

        try:
            plan = Plan.objects.get(id=data["plan"], active=True)
        except Plan.DoesNotExist:
            raise ValueError("TODO: Raise better error for missing Plan")

        if not plan.product.extension.is_purchasable and not coupon_code:
            raise ValueError("This product is only purchasable with a coupon.")

        if not customer_id:
            email = request.user.emailaddress_set.filter(
                email=data["email"], verified=True).first()

            if not email:
                raise ValueError(
                    "Validated email address {} was not found for the customer."
                    .format(email.email))

            dj_customer, created = Customer.get_or_create(
                subscriber=request.user)

            if not coupon_code:
                PaymentMethod.attach(data["payment_method"], dj_customer)

                customer = dj_customer.api_retrieve()

                customer["invoice_settings"] = {
                    "default_payment_method": data["payment_method"]
                }
                customer.save()

                dj_customer.sync_from_stripe_data(customer)
        else:
            try:
                dj_customer = Customer.objects.get(djstripe_id=customer_id)
            except Customer.DoesNotExist:
                raise ValueError(
                    "TODO: Raise better error for missing Customer.")

            if dj_customer.subscriber != request.user:
                raise PermissionDenied(
                    "The current user does not own this Customer.")

        try:
            subscription = dj_customer.subscribe(plan, coupon=coupon_code)
        except InvalidRequestError as e:
            return JsonResponse({"success": False, "error": e.user_message})

        AccountSubscription.objects.create(account=self.account,
                                           subscription=subscription)

        messages.success(
            request,
            "Sign up to {} subscription was successful.".format(plan.name))

        post_redirect = reverse("account_subscription_detail",
                                args=(account_name, subscription.id))

        return JsonResponse({
            "success": True,
            "customer_id": dj_customer.djstripe_id,
            "redirect": post_redirect,
        })