Example #1
0
	def test_calculate_refund_above_max_refund(self):
		charge = Charge(
			id="ch_111111", customer=self.customer, amount=decimal.Decimal("500.00")
		)
		self.assertEqual(
			charge._calculate_refund_amount(amount=decimal.Decimal("600.00")), 50000
		)
Example #2
0
	def test__attach_objects_hook_missing_source_data(
		self, mock_account, mock_payment_method, mock_charge_account, mock_charge_source
	):
		"""Make sure we handle the case where the source data is empty or insufficient."""
		charge = Charge(
			amount=50,
			currency="usd",
			id="ch_test",
			status=ChargeStatus.failed,
			captured=False,
			paid=False,
		)
		mock_cls = create_autospec(spec=Charge, spec_set=True)
		# Empty data dict works for this test since we only look up the source key and
		# everything else is mocked.
		mock_data = {}
		starting_source = charge.source

		charge._attach_objects_hook(cls=mock_cls, data=mock_data)

		# source shouldn't be touched
		self.assertEqual(starting_source, charge.source)
		mock_payment_method._get_or_create_source.assert_not_called()

		# try again with a source key, but no object sub key.
		mock_data = {"source": {"foo": "bar"}}

		charge._attach_objects_hook(cls=mock_cls, data=mock_data)

		# source shouldn't be touched
		self.assertEqual(starting_source, charge.source)
		mock_payment_method._get_or_create_source.assert_not_called()
Example #3
0
	def test_sync_from_stripe_data_no_customer(
		self, charge_retrieve_mock, balance_transaction_retrieve_mock, default_account_mock
	):
		from djstripe.settings import STRIPE_SECRET_KEY

		default_account_mock.return_value = self.account

		fake_charge_copy = deepcopy(FAKE_CHARGE)

		fake_charge_copy.pop("customer", None)
		# remove invoice since it requires a customer
		fake_charge_copy.pop("invoice", None)

		Charge.sync_from_stripe_data(fake_charge_copy)
		assert Charge.objects.count() == 1
		charge = Charge.objects.get()
		assert charge.customer is None

		charge_retrieve_mock.assert_not_called()
		balance_transaction_retrieve_mock.assert_called_once_with(
			api_key=STRIPE_SECRET_KEY, expand=[], id=FAKE_BALANCE_TRANSACTION["id"]
		)

		self.assert_fks(
			charge,
			expected_blank_fks={
				"djstripe.Account.business_logo",
				"djstripe.Charge.customer",
				"djstripe.Charge.dispute",
				"djstripe.Charge.invoice",
				"djstripe.Charge.transfer",
				"djstripe.Customer.coupon",
				"djstripe.Plan.product",
			},
		)
Example #4
0
 def test_calculate_refund_amount_full_refund(self):
     charge = Charge(
         stripe_id="ch_111111",
         customer=self.customer,
         amount=decimal.Decimal("500.00")
     )
     self.assertEqual(charge._calculate_refund_amount(), 50000)
Example #5
0
	def test__attach_objects_hook_no_destination_account(
		self, mock_account, mock_payment_method, mock_charge_account, mock_charge_source
	):
		"""Test that _attach_objects_hook works as expected when there is no destination account."""
		charge = Charge(
			amount=50,
			currency="usd",
			id="ch_test",
			status=ChargeStatus.failed,
			captured=False,
			paid=False,
		)
		mock_cls = create_autospec(spec=Charge, spec_set=True)
		mock_cls._stripe_object_destination_to_account.return_value = False
		mock_data = {"source": {"object": "foo"}}
		mock_payment_method._get_or_create_source.return_value = ("bar", "unused")

		charge._attach_objects_hook(cls=mock_cls, data=mock_data)

		# expect the attributes to be set appropriately
		self.assertEqual(mock_account.get_default_account.return_value, charge.account)
		self.assertEqual(
			mock_payment_method._get_or_create_source.return_value[0], charge.source
		)
		# expect the appropriate calls to be made
		mock_cls._stripe_object_destination_to_account.assert_called_once_with(
			target_cls=mock_account, data=mock_data
		)
		mock_payment_method._get_or_create_source.assert_called_once_with(
			data=mock_data["source"], source_type=mock_data["source"]["object"]
		)
Example #6
0
    def test_sync_from_stripe_data_no_customer(self, default_account_mock):
        default_account_mock.return_value = self.account

        fake_charge_copy = deepcopy(FAKE_CHARGE)
        fake_charge_copy.pop("customer", None)

        Charge.sync_from_stripe_data(fake_charge_copy)
        assert Charge.objects.count() == 1
        charge = Charge.objects.get()
        assert charge.customer is None
Example #7
0
 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
     )
Example #8
0
	def test_str(self):
		charge = Charge(
			amount=50,
			currency="usd",
			id="ch_test",
			status=ChargeStatus.failed,
			captured=False,
			paid=False,
		)
		self.assertEqual(str(charge), "$50.00 USD (Uncaptured)")

		charge.captured = True
		self.assertEqual(str(charge), "$50.00 USD (Failed)")
		charge.status = ChargeStatus.succeeded

		charge.dispute = Dispute()
		self.assertEqual(str(charge), "$50.00 USD (Disputed)")

		charge.dispute = None
		charge.refunded = True
		charge.amount_refunded = 50
		self.assertEqual(str(charge), "$50.00 USD (Refunded)")

		charge.refunded = False
		self.assertEqual(str(charge), "$50.00 USD (Partially refunded)")

		charge.amount_refunded = 0
		self.assertEqual(str(charge), "$50.00 USD")
