예제 #1
0
    def test_sync_from_stripe_data(self):
        invoice = Invoice.sync_from_stripe_data(FAKE_INVOICE)

        self.assertEqual("in_xxxxxxxxxxxxxxx", invoice.stripe_id)
        self.assertEqual(False, invoice.attempted)
        self.assertEqual(False, invoice.closed)
        self.assertEqual(False, invoice.paid)
        self.assertEqual(Decimal("9.95"), invoice.subtotal)
        self.assertEqual(Decimal("9.95"), invoice.total)
        self.assertEqual("", invoice.charge)

        self.assertEqual(1, invoice.items.count())
        invoice_item = invoice.items.all()[0]

        self.assertEqual("sub_xxxxxxxxxxxxxxx", invoice_item.stripe_id)
        self.assertEqual(Decimal("9.95"), invoice_item.amount)
        self.assertEqual("usd", invoice_item.currency)
        self.assertEqual(False, invoice_item.proration)
        self.assertEqual("", invoice_item.description)
        self.assertEqual("subscription", invoice_item.line_type)
        self.assertEqual("test", invoice_item.plan)
        self.assertEqual(1, invoice_item.quantity)

        # period_end is determined by latest invoice_item
        self.assertEqual(invoice_item.period_end, invoice.period_end)

        # Update invoice
        Invoice.sync_from_stripe_data(FAKE_INVOICE)
예제 #2
0
    def test_sync_from_stripe_data_with_charge_no_receipt(self, record_charge_mock, send_receipt_mock):
        record_charge_mock.return_value = Charge(customer=self.customer)

        FAKE_INVOICE_WITH_CHARGE = deepcopy(FAKE_INVOICE)
        FAKE_INVOICE_WITH_CHARGE["id"] = "in_zzzzzzzzzzzzzzz1"
        FAKE_INVOICE_WITH_CHARGE["subscription"] = "sub_zzzzzzzzzzzzzzz1"
        FAKE_INVOICE_WITH_CHARGE["lines"]["data"][0]["id"] = "sub_zzzzzzzzzzzzzzz1"

        FAKE_INVOICE_WITH_CHARGE["charge"] = "taco1"

        Invoice.sync_from_stripe_data(FAKE_INVOICE_WITH_CHARGE, send_receipt=False)
        record_charge_mock.assert_called_once_with("taco1")
        self.assertFalse(send_receipt_mock.called)
예제 #3
0
    def test_invoice_without_plan(self, charge_retrieve_mock, subscription_retrieve_mock, default_account_mock):
        default_account_mock.return_value = self.account

        invoice_data = deepcopy(FAKE_INVOICE)
        invoice_data["lines"]["data"][0]["plan"] = None
        invoice_data["subscription"] = None
        invoice = Invoice.sync_from_stripe_data(invoice_data)
        self.assertIsNone(invoice.plan)
예제 #4
0
    def test_status_open(self, charge_retrieve_mock, subscription_retrieve_mock, default_account_mock):
        default_account_mock.return_value = self.account

        invoice_data = deepcopy(FAKE_INVOICE)
        invoice_data.update({"paid": False, "closed": False})
        invoice = Invoice.sync_from_stripe_data(invoice_data)

        self.assertEqual(Invoice.STATUS_OPEN, invoice.status)
예제 #5
0
    def test_invoice_plan_from_subscription(self, charge_retrieve_mock, subscription_retrieve_mock,
                                            default_account_mock):
        default_account_mock.return_value = self.account

        invoice_data = deepcopy(FAKE_INVOICE)
        invoice_data["lines"]["data"][0]["plan"] = None
        invoice = Invoice.sync_from_stripe_data(invoice_data)
        self.assertIsNotNone(invoice.plan)  # retrieved from subscription
        self.assertEqual(FAKE_PLAN["id"], invoice.plan.stripe_id)
예제 #6
0
def sync_invoices(apps, schema_editor):
    # This is okay, since we're only doing a forward migration.
    from djstripe.models import Invoice

    from djstripe.context_managers import stripe_temporary_api_version

    with stripe_temporary_api_version("2016-03-07"):
        if Invoice.objects.count():
            print("syncing invoices. This may take a while.")

            for invoice in tqdm(iterable=Invoice.objects.all(), desc="Sync", unit=" invoices"):
                try:
                    Invoice.sync_from_stripe_data(invoice.api_retrieve())
                except InvalidRequestError:
                    tqdm.write("There was an error while syncing invoice \
                    ({invoice_id}).".format(invoice_id=invoice.stripe_id))

            print("Invoice sync complete.")