Example #9
0
	def test_capture_charge(
		self, balance_transaction_retrieve_mock, charge_retrieve_mock, default_account_mock
	):
		default_account_mock.return_value = self.account

		fake_charge_no_invoice = deepcopy(FAKE_CHARGE)
		fake_charge_no_invoice.update({"invoice": None})

		charge_retrieve_mock.return_value = fake_charge_no_invoice

		charge, created = Charge._get_or_create_from_stripe_object(fake_charge_no_invoice)
		self.assertTrue(created)

		captured_charge = charge.capture()
		self.assertTrue(captured_charge.captured)

		self.assert_fks(
			charge,
			expected_blank_fks={
				"djstripe.Account.business_logo",
				"djstripe.Charge.dispute",
				"djstripe.Charge.invoice",
				"djstripe.Charge.transfer",
				"djstripe.Customer.coupon",
				"djstripe.Plan.product",
			},
		)
Example #10
0
	def test_sync_from_stripe_data_max_amount(
		self,
		default_account_mock,
		subscription_retrieve_mock,
		product_retrieve_mock,
		invoice_retrieve_mock,
		charge_retrieve_mock,
		balance_transaction_retrieve_mock,
	):
		default_account_mock.return_value = self.account

		fake_charge_copy = deepcopy(FAKE_CHARGE)
		# https://support.stripe.com/questions/what-is-the-maximum-amount-i-can-charge-with-stripe
		fake_charge_copy.update({"amount": 99999999})

		charge = Charge.sync_from_stripe_data(fake_charge_copy)

		self.assertEqual(Decimal("999999.99"), charge.amount)
		self.assertEqual(True, charge.paid)
		self.assertEqual(False, charge.refunded)
		self.assertEqual(True, charge.captured)
		self.assertEqual(False, charge.disputed)
		self.assertEqual(0, charge.amount_refunded)

		charge_retrieve_mock.assert_not_called()

		self.assert_fks(
			charge,
			expected_blank_fks={
				"djstripe.Account.business_logo",
				"djstripe.Charge.dispute",
				"djstripe.Charge.transfer",
				"djstripe.Customer.coupon",
			},
		)
def sync_charges(apps, schema_editor):
    # This is okay, since we're only doing a forward migration.
    from djstripe.models import Charge

    from djstripe.context_managers import stripe_temporary_api_version

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

            for charge in tqdm(Charge.objects.all(), desc="Sync", unit=" charges"):
                try:
                    Charge.sync_from_stripe_data(charge.api_retrieve())
                except InvalidRequestError:
                    tqdm.write("There was an error while syncing charge ({charge_id}).".format(charge_id=charge.stripe_id))

            print("Charge sync complete.")
Example #12
0
    def test_refund_charge(self, charge_retrieve_mock, default_account_mock):
        default_account_mock.return_value = self.account

        fake_charge_no_invoice = deepcopy(FAKE_CHARGE)
        fake_charge_no_invoice.update({"invoice": None})

        charge_retrieve_mock.return_value = fake_charge_no_invoice

        charge, created = Charge._get_or_create_from_stripe_object(fake_charge_no_invoice)
        self.assertTrue(created)

        charge.refund()

        refunded_charge, created2 = Charge._get_or_create_from_stripe_object(fake_charge_no_invoice)
        self.assertFalse(created2)

        self.assertEqual(refunded_charge.refunded, True)
        self.assertEqual(refunded_charge.amount_refunded, decimal.Decimal("22.00"))
Example #13
0
	def test_refund_charge(
		self, charge_retrieve_mock, balance_transaction_retrieve_mock, default_account_mock
	):
		default_account_mock.return_value = self.account

		fake_charge_no_invoice = deepcopy(FAKE_CHARGE)
		fake_charge_no_invoice.update({"invoice": None})

		charge_retrieve_mock.return_value = fake_charge_no_invoice

		charge, created = Charge._get_or_create_from_stripe_object(fake_charge_no_invoice)
		self.assertTrue(created)

		self.assert_fks(
			charge,
			expected_blank_fks={
				"djstripe.Account.business_logo",
				"djstripe.Charge.dispute",
				"djstripe.Charge.invoice",
				"djstripe.Charge.transfer",
				"djstripe.Customer.coupon",
			},
		)

		charge.refund()

		refunded_charge, created2 = Charge._get_or_create_from_stripe_object(
			fake_charge_no_invoice
		)
		self.assertFalse(created2)

		self.assertEqual(refunded_charge.refunded, True)
		self.assertEqual(refunded_charge.amount_refunded, decimal.Decimal("22.00"))

		self.assert_fks(
			refunded_charge,
			expected_blank_fks={
				"djstripe.Account.business_logo",
				"djstripe.Charge.dispute",
				"djstripe.Charge.invoice",
				"djstripe.Charge.transfer",
				"djstripe.Customer.coupon",
			},
		)