예제 #7
0
    def test_invoice_with_non_subscription_invoice_items(self, charge_retrieve_mock, subscription_retrieve_mock,
                                                         default_account_mock):
        default_account_mock.return_value = self.account

        invoice_data = deepcopy(FAKE_INVOICE)
        invoice_data["lines"]["data"].append(deepcopy(FAKE_INVOICEITEM_II))
        invoice_data["lines"]["total_count"] += 1
        invoice = Invoice.sync_from_stripe_data(invoice_data)

        self.assertIsNotNone(invoice)
        self.assertEqual(2, len(invoice.invoiceitems.all()))
예제 #8
0
    def test_invoice_with_subscription_invoice_items(self, charge_retrieve_mock, subscription_retrieve_mock,
                                                     default_account_mock):
        default_account_mock.return_value = self.account

        invoice_data = deepcopy(FAKE_INVOICE)
        invoice = Invoice.sync_from_stripe_data(invoice_data)

        items = invoice.invoiceitems.all()
        self.assertEqual(1, len(items))
        item_id = "{invoice_id}-{subscription_id}".format(invoice_id=invoice.stripe_id,
                                                          subscription_id=FAKE_SUBSCRIPTION["id"])
        self.assertEqual(item_id, items[0].stripe_id)
예제 #9
0
    def test_retry_false(self, charge_retrieve_mock, subscription_retrieve_mock, default_account_mock,
                         invoice_retrieve_mock):
        default_account_mock.return_value = self.account

        fake_invoice = deepcopy(FAKE_INVOICE)
        invoice_retrieve_mock.return_value = fake_invoice

        invoice = Invoice.sync_from_stripe_data(fake_invoice)
        return_value = invoice.retry()

        self.assertFalse(invoice_retrieve_mock.called)
        self.assertFalse(return_value)
예제 #10
0
    def test_sync_from_stripe_data_no_plan(self):
        FAKE_INVOICE_NO_PLAN = deepcopy(FAKE_INVOICE)
        FAKE_INVOICE_NO_PLAN["id"] = "in_yyyyyyyyyyyyyyy"
        FAKE_INVOICE_NO_PLAN["subscription"] = "sub_yyyyyyyyyyyyyyy"
        FAKE_INVOICE_NO_PLAN["lines"]["data"][0]["id"] = "sub_yyyyyyyyyyyyyyy"

        FAKE_INVOICE_NO_PLAN["lines"]["data"][0]["plan"] = None

        invoice = Invoice.sync_from_stripe_data(FAKE_INVOICE_NO_PLAN)
        self.assertEqual(1, invoice.items.count())
        invoice_item = invoice.items.all()[0]

        self.assertEqual("", invoice_item.plan)
예제 #11
0
    def test_sync_no_subscription(self, charge_retrieve_mock, subscription_retrieve_mock, plan_retrieve_mock,
                                  default_account_mock):
        default_account_mock.return_value = self.account

        invoice_data = deepcopy(FAKE_INVOICE)
        invoice_data.update({"subscription": None})
        invoice = Invoice.sync_from_stripe_data(invoice_data)

        self.assertEqual(None, invoice.subscription)

        charge_retrieve_mock.assert_called_once_with(api_key=ANY, expand=ANY, id=FAKE_CHARGE["id"])
        plan_retrieve_mock.assert_called_once_with(api_key=ANY, expand=ANY, id=FAKE_PLAN["id"])

        subscription_retrieve_mock.assert_not_called()
예제 #12
0
    def test_retry_true(self, charge_retrieve_mock, subscription_retrieve_mock, default_account_mock,
                        invoice_retrieve_mock):
        default_account_mock.return_value = self.account

        fake_invoice = deepcopy(FAKE_INVOICE)
        fake_invoice.update({"paid": False, "closed": False})
        invoice_retrieve_mock.return_value = fake_invoice

        invoice = Invoice.sync_from_stripe_data(fake_invoice)
        return_value = invoice.retry()

        invoice_retrieve_mock.assert_called_once_with(id=invoice.stripe_id, api_key=settings.STRIPE_SECRET_KEY,
                                                      expand=None)
        self.assertTrue(return_value)
예제 #13
0
    def test_str(self, charge_retrieve_mock, subscription_retrive_mock, default_account_mock):
        default_account_mock.return_value = self.account
        invoice = Invoice.sync_from_stripe_data(deepcopy(FAKE_INVOICE))
        self.assertEqual(invoice.get_stripe_dashboard_url(), self.customer.get_stripe_dashboard_url())

        self.assertEqual(
            "<amount_due={amount_due}, date={date}, status={status}, stripe_id={stripe_id}>".format(
                amount_due=invoice.amount_due,
                date=invoice.date,
                status=invoice.status,
                stripe_id=invoice.stripe_id
            ),
            str(invoice)
        )
예제 #14
0
    def test_sync_no_subscription(self, charge_retrieve_mock,
                                  subscription_retrieve_mock,
                                  plan_retrieve_mock, default_account_mock):
        default_account_mock.return_value = self.account

        invoice_data = deepcopy(FAKE_INVOICE)
        invoice_data.update({"subscription": None})
        invoice = Invoice.sync_from_stripe_data(invoice_data)

        self.assertEqual(None, invoice.subscription)

        charge_retrieve_mock.assert_called_once_with(api_key=ANY,
                                                     expand=ANY,
                                                     id=FAKE_CHARGE["id"])
        plan_retrieve_mock.assert_called_once_with(api_key=ANY,
                                                   expand=ANY,
                                                   id=FAKE_PLAN["id"])

        subscription_retrieve_mock.assert_not_called()
예제 #15
0
    def test_status_forgiven_default(
        self,
        product_retrieve_mock,
        payment_intent_retrieve_mock,
        charge_retrieve_mock,
        subscription_retrieve_mock,
        balance_transaction_retrieve_mock,
        default_account_mock,
    ):
        # forgiven parameter deprecated in API 2018-11-08
        # see https://stripe.com/docs/upgrades#2018-11-08
        default_account_mock.return_value = self.account

        invoice_data = deepcopy(FAKE_INVOICE)
        invoice_data.update({"paid": False, "closed": False})
        invoice_data.pop("forgiven", None)  # TODO remove
        invoice = Invoice.sync_from_stripe_data(invoice_data)

        self.assertEqual(Invoice.STATUS_OPEN, invoice.status)
예제 #16
0
    def test_status_closed_deprecated(
        self,
        product_retrieve_mock,
        charge_retrieve_mock,
        subscription_retrieve_mock,
        balance_transaction_retrieve_mock,
        default_account_mock,
    ):
        # closed parameter deprecated in API 2018-11-08 - see https://stripe.com/docs/upgrades#2018-11-08
        default_account_mock.return_value = self.account

        invoice_data = deepcopy(FAKE_INVOICE)
        invoice_data.update({"paid": False})
        invoice_data["auto_advance"] = False

        invoice = Invoice.sync_from_stripe_data(invoice_data)

        self.assertEqual(Invoice.STATUS_CLOSED, invoice.status)
        self.assertEqual(invoice.auto_advance, invoice_data["auto_advance"])
예제 #17
0
 def test_sync_from_stripe_data(
     self,
     product_retrieve_mock,
     payment_intent_retrieve_mock,
     paymentmethod_card_retrieve_mock,
     charge_retrieve_mock,
     subscription_retrieve_mock,
     balance_transaction_retrieve_mock,
     default_account_mock,
 ):
     default_account_mock.return_value = self.account
     invoice = Invoice.sync_from_stripe_data(deepcopy(FAKE_INVOICE))
     self.assertEqual(invoice.get_stripe_dashboard_url(),
                      self.customer.get_stripe_dashboard_url())
     self.assertEqual(str(invoice),
                      "Invoice #{}".format(FAKE_INVOICE["number"]))
     self.assertGreater(len(invoice.status_transitions.keys()), 1)
     self.assert_fks(invoice,
                     expected_blank_fks=self.default_expected_blank_fks)
예제 #18
0
    def test_status_void(
        self,
        product_retrieve_mock,
        paymentmethod_card_retrieve_mock,
        payment_intent_retrieve_mock,
        charge_retrieve_mock,
        subscription_retrieve_mock,
        balance_transaction_retrieve_mock,
        default_account_mock,
    ):
        default_account_mock.return_value = self.account

        invoice_data = deepcopy(FAKE_INVOICE)
        invoice_data.update({"paid": False, "status": "void"})
        invoice = Invoice.sync_from_stripe_data(invoice_data)

        self.assertEqual(InvoiceStatus.void, invoice.status)

        self.assert_fks(invoice, expected_blank_fks=self.default_expected_blank_fks)
예제 #19
0
    def test_invoice_plan_from_subscription(
        self,
        product_retrieve_mock,
        paymentmethod_card_retrieve_mock,
        payment_intent_retrieve_mock,
        charge_retrieve_mock,
        subscription_retrieve_mock,
        balance_transaction_retrieve_mock,
        default_account_mock,
    ):
        default_account_mock.return_value = self.account

        invoice_data = deepcopy(FAKE_INVOICE)
        invoice_data["lines"]["data"][0]["plan"] = None
        invoice = Invoice.sync_from_stripe_data(invoice_data)
        self.assertIsNotNone(invoice.plan)  # retrieved from subscription
        self.assertEqual(FAKE_PLAN["id"], invoice.plan.id)

        self.assert_fks(invoice, expected_blank_fks=self.default_expected_blank_fks)