Example #14
0
	def test_sync_from_stripe_data_refunded(
		self,
		subscription_retrieve_mock,
		product_retrieve_mock,
		invoice_retrieve_mock,
		charge_retrieve_mock,
		balance_transaction_retrieve_mock,
		default_account_mock,
	):
		from djstripe.settings import STRIPE_SECRET_KEY

		default_account_mock.return_value = self.account
		fake_charge_copy = deepcopy(FAKE_CHARGE_REFUNDED)

		charge = Charge.sync_from_stripe_data(fake_charge_copy)

		self.assertEqual(Decimal("22"), charge.amount)
		self.assertEqual(True, charge.paid)
		self.assertEqual(True, charge.refunded)
		self.assertEqual(True, charge.captured)
		self.assertEqual(False, charge.disputed)
		self.assertEqual("VideoDoc consultation for ivanp0001 berkp0001", charge.description)
		self.assertEqual(charge.amount, charge.amount_refunded)

		charge_retrieve_mock.assert_not_called()

		# We expect two calls - for charge and then for charge.refunds
		balance_transaction_retrieve_mock.assert_has_calls(
			[
				call(api_key=STRIPE_SECRET_KEY, expand=[], id=FAKE_BALANCE_TRANSACTION["id"]),
				call(
					api_key=STRIPE_SECRET_KEY, expand=[], id=FAKE_BALANCE_TRANSACTION_REFUND["id"]
				),
			]
		)

		refunds = list(charge.refunds.all())
		self.assertEqual(len(refunds), 1)

		refund = refunds[0]

		self.assertEqual(refund.id, FAKE_REFUND["id"])

		self.assertNotEqual(charge.balance_transaction.id, refund.balance_transaction.id)
		self.assertEqual(charge.balance_transaction.id, FAKE_BALANCE_TRANSACTION["id"])
		self.assertEqual(refund.balance_transaction.id, FAKE_BALANCE_TRANSACTION_REFUND["id"])

		self.assert_fks(
			charge,
			expected_blank_fks={
				"djstripe.Account.business_logo",
				"djstripe.Charge.dispute",
				"djstripe.Charge.transfer",
				"djstripe.Customer.coupon",
			},
		)
Example #15
0
    def test_sync_from_stripe_data_unsupported_source(self, default_account_mock):
        default_account_mock.return_value = self.account

        fake_charge_copy = deepcopy(FAKE_CHARGE)
        fake_charge_copy.update({"source": {"id": "test_id", "object": "unsupported"}})

        charge = Charge.sync_from_stripe_data(fake_charge_copy)
        self.assertEqual("test_id", charge.source_stripe_id)
        self.assertEqual("unsupported", charge.source_type)
        self.assertEqual(charge.source, PaymentMethod.objects.get(id="test_id"))
Example #16
0
    def test_capture_charge(self, charge_retrieve_mock, default_account_mock):
        default_account_mock.return_value = self.account

        fake_charge_no_invoice = deepcopy(FAKE_CHARGE)
        fake_charge_no_invoice.update({"invoice": None})

        charge_retrieve_mock.return_value = fake_charge_no_invoice

        charge, created = Charge._get_or_create_from_stripe_object(fake_charge_no_invoice)
        self.assertTrue(created)

        captured_charge = charge.capture()
        self.assertTrue(captured_charge.captured)
Example #17
0
    def test_sync_from_stripe_data(self):
        charge = Charge.sync_from_stripe_data(FAKE_CHARGE)

        self.assertEqual(self.invoice, charge.invoice)
        self.assertEqual("9999", charge.card_last_4)
        self.assertEqual("Visa", charge.card_kind)
        self.assertEqual(Decimal("9.95"), charge.amount)
        self.assertEqual(True, charge.paid)
        self.assertEqual(False, charge.refunded)
        self.assertEqual(True, charge.captured)
        self.assertEqual(Decimal("0"), charge.fee)
        self.assertEqual(False, charge.disputed)
        self.assertEqual("test_description", charge.description)
        self.assertEqual(None, charge.amount_refunded)
Example #18
0
    def test_sync_from_stripe_data_with_destination(self, account_retrieve_mock, charge_retrieve_mock):
        account_retrieve_mock.return_value = FAKE_ACCOUNT

        fake_charge_copy = deepcopy(FAKE_CHARGE)
        fake_charge_copy.update({"destination": FAKE_ACCOUNT["id"]})

        charge_retrieve_mock.return_value = fake_charge_copy

        charge, created = Charge._get_or_create_from_stripe_object(fake_charge_copy)
        self.assertTrue(created)

        self.assertEqual(2, Account.objects.count())
        account = Account.objects.get(stripe_id=FAKE_ACCOUNT["id"])

        self.assertEqual(account, charge.account)
Example #19
0
    def test_sync_from_stripe_data_max_amount(self, default_account_mock):
        default_account_mock.return_value = self.account

        fake_charge_copy = deepcopy(FAKE_CHARGE)
        # https://support.stripe.com/questions/what-is-the-maximum-amount-i-can-charge-with-stripe
        fake_charge_copy.update({"amount": 99999999})

        charge = Charge.sync_from_stripe_data(fake_charge_copy)

        self.assertEqual(Decimal("999999.99"), charge.amount)
        self.assertEqual(True, charge.paid)
        self.assertEqual(False, charge.refunded)
        self.assertEqual(True, charge.captured)
        self.assertEqual(False, charge.disputed)
        self.assertEqual(0, charge.amount_refunded)