예제 #20
0
    def test_retry_true(
        self,
        charge_retrieve_mock,
        subscription_retrieve_mock,
        default_account_mock,
        invoice_retrieve_mock,
    ):
        default_account_mock.return_value = self.account

        fake_invoice = deepcopy(FAKE_INVOICE)
        fake_invoice.update({"paid": False, "closed": False})
        invoice_retrieve_mock.return_value = fake_invoice

        invoice = Invoice.sync_from_stripe_data(fake_invoice)
        return_value = invoice.retry()

        invoice_retrieve_mock.assert_called_once_with(
            id=invoice.id, api_key=STRIPE_SECRET_KEY, expand=[])
        self.assertTrue(return_value)
예제 #21
0
    def test_status_closed(self, charge_retrieve_mock,
                           subscription_retrieve_mock, default_account_mock):
        default_account_mock.return_value = self.account

        invoice_data = deepcopy(FAKE_INVOICE)
        invoice_data.update({"paid": False})
        invoice = Invoice.sync_from_stripe_data(invoice_data)

        self.assertEqual(Invoice.STATUS_CLOSED, invoice.status)

        self.assert_fks(
            invoice,
            expected_blank_fks={
                "djstripe.Account.business_logo",
                "djstripe.Charge.dispute",
                "djstripe.Charge.transfer",
                "djstripe.Customer.coupon",
                "djstripe.Plan.product",
            },
        )
예제 #22
0
def invoice_paid_to_slack(event, **kwargs):
    """Post paid invoices to Slack."""
    data = event.data["object"]

    # This is a little over-engineered
    # However, by fetching from Stripe and then syncing,
    # we ensure there isn't a race condition between the webhook and this handler
    # Also, this will work in test-mode as well as production
    invoice_id = data["id"]
    invoice = Invoice.sync_from_stripe_data(
        stripe.Invoice.retrieve(invoice_id))

    log.debug("Stripe invoice %s is paid. Posting to Slack...", invoice)
    slack_message(
        "adserver/slack/invoice-paid.slack",
        {
            "customer": invoice.customer,
            "invoice": invoice,
        },
    )
예제 #23
0
    def test_sync_from_stripe_data(
        self,
        product_retrieve_mock,
        payment_intent_retrieve_mock,
        paymentmethod_card_retrieve_mock,
        charge_retrieve_mock,
        subscription_retrieve_mock,
        balance_transaction_retrieve_mock,
        default_account_mock,
    ):
        default_account_mock.return_value = self.account
        invoice = Invoice.sync_from_stripe_data(deepcopy(FAKE_INVOICE))
        self.assertEqual(
            invoice.get_stripe_dashboard_url(), self.customer.get_stripe_dashboard_url()
        )
        self.assertEqual(str(invoice), "Invoice #{}".format(FAKE_INVOICE["number"]))
        self.assertGreater(len(invoice.status_transitions.keys()), 1)
        self.assertTrue(bool(invoice.account_country))
        self.assertTrue(bool(invoice.account_name))
        self.assertTrue(bool(invoice.collection_method))

        with self.assertWarns(DeprecationWarning):
            self.assertEqual(invoice.billing, invoice.collection_method)

        self.assertEqual(invoice.default_tax_rates.count(), 1)
        self.assertEqual(
            invoice.default_tax_rates.first().id, FAKE_TAX_RATE_EXAMPLE_1_VAT["id"]
        )

        self.assertEqual(invoice.total_tax_amounts.count(), 1)

        first_tax_amount = invoice.total_tax_amounts.first()
        self.assertEqual(
            first_tax_amount.tax_rate.id, FAKE_TAX_RATE_EXAMPLE_1_VAT["id"]
        )
        self.assertEqual(
            first_tax_amount.inclusive, FAKE_TAX_RATE_EXAMPLE_1_VAT["inclusive"]
        )
        self.assertEqual(first_tax_amount.amount, 261)

        self.assert_fks(invoice, expected_blank_fks=self.default_expected_blank_fks)