Example #20
0
    def test_sync_from_stripe_data_with_transfer(self, default_account_mock, transfer_retrieve_mock,
                                                 charge_retrieve_mock):
        default_account_mock.return_value = self.account

        fake_transfer = deepcopy(FAKE_TRANSFER)

        fake_charge_copy = deepcopy(FAKE_CHARGE)
        fake_charge_copy.update({"transfer": fake_transfer["id"]})

        transfer_retrieve_mock.return_value = fake_transfer
        charge_retrieve_mock.return_value = fake_charge_copy

        charge, created = Charge._get_or_create_from_stripe_object(fake_charge_copy)
        self.assertTrue(created)

        self.assertNotEqual(None, charge.transfer)
        self.assertEqual(fake_transfer["id"], charge.transfer.stripe_id)
Example #21
0
    def test_sync_from_stripe_data(self, default_account_mock):
        default_account_mock.return_value = self.account

        fake_charge_copy = deepcopy(FAKE_CHARGE)
        fake_charge_copy.update({"application_fee": {"amount": 0}})

        charge = Charge.sync_from_stripe_data(FAKE_CHARGE)

        self.assertEqual(Decimal("22"), charge.amount)
        self.assertEqual(True, charge.paid)
        self.assertEqual(False, charge.refunded)
        self.assertEqual(True, charge.captured)
        self.assertEqual(False, charge.disputed)
        self.assertEqual("VideoDoc consultation for ivanp0001 berkp0001", charge.description)
        self.assertEqual(0, charge.amount_refunded)

        self.assertEqual("card_16YKQh2eZvKYlo2Cblc5Feoo", charge.source_stripe_id)
        self.assertEqual(charge.source_type, LegacySourceType.card)
Example #22
0
	def test_sync_from_stripe_data_with_transfer(
		self,
		default_account_mock,
		subscription_retrieve_mock,
		product_retrieve_mock,
		transfer_retrieve_mock,
		invoice_retrieve_mock,
		charge_retrieve_mock,
		balance_transaction_retrieve_mock,
	):
		from djstripe.settings import STRIPE_SECRET_KEY

		default_account_mock.return_value = self.account

		fake_transfer = deepcopy(FAKE_TRANSFER)

		fake_charge_copy = deepcopy(FAKE_CHARGE)
		fake_charge_copy.update({"transfer": fake_transfer["id"]})

		transfer_retrieve_mock.return_value = fake_transfer
		charge_retrieve_mock.return_value = fake_charge_copy

		charge, created = Charge._get_or_create_from_stripe_object(
			fake_charge_copy, current_ids={fake_charge_copy["id"]}
		)
		self.assertTrue(created)

		self.assertNotEqual(None, charge.transfer)
		self.assertEqual(fake_transfer["id"], charge.transfer.id)

		charge_retrieve_mock.assert_not_called()
		balance_transaction_retrieve_mock.assert_called_once_with(
			api_key=STRIPE_SECRET_KEY, expand=[], id=FAKE_BALANCE_TRANSACTION["id"]
		)

		self.assert_fks(
			charge,
			expected_blank_fks={
				"djstripe.Account.business_logo",
				"djstripe.Charge.dispute",
				"djstripe.Customer.coupon",
			},
		)
Example #23
0
	def test_sync_from_stripe_data(
		self,
		subscription_retrieve_mock,
		product_retrieve_mock,
		invoice_retrieve_mock,
		charge_retrieve_mock,
		balance_transaction_retrieve_mock,
		default_account_mock,
	):
		from djstripe.settings import STRIPE_SECRET_KEY

		default_account_mock.return_value = self.account

		fake_charge_copy = deepcopy(FAKE_CHARGE)
		fake_charge_copy.update({"application_fee": {"amount": 0}})

		charge = Charge.sync_from_stripe_data(fake_charge_copy)

		self.assertEqual(Decimal("22"), charge.amount)
		self.assertEqual(True, charge.paid)
		self.assertEqual(False, charge.refunded)
		self.assertEqual(True, charge.captured)
		self.assertEqual(False, charge.disputed)
		self.assertEqual("VideoDoc consultation for ivanp0001 berkp0001", charge.description)
		self.assertEqual(0, charge.amount_refunded)

		self.assertEqual("card_16YKQh2eZvKYlo2Cblc5Feoo", charge.source_id)
		self.assertEqual(charge.source.type, LegacySourceType.card)

		charge_retrieve_mock.assert_not_called()
		balance_transaction_retrieve_mock.assert_called_once_with(
			api_key=STRIPE_SECRET_KEY, expand=[], id=FAKE_BALANCE_TRANSACTION["id"]
		)

		self.assert_fks(
			charge,
			expected_blank_fks={
				"djstripe.Account.business_logo",
				"djstripe.Charge.dispute",
				"djstripe.Charge.transfer",
				"djstripe.Customer.coupon",
			},
		)
Example #24
0
	def test_sync_from_stripe_data_with_destination(
		self,
		file_retrive_mock,
		invoice_retrieve_mock,
		subscription_retrieve_mock,
		product_retrieve_mock,
		balance_transaction_retrieve_mock,
		account_retrieve_mock,
		charge_retrieve_mock,
	):
		from djstripe.settings import STRIPE_SECRET_KEY

		account_retrieve_mock.return_value = FAKE_ACCOUNT

		fake_charge_copy = deepcopy(FAKE_CHARGE)
		fake_charge_copy.update({"destination": FAKE_ACCOUNT["id"]})

		charge, created = Charge._get_or_create_from_stripe_object(
			fake_charge_copy, current_ids={fake_charge_copy["id"]}
		)
		self.assertTrue(created)

		self.assertEqual(2, Account.objects.count())
		account = Account.objects.get(id=FAKE_ACCOUNT["id"])

		self.assertEqual(account, charge.account)

		charge_retrieve_mock.assert_not_called()
		balance_transaction_retrieve_mock.assert_called_once_with(
			api_key=STRIPE_SECRET_KEY, expand=[], id=FAKE_BALANCE_TRANSACTION["id"]
		)

		self.assert_fks(
			charge,
			expected_blank_fks={
				"djstripe.Charge.dispute",
				"djstripe.Charge.transfer",
				"djstripe.Customer.coupon",
			},
		)