예제 #24
0
    def test_invoice_plan_from_subscription(self, charge_retrieve_mock,
                                            subscription_retrieve_mock,
                                            default_account_mock):
        default_account_mock.return_value = self.account

        invoice_data = deepcopy(FAKE_INVOICE)
        invoice_data["lines"]["data"][0]["plan"] = None
        invoice = Invoice.sync_from_stripe_data(invoice_data)
        self.assertIsNotNone(invoice.plan)  # retrieved from subscription
        self.assertEqual(FAKE_PLAN["id"], invoice.plan.id)

        self.assert_fks(
            invoice,
            expected_blank_fks={
                "djstripe.Account.business_logo",
                "djstripe.Charge.dispute",
                "djstripe.Charge.transfer",
                "djstripe.Customer.coupon",
                "djstripe.Plan.product",
            },
        )
예제 #25
0
    def test_invoice_with_non_subscription_invoice_items(
        self,
        product_retrieve_mock,
        paymentmethod_card_retrieve_mock,
        payment_intent_retrieve_mock,
        charge_retrieve_mock,
        subscription_retrieve_mock,
        balance_transaction_retrieve_mock,
        default_account_mock,
    ):
        default_account_mock.return_value = self.account

        invoice_data = deepcopy(FAKE_INVOICE)
        invoice_data["lines"]["data"].append(deepcopy(FAKE_INVOICEITEM_II))
        invoice_data["lines"]["total_count"] += 1
        invoice = Invoice.sync_from_stripe_data(invoice_data)

        self.assertIsNotNone(invoice)
        self.assertEqual(2, len(invoice.invoiceitems.all()))

        self.assert_fks(invoice, expected_blank_fks=self.default_expected_blank_fks)
예제 #26
0
    def test_sync_no_subscription(
        self,
        product_retrieve_mock,
        payment_intent_retrieve_mock,
        charge_retrieve_mock,
        subscription_retrieve_mock,
        plan_retrieve_mock,
        balance_transaction_retrieve_mock,
        default_account_mock,
    ):
        default_account_mock.return_value = self.account

        invoice_data = deepcopy(FAKE_INVOICE)
        invoice_data.update({"subscription": None})
        invoice_data["lines"]["data"][0]["subscription"] = None
        invoice = Invoice.sync_from_stripe_data(invoice_data)

        self.assertEqual(None, invoice.subscription)

        self.assertEqual(FAKE_CHARGE["id"], invoice.charge.id)
        self.assertEqual(FAKE_PLAN["id"], invoice.plan.id)

        # charge_retrieve_mock.assert_not_called()
        plan_retrieve_mock.assert_not_called()
        subscription_retrieve_mock.assert_not_called()

        self.assert_fks(
            invoice,
            expected_blank_fks={
                "djstripe.Account.branding_logo",
                "djstripe.Account.branding_icon",
                "djstripe.Charge.dispute",
                "djstripe.Charge.transfer",
                "djstripe.Customer.coupon",
                "djstripe.Invoice.subscription",
                "djstripe.PaymentIntent.on_behalf_of",
                "djstripe.PaymentIntent.payment_method",
                "djstripe.Subscription.pending_setup_intent",
            },
        )
예제 #27
0
    def test_invoice_without_plan(
        self,
        charge_retrieve_mock,
        payment_intent_retrieve_mock,
        subscription_retrieve_mock,
        balance_transaction_retrieve_mock,
        default_account_mock,
    ):
        default_account_mock.return_value = self.account

        invoice_data = deepcopy(FAKE_INVOICE)
        invoice_data["lines"]["data"][0]["plan"] = None
        invoice_data["lines"]["data"][0]["subscription"] = None
        invoice_data["subscription"] = None
        invoice = Invoice.sync_from_stripe_data(invoice_data)
        self.assertIsNone(invoice.plan)

        self.assert_fks(
            invoice,
            expected_blank_fks=self.default_expected_blank_fks
            | {"djstripe.Invoice.subscription"},
        )
예제 #28
0
    def test_str(
        self,
        product_retrieve_mock,
        charge_retrieve_mock,
        subscription_retrive_mock,
        default_account_mock,
    ):
        default_account_mock.return_value = self.account
        invoice = Invoice.sync_from_stripe_data(deepcopy(FAKE_INVOICE))
        self.assertEqual(invoice.get_stripe_dashboard_url(),
                         self.customer.get_stripe_dashboard_url())
        self.assertEqual(str(invoice), "Invoice #XXXXXXX-0001")

        self.assert_fks(
            invoice,
            expected_blank_fks={
                "djstripe.Account.business_logo",
                "djstripe.Charge.dispute",
                "djstripe.Charge.transfer",
                "djstripe.Customer.coupon",
            },
        )
예제 #29
0
    def test_status_paid(
        self,
        product_retrieve_mock,
        charge_retrieve_mock,
        subscription_retrieve_mock,
        default_account_mock,
    ):
        default_account_mock.return_value = self.account

        invoice = Invoice.sync_from_stripe_data(deepcopy(FAKE_INVOICE))

        self.assertEqual(Invoice.STATUS_PAID, invoice.status)

        self.assert_fks(
            invoice,
            expected_blank_fks={
                "djstripe.Account.business_logo",
                "djstripe.Charge.dispute",
                "djstripe.Charge.transfer",
                "djstripe.Customer.coupon",
            },
        )
예제 #30
0
    def test_invoice_with_subscription_invoice_items(
        self,
        product_retrieve_mock,
        payment_intent_retrieve_mock,
        charge_retrieve_mock,
        subscription_retrieve_mock,
        balance_transaction_retrieve_mock,
        default_account_mock,
    ):
        default_account_mock.return_value = self.account

        invoice_data = deepcopy(FAKE_INVOICE)
        invoice = Invoice.sync_from_stripe_data(invoice_data)

        items = invoice.invoiceitems.all()
        self.assertEqual(1, len(items))

        # Previously the test asserted item_id="{invoice_id}-{subscription_id}",
        # but this doesn't match what I'm seeing from Stripe
        # I'm not sure if it's possible to predict the whole item id now,
        # sli seems to not reference anything
        item_id_prefix = "{invoice_id}-sli_".format(invoice_id=invoice.id)
        self.assertTrue(items[0].id.startswith(item_id_prefix))
        self.assertEqual(items[0].subscription.id, FAKE_SUBSCRIPTION["id"])

        self.assert_fks(
            invoice,
            expected_blank_fks={
                "djstripe.Account.branding_logo",
                "djstripe.Account.branding_icon",
                "djstripe.Charge.dispute",
                "djstripe.Charge.transfer",
                "djstripe.Customer.coupon",
                "djstripe.PaymentIntent.on_behalf_of",
                "djstripe.PaymentIntent.payment_method",
                "djstripe.Subscription.pending_setup_intent",
            },
        )
예제 #31
0
    def test_retry_false(
        self,
        product_retrieve_mock,
        paymentmethod_card_retrieve_mock,
        payment_intent_retrieve_mock,
        charge_retrieve_mock,
        subscription_retrieve_mock,
        balance_transaction_retrieve_mock,
        default_account_mock,
        invoice_retrieve_mock,
    ):
        default_account_mock.return_value = self.account

        fake_invoice = deepcopy(FAKE_INVOICE)
        invoice_retrieve_mock.return_value = fake_invoice

        invoice = Invoice.sync_from_stripe_data(fake_invoice)
        return_value = invoice.retry()

        self.assertFalse(invoice_retrieve_mock.called)
        self.assertFalse(return_value)

        self.assert_fks(invoice, expected_blank_fks=self.default_expected_blank_fks)
예제 #32
0
    def test_invoice_with_non_subscription_invoice_items(
            self, charge_retrieve_mock, subscription_retrieve_mock,
            default_account_mock):
        default_account_mock.return_value = self.account

        invoice_data = deepcopy(FAKE_INVOICE)
        invoice_data["lines"]["data"].append(deepcopy(FAKE_INVOICEITEM_II))
        invoice_data["lines"]["total_count"] += 1
        invoice = Invoice.sync_from_stripe_data(invoice_data)

        self.assertIsNotNone(invoice)
        self.assertEqual(2, len(invoice.invoiceitems.all()))

        self.assert_fks(
            invoice,
            expected_blank_fks={
                "djstripe.Account.business_logo",
                "djstripe.Charge.dispute",
                "djstripe.Charge.transfer",
                "djstripe.Customer.coupon",
                "djstripe.Plan.product",
            },
        )
예제 #33
0
    def test_retry_true(
        self,
        product_retrieve_mock,
        payment_intent_retrieve_mock,
        charge_retrieve_mock,
        subscription_retrieve_mock,
        balance_transaction_retrieve_mock,
        default_account_mock,
        invoice_retrieve_mock,
    ):
        default_account_mock.return_value = self.account

        fake_invoice = deepcopy(FAKE_INVOICE)
        fake_invoice.update({"paid": False, "closed": False})
        invoice_retrieve_mock.return_value = fake_invoice

        invoice = Invoice.sync_from_stripe_data(fake_invoice)
        return_value = invoice.retry()

        invoice_retrieve_mock.assert_called_once_with(
            id=invoice.id, api_key=STRIPE_SECRET_KEY, expand=[]
        )
        self.assertTrue(return_value)

        self.assert_fks(
            invoice,
            expected_blank_fks={
                "djstripe.Account.branding_logo",
                "djstripe.Account.branding_icon",
                "djstripe.Charge.dispute",
                "djstripe.Charge.transfer",
                "djstripe.Customer.coupon",
                "djstripe.PaymentIntent.on_behalf_of",
                "djstripe.PaymentIntent.payment_method",
                "djstripe.Subscription.pending_setup_intent",
            },
        )