Example #25
0
	def test_sync_from_stripe_data_unsupported_source(
		self,
		invoice_retrieve_mock,
		subscription_retrieve_mock,
		product_retrieve_mock,
		charge_retrieve_mock,
		balance_transaction_retrieve_mock,
		default_account_mock,
	):
		from djstripe.settings import STRIPE_SECRET_KEY

		default_account_mock.return_value = self.account

		fake_charge_copy = deepcopy(FAKE_CHARGE)
		fake_charge_copy.update({"source": {"id": "test_id", "object": "unsupported"}})

		charge = Charge.sync_from_stripe_data(fake_charge_copy)
		self.assertEqual("test_id", charge.source_id)
		self.assertEqual("unsupported", charge.source.type)
		self.assertEqual(charge.source, DjstripePaymentMethod.objects.get(id="test_id"))

		# alias to old model name should work the same
		self.assertEqual(charge.source, PaymentMethod.objects.get(id="test_id"))

		charge_retrieve_mock.assert_not_called()

		balance_transaction_retrieve_mock.assert_called_once_with(
			api_key=STRIPE_SECRET_KEY, expand=[], id=FAKE_BALANCE_TRANSACTION["id"]
		)

		self.assert_fks(
			charge,
			expected_blank_fks={
				"djstripe.Account.business_logo",
				"djstripe.Charge.dispute",
				"djstripe.Charge.transfer",
				"djstripe.Customer.coupon",
			},
		)
Example #26
0
 def test_calculate_refund_amount_full_refund(self):
     charge = Charge(stripe_id="ch_111111",
                     customer=self.customer,
                     amount=decimal.Decimal("500.00"))
     self.assertEqual(charge._calculate_refund_amount(), 50000)
Example #27
0
    def test_send_receipt_not_sent(self, get_current_mock):
        charge = Charge(receipt_sent=True)
        charge.send_receipt()

        # Assert the condition caused exit
        self.assertFalse(get_current_mock.called)
Example #28
0
class InvoiceTest(TestCase):

    def setUp(self):
        self.customer = Customer.objects.create(stripe_id="cus_xxxxxxxxxxxxxxx")
        self.invoice = Invoice.objects.create(stripe_id="inv_xxxxxxxx123456",
                                              customer=self.customer,
                                              period_start=timezone.now(),
                                              period_end=timezone.now() + timedelta(days=5),
                                              subtotal=Decimal("35.00"),
                                              total=Decimal("50.00"),
                                              date=timezone.now(),
                                              charge="crg_xxxxxxxxx12345")

    def test_tostring(self):
        self.assertEquals("<total=50.00, paid=False, stripe_id=inv_xxxxxxxx123456>", str(self.invoice))

    @patch("stripe.Invoice.retrieve")
    def test_retry_true(self, invoice_retrieve_mock):
        return_value = self.invoice.retry()

        invoice_retrieve_mock.assert_called_once_with(self.invoice.stripe_id)
        self.assertTrue(return_value)

    @patch("stripe.Invoice.retrieve")
    def test_retry_false(self, invoice_retrieve_mock):
        invoice = self.invoice
        invoice.pk = None
        invoice.stripe_id = "inv_xxxxxxxx1234567"
        invoice.paid = True
        invoice.save()

        return_value = invoice.retry()

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

    def test_status_open(self):
        status = self.invoice.status()
        self.assertEqual("Open", status)

    def test_status_paid(self):
        invoice = self.invoice
        invoice.pk = None
        invoice.stripe_id = "inv_xxxxxxxx12345678"
        invoice.paid = True
        invoice.save()

        status = invoice.status()
        self.assertEqual("Paid", status)

    def test_status_closed(self):
        invoice = self.invoice
        invoice.pk = None
        invoice.stripe_id = "inv_xxxxxxxx123456789"
        invoice.closed = True
        invoice.save()

        status = invoice.status()
        self.assertEqual("Closed", status)

    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)

    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)

    @patch("djstripe.models.Charge.send_receipt")
    @patch("djstripe.models.Customer.record_charge")
    def test_sync_from_stripe_data_with_charge(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_zzzzzzzzzzzzzzz"
        FAKE_INVOICE_WITH_CHARGE["subscription"] = "sub_zzzzzzzzzzzzzzz"
        FAKE_INVOICE_WITH_CHARGE["lines"]["data"][0]["id"] = "sub_zzzzzzzzzzzzzzz"

        FAKE_INVOICE_WITH_CHARGE["charge"] = "taco"

        Invoice.sync_from_stripe_data(FAKE_INVOICE_WITH_CHARGE)
        record_charge_mock.assert_called_once_with("taco")
        send_receipt_mock.assert_called_once_with()

    @patch("djstripe.models.Charge.send_receipt")
    @patch("djstripe.models.Customer.record_charge", return_value=Charge())
    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)

    @patch("djstripe.models.Invoice.sync_from_stripe_data")
    @patch("stripe.Invoice.retrieve", return_value="lock")
    def test_handle_event_payment_failed(self, invoice_retrieve_mock, sync_invoice_mock):
        fake_event = Event(kind="invoice.payment_failed", validated_message={"data": {"object": {"id": "door"}}})

        invoice_webhook_handler(fake_event, fake_event.message["data"], "invoice", "payment_failed")

        invoice_retrieve_mock.assert_called_once_with("door")
        sync_invoice_mock.assert_called_once_with("lock", send_receipt=True)

    @patch("djstripe.models.Invoice.sync_from_stripe_data")
    @patch("stripe.Invoice.retrieve", return_value="key")
    def test_handle_event_payment_succeeded(self, invoice_retrieve_mock, sync_invoice_mock):
        fake_event = Event(kind="invoice.payment_succeeded", validated_message={"data": {"object": {"id": "lock"}}})

        invoice_webhook_handler(fake_event, fake_event.message["data"], "invoice", "payment_failed")

        invoice_retrieve_mock.assert_called_once_with("lock")
        sync_invoice_mock.assert_called_once_with("key", send_receipt=True)