예제 #34
0
    def test_invoice_with_subscription_invoice_items(
            self, charge_retrieve_mock, subscription_retrieve_mock,
            default_account_mock):
        default_account_mock.return_value = self.account

        invoice_data = deepcopy(FAKE_INVOICE)
        invoice = Invoice.sync_from_stripe_data(invoice_data)

        items = invoice.invoiceitems.all()
        self.assertEqual(1, len(items))
        item_id = "{invoice_id}-{subscription_id}".format(
            invoice_id=invoice.id, subscription_id=FAKE_SUBSCRIPTION["id"])
        self.assertEqual(item_id, items[0].id)

        self.assert_fks(
            invoice,
            expected_blank_fks={
                "djstripe.Account.business_logo",
                "djstripe.Charge.dispute",
                "djstripe.Charge.transfer",
                "djstripe.Customer.coupon",
                "djstripe.Plan.product",
            },
        )
예제 #35
0
    def test_sync_from_stripe_data_default_payment_method(
        self,
        product_retrieve_mock,
        payment_intent_retrieve_mock,
        paymentmethod_card_retrieve_mock,
        charge_retrieve_mock,
        subscription_retrieve_mock,
        balance_transaction_retrieve_mock,
        default_account_mock,
    ):
        default_account_mock.return_value = self.account
        fake_invoice = deepcopy(FAKE_INVOICE)
        fake_invoice["default_payment_method"] = deepcopy(FAKE_CARD_AS_PAYMENT_METHOD)
        invoice = Invoice.sync_from_stripe_data(fake_invoice)

        self.assertEqual(
            invoice.default_payment_method.id, FAKE_CARD_AS_PAYMENT_METHOD["id"]
        )

        self.assert_fks(
            invoice,
            expected_blank_fks=self.default_expected_blank_fks
            - {"djstripe.Invoice.default_payment_method"},
        )
예제 #36
0
    def test_status_paid(self, charge_retrieve_mock, subscription_retrieve_mock, default_account_mock):
        default_account_mock.return_value = self.account

        invoice = Invoice.sync_from_stripe_data(deepcopy(FAKE_INVOICE))

        self.assertEqual(Invoice.STATUS_PAID, invoice.status)
예제 #37
0
    def test_status_paid(self, charge_retrieve_mock, subscription_retrieve_mock, default_account_mock):
        default_account_mock.return_value = self.account

        invoice = Invoice.sync_from_stripe_data(deepcopy(FAKE_INVOICE))

        self.assertEqual(Invoice.STATUS_PAID, invoice.status)
예제 #38
0
    def action_create_draft_invoice(self, request, queryset):
        """
        Create a draft invoice for selected flights with metadata attached.

        This fails with a message if the flights aren't all from the same advertiser.
        """
        # TODO: convert to using djstripe and tie FK to flights
        if not settings.STRIPE_ENABLED:
            messages.add_message(
                request,
                messages.ERROR,
                _("Stripe is not configured. Please set the envvar STRIPE_SECRET_KEY."
                  ),
            )
            return

        flights = list(
            queryset.select_related("campaign", "campaign__advertiser"))

        if not flights:
            # Django actually doesn't take this path and instead shows its own error message
            return  # pragma: no cover
        if len({f.campaign.advertiser_id for f in flights}) > 1:
            messages.add_message(
                request,
                messages.ERROR,
                _("All selected flights must be from a single advertiser."),
            )
            return

        earliest_start_date = min([f.start_date for f in flights])
        latest_end_date = max([f.end_date for f in flights])
        advertiser = [f.campaign.advertiser for f in flights][0]

        if not advertiser.djstripe_customer:
            messages.add_message(
                request,
                messages.ERROR,
                _("No Stripe customer ID for {}".format(advertiser)),
            )
            return

        for flight in flights:
            message_components = ["Advertising", flight.name]
            unit_amount = 0
            quantity = 1

            if flight.cpc:
                message_components.append("per click")
                unit_amount = int(flight.cpc * 100)  # Convert to US cents
                quantity = flight.sold_clicks
            elif flight.cpm:
                message_components.append("per 1k impressions")
                unit_amount = int(flight.cpm * 100)  # Convert to US cents
                quantity = flight.sold_impressions // 1000

            # Amounts, prices, and description can be customized before sending
            stripe.InvoiceItem.create(
                customer=advertiser.djstripe_customer.id,
                description=" - ".join(message_components),
                quantity=quantity,
                unit_amount=unit_amount,  # in US cents
                currency="USD",
                metadata={
                    "Advertiser": advertiser.slug,
                    "Flight": flight.slug,
                    "Flight Start": flight.start_date.strftime("%Y-%m-%d"),
                    "Flight End": flight.end_date.strftime("%Y-%m-%d"),
                },
            )

        # https://stripe.com/docs/api/invoices/create
        inv = stripe.Invoice.create(
            customer=advertiser.djstripe_customer.id,
            auto_advance=False,  # Draft invoice
            collection_method="send_invoice",
            custom_fields=[
                {
                    "name": "Advertiser",
                    "value": advertiser.slug
                },
                {
                    "name": "Estimated Start",
                    "value": earliest_start_date.strftime("%Y-%m-%d"),
                },
                {
                    "name": "Estimated End",
                    "value": latest_end_date.strftime("%Y-%m-%d"),
                },
            ],
            days_until_due=30,
        )
        invoice = Invoice.sync_from_stripe_data(inv)

        # Attach Stripe invoices to flights
        # There isn't a good way to mock invoices for tests so this check is to mock the invoice
        if invoice.pk:
            for flight in flights:
                flight.invoices.add(invoice)

        messages.add_message(
            request,
            messages.SUCCESS,
            _("New Stripe invoice for {}: {}".format(
                advertiser, invoice.get_stripe_dashboard_url())),
        )