Example #29
0
    def test___str__(self):
        charge = Charge(
            amount=50,
            currency="usd",
            id="ch_test",
            status=ChargeStatus.failed,
            captured=False,
            paid=False,
        )
        self.assertEqual(str(charge), "$50.00 USD (Uncaptured)")

        charge.captured = True
        self.assertEqual(str(charge), "$50.00 USD (Failed)")
        charge.status = ChargeStatus.succeeded

        charge.disputed = True
        self.assertEqual(str(charge), "$50.00 USD (Disputed)")

        charge.disputed = False
        charge.refunded = True
        charge.amount_refunded = 50
        self.assertEqual(str(charge), "$50.00 USD (Refunded)")

        charge.refunded = False
        charge.amount_refunded = 0
        self.assertEqual(str(charge), "$50.00 USD (Succeeded)")

        charge.status = ChargeStatus.pending
        self.assertEqual(str(charge), "$50.00 USD (Pending)")
Example #30
0
 def test_str(self):
     charge = Charge(amount=50, paid=True, stripe_id='charge_xxxxxxxxxxxxxx')
     self.assertEqual("<amount=50, paid=True, stripe_id=charge_xxxxxxxxxxxxxx>", str(charge))
Example #31
0
	def test_sync_from_stripe_data_refunded_on_update(
		self,
		subscription_retrieve_mock,
		product_retrieve_mock,
		invoice_retrieve_mock,
		charge_retrieve_mock,
		default_account_mock,
	):
		# first sync charge (as per test_sync_from_stripe_data) then sync refunded version,
		# to hit the update code-path instead of insert

		from djstripe.settings import STRIPE_SECRET_KEY

		default_account_mock.return_value = self.account

		fake_charge_copy = deepcopy(FAKE_CHARGE)

		with patch(
			"stripe.BalanceTransaction.retrieve", return_value=deepcopy(FAKE_BALANCE_TRANSACTION)
		):
			charge = Charge.sync_from_stripe_data(fake_charge_copy)

		self.assertEqual(Decimal("22"), charge.amount)
		self.assertEqual(True, charge.paid)
		self.assertEqual(False, charge.refunded)
		self.assertEqual(True, charge.captured)
		self.assertEqual(False, charge.disputed)

		self.assertEqual(len(charge.refunds.all()), 0)

		fake_charge_refunded_copy = deepcopy(FAKE_CHARGE_REFUNDED)

		with patch(
			"stripe.BalanceTransaction.retrieve",
			return_value=deepcopy(FAKE_BALANCE_TRANSACTION_REFUND),
		) as balance_transaction_retrieve_mock:
			charge_refunded = Charge.sync_from_stripe_data(fake_charge_refunded_copy)

		self.assertEqual(charge.id, charge_refunded.id)

		self.assertEqual(Decimal("22"), charge_refunded.amount)
		self.assertEqual(True, charge_refunded.paid)
		self.assertEqual(True, charge_refunded.refunded)
		self.assertEqual(True, charge_refunded.captured)
		self.assertEqual(False, charge_refunded.disputed)
		self.assertEqual(
			"VideoDoc consultation for ivanp0001 berkp0001", charge_refunded.description
		)
		self.assertEqual(charge_refunded.amount, charge_refunded.amount_refunded)

		charge_retrieve_mock.assert_not_called()
		balance_transaction_retrieve_mock.assert_called_once_with(
			api_key=STRIPE_SECRET_KEY, expand=[], id=FAKE_BALANCE_TRANSACTION_REFUND["id"]
		)

		refunds = list(charge_refunded.refunds.all())
		self.assertEqual(len(refunds), 1)

		refund = refunds[0]

		self.assertEqual(refund.id, FAKE_REFUND["id"])

		self.assertNotEqual(
			charge_refunded.balance_transaction.id, refund.balance_transaction.id
		)
		self.assertEqual(
			charge_refunded.balance_transaction.id, FAKE_BALANCE_TRANSACTION["id"]
		)
		self.assertEqual(refund.balance_transaction.id, FAKE_BALANCE_TRANSACTION_REFUND["id"])

		self.assert_fks(
			charge_refunded,
			expected_blank_fks={
				"djstripe.Account.business_logo",
				"djstripe.Charge.dispute",
				"djstripe.Charge.transfer",
				"djstripe.Customer.coupon",
			},
		)
Example #32
0
class TestCustomer(TestCase):
    fake_current_subscription = CurrentSubscription(
        plan="test_plan",
        quantity=1,
        start=timezone.now(),
        amount=decimal.Decimal(25.00))

    fake_current_subscription_cancelled_in_stripe = CurrentSubscription(
        plan="test_plan",
        quantity=1,
        start=timezone.now(),
        amount=decimal.Decimal(25.00),
        status=CurrentSubscription.STATUS_ACTIVE)

    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, [email protected], 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_refund_charge_passes_extra_args(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(amount=decimal.Decimal("10.00"),
                      reverse_transfer=True,
                      refund_application_fee=False)
        _, kwargs = charge_retrieve_mock.return_value.refund.call_args
        self.assertEquals(kwargs["reverse_transfer"], True)
        self.assertEquals(kwargs["refund_application_fee"], False)

    @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,
            ),
            deleted=False)

        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, deleted=False))
    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.Customer.stripe_customer",
           new_callable=PropertyMock,
           return_value=PropertyMock(deleted=True))
    def test_sync_deleted_in_stripe(self, stripe_customer_mock):
        self.customer.sync()
        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("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.current_subscription",
           new_callable=PropertyMock,
           return_value=fake_current_subscription_cancelled_in_stripe)
    @patch("djstripe.models.Customer.stripe_customer",
           new_callable=PropertyMock,
           return_value=PropertyMock(subscription=None))
    def test_sync_current_subscription_subscription_cancelled_from_Stripe(
            self, stripe_customer_mock, customer_subscription_mock):
        self.assertEqual(CurrentSubscription.STATUS_CANCELLED,
                         self.customer.sync_current_subscription().status)

    @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.assertLessEqual(
            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.assertLessEqual(
            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)
Example #33
0
    def test_sync_from_stripe_data_refunded_on_update(
        self,
        subscription_retrieve_mock,
        product_retrieve_mock,
        paymentmethod_card_retrieve_mock,
        payment_intent_retrieve_mock,
        invoice_retrieve_mock,
        charge_retrieve_mock,
        default_account_mock,
    ):
        # first sync charge (as per test_sync_from_stripe_data)
        # then sync refunded version, to hit the update code-path instead of insert

        from djstripe.settings import STRIPE_SECRET_KEY

        default_account_mock.return_value = self.account

        fake_charge_copy = deepcopy(FAKE_CHARGE)

        with patch(
                "stripe.BalanceTransaction.retrieve",
                return_value=deepcopy(FAKE_BALANCE_TRANSACTION),
        ):
            charge = Charge.sync_from_stripe_data(fake_charge_copy)

        self.assertEqual(Decimal("20"), charge.amount)
        self.assertEqual(True, charge.paid)
        self.assertEqual(False, charge.refunded)
        self.assertEqual(True, charge.captured)
        self.assertEqual(False, charge.disputed)

        self.assertEqual(len(charge.refunds.all()), 0)

        fake_charge_refunded_copy = deepcopy(FAKE_CHARGE_REFUNDED)

        with patch(
                "stripe.BalanceTransaction.retrieve",
                return_value=deepcopy(FAKE_BALANCE_TRANSACTION_REFUND),
        ) as balance_transaction_retrieve_mock:
            charge_refunded = Charge.sync_from_stripe_data(
                fake_charge_refunded_copy)

        self.assertEqual(charge.id, charge_refunded.id)

        self.assertEqual(Decimal("20"), charge_refunded.amount)
        self.assertEqual(True, charge_refunded.paid)
        self.assertEqual(True, charge_refunded.refunded)
        self.assertEqual(True, charge_refunded.captured)
        self.assertEqual(False, charge_refunded.disputed)
        self.assertEqual("Subscription creation", charge_refunded.description)
        self.assertEqual(charge_refunded.amount,
                         charge_refunded.amount_refunded)

        charge_retrieve_mock.assert_not_called()
        balance_transaction_retrieve_mock.assert_called_once_with(
            api_key=STRIPE_SECRET_KEY,
            expand=[],
            id=FAKE_BALANCE_TRANSACTION_REFUND["id"],
            stripe_account=None,
        )

        refunds = list(charge_refunded.refunds.all())
        self.assertEqual(len(refunds), 1)

        refund = refunds[0]

        self.assertEqual(refund.id, FAKE_REFUND["id"])

        self.assertNotEqual(charge_refunded.balance_transaction.id,
                            refund.balance_transaction.id)
        self.assertEqual(charge_refunded.balance_transaction.id,
                         FAKE_BALANCE_TRANSACTION["id"])
        self.assertEqual(refund.balance_transaction.id,
                         FAKE_BALANCE_TRANSACTION_REFUND["id"])

        self.assert_fks(
            charge_refunded,
            expected_blank_fks=self.default_expected_blank_fks
            | {
                "djstripe.Account.branding_logo",
                "djstripe.Account.branding_icon"
            },
        )
Example #34
0
    def test_send_receipt_not_sent(self, get_current_mock):
        charge = Charge(receipt_sent=True)
        charge.send_receipt()

        # Assert the condition caused exit
        self.assertFalse(get_current_mock.called)
Example #35
0
    def test_sync_from_stripe_data_refunded(
        self,
        subscription_retrieve_mock,
        product_retrieve_mock,
        paymentmethod_card_retrieve_mock,
        payment_intent_retrieve_mock,
        invoice_retrieve_mock,
        charge_retrieve_mock,
        balance_transaction_retrieve_mock,
        default_account_mock,
    ):
        from djstripe.settings import STRIPE_SECRET_KEY

        default_account_mock.return_value = self.account
        fake_charge_copy = deepcopy(FAKE_CHARGE_REFUNDED)

        charge = Charge.sync_from_stripe_data(fake_charge_copy)

        self.assertEqual(Decimal("20"), charge.amount)
        self.assertEqual(True, charge.paid)
        self.assertEqual(True, charge.refunded)
        self.assertEqual(True, charge.captured)
        self.assertEqual(False, charge.disputed)
        self.assertEqual("Subscription creation", charge.description)
        self.assertEqual(charge.amount, charge.amount_refunded)

        charge_retrieve_mock.assert_not_called()

        # We expect two calls - for charge and then for charge.refunds
        balance_transaction_retrieve_mock.assert_has_calls([
            call(
                api_key=STRIPE_SECRET_KEY,
                expand=[],
                id=FAKE_BALANCE_TRANSACTION["id"],
                stripe_account=None,
            ),
            call(
                api_key=STRIPE_SECRET_KEY,
                expand=[],
                id=FAKE_BALANCE_TRANSACTION_REFUND["id"],
                stripe_account=None,
            ),
        ])

        refunds = list(charge.refunds.all())
        self.assertEqual(len(refunds), 1)

        refund = refunds[0]

        self.assertEqual(refund.id, FAKE_REFUND["id"])

        self.assertNotEqual(charge.balance_transaction.id,
                            refund.balance_transaction.id)
        self.assertEqual(charge.balance_transaction.id,
                         FAKE_BALANCE_TRANSACTION["id"])
        self.assertEqual(refund.balance_transaction.id,
                         FAKE_BALANCE_TRANSACTION_REFUND["id"])

        self.assert_fks(
            charge,
            expected_blank_fks=self.default_expected_blank_fks
            | {
                "djstripe.Account.branding_logo",
                "djstripe.Account.branding_icon"
            },
        )
Example #36
0
    def test_sync_from_stripe_data_refunded_on_update(
        self,
        subscription_retrieve_mock,
        invoice_retrieve_mock,
        charge_retrieve_mock,
        default_account_mock,
    ):
        # first sync charge (as per test_sync_from_stripe_data) then sync refunded version,
        # to hit the update code-path instead of insert

        from djstripe.settings import STRIPE_SECRET_KEY

        default_account_mock.return_value = self.account

        fake_charge_copy = deepcopy(FAKE_CHARGE)

        with patch("stripe.BalanceTransaction.retrieve",
                   return_value=deepcopy(FAKE_BALANCE_TRANSACTION)):
            charge = Charge.sync_from_stripe_data(fake_charge_copy)

        self.assertEqual(Decimal("22"), charge.amount)
        self.assertEqual(True, charge.paid)
        self.assertEqual(False, charge.refunded)
        self.assertEqual(True, charge.captured)
        self.assertEqual(False, charge.disputed)

        self.assertEqual(len(charge.refunds.all()), 0)

        fake_charge_refunded_copy = deepcopy(FAKE_CHARGE_REFUNDED)

        with patch(
                "stripe.BalanceTransaction.retrieve",
                return_value=deepcopy(FAKE_BALANCE_TRANSACTION_REFUND),
        ) as balance_transaction_retrieve_mock:
            charge_refunded = Charge.sync_from_stripe_data(
                fake_charge_refunded_copy)

        self.assertEqual(charge.id, charge_refunded.id)

        self.assertEqual(Decimal("22"), charge_refunded.amount)
        self.assertEqual(True, charge_refunded.paid)
        self.assertEqual(True, charge_refunded.refunded)
        self.assertEqual(True, charge_refunded.captured)
        self.assertEqual(False, charge_refunded.disputed)
        self.assertEqual("VideoDoc consultation for ivanp0001 berkp0001",
                         charge_refunded.description)
        self.assertEqual(charge_refunded.amount,
                         charge_refunded.amount_refunded)

        charge_retrieve_mock.assert_not_called()
        balance_transaction_retrieve_mock.assert_called_once_with(
            api_key=STRIPE_SECRET_KEY,
            expand=[],
            id=FAKE_BALANCE_TRANSACTION_REFUND["id"])

        refunds = list(charge_refunded.refunds.all())
        self.assertEqual(len(refunds), 1)

        refund = refunds[0]

        self.assertEqual(refund.id, FAKE_REFUND["id"])

        self.assertNotEqual(charge_refunded.balance_transaction.id,
                            refund.balance_transaction.id)
        self.assertEqual(charge_refunded.balance_transaction.id,
                         FAKE_BALANCE_TRANSACTION["id"])
        self.assertEqual(refund.balance_transaction.id,
                         FAKE_BALANCE_TRANSACTION_REFUND["id"])

        self.assert_fks(
            charge_refunded,
            expected_blank_fks={
                "djstripe.Account.business_logo",
                "djstripe.Charge.dispute",
                "djstripe.Charge.transfer",
                "djstripe.Customer.coupon",
                "djstripe.Plan.product",
            },
        )