예제 #39
0
    def test_sync_from_stripe_data_update_total_tax_amounts(
        self,
        product_retrieve_mock,
        payment_intent_retrieve_mock,
        paymentmethod_card_retrieve_mock,
        charge_retrieve_mock,
        subscription_retrieve_mock,
        balance_transaction_retrieve_mock,
        default_account_mock,
    ):
        default_account_mock.return_value = self.account
        invoice = Invoice.sync_from_stripe_data(deepcopy(FAKE_INVOICE))

        # as per basic sync test
        self.assertEqual(invoice.default_tax_rates.count(), 1)
        self.assertEqual(
            invoice.default_tax_rates.first().id, FAKE_TAX_RATE_EXAMPLE_1_VAT["id"]
        )

        self.assertEqual(invoice.total_tax_amounts.count(), 1)

        first_tax_amount = invoice.total_tax_amounts.first()
        self.assertEqual(
            first_tax_amount.tax_rate.id, FAKE_TAX_RATE_EXAMPLE_1_VAT["id"]
        )
        self.assertEqual(
            first_tax_amount.inclusive, FAKE_TAX_RATE_EXAMPLE_1_VAT["inclusive"]
        )
        self.assertEqual(first_tax_amount.amount, 261)
        self.assert_fks(invoice, expected_blank_fks=self.default_expected_blank_fks)

        # Now update with a different tax rate
        # TODO - should update tax rate in invoice items etc as well,
        #  but here we're mainly testing that invoice.total_tax_rates is
        #  correctly updated
        fake_updated_invoice = deepcopy(FAKE_INVOICE)
        fake_tax_rate_2 = deepcopy(FAKE_TAX_RATE_EXAMPLE_2_SALES)

        new_tax_amount = int(
            fake_updated_invoice["total"] * fake_tax_rate_2["percentage"] / 100
        )

        fake_updated_invoice.update(
            {
                "default_tax_rates": [fake_tax_rate_2],
                "tax": new_tax_amount,
                "total": fake_updated_invoice["total"] + new_tax_amount,
                "total_tax_amounts": [
                    {
                        "amount": new_tax_amount,
                        "inclusive": False,
                        "tax_rate": fake_tax_rate_2["id"],
                    }
                ],
            }
        )

        invoice_updated = Invoice.sync_from_stripe_data(fake_updated_invoice)

        self.assertEqual(invoice_updated.default_tax_rates.count(), 1)
        self.assertEqual(
            invoice_updated.default_tax_rates.first().id, fake_tax_rate_2["id"]
        )

        self.assertEqual(invoice_updated.total_tax_amounts.count(), 1)

        first_tax_amount = invoice_updated.total_tax_amounts.first()
        self.assertEqual(first_tax_amount.tax_rate.id, fake_tax_rate_2["id"])
        self.assertEqual(first_tax_amount.inclusive, fake_tax_rate_2["inclusive"])
        self.assertEqual(first_tax_amount.amount, new_tax_amount)
        self.assert_fks(
            invoice_updated, expected_blank_fks=self.default_expected_blank_fks
        )