Stripe checkout #104411

Merged
Anna Sirota merged 61 commits from stripe into main 2024-06-17 18:08:41 +02:00
3 changed files with 156 additions and 76 deletions
Showing only changes of commit 0aa1a7afd6 - Show all commits

View File

@ -0,0 +1,59 @@
responses:
- response:
auto_calculate_content_length: false
body: "{\n \"id\": \"cus_QFaxRrT6ZbD9NN\",\n \"object\": \"customer\",\n \"\
address\": null,\n \"balance\": 0,\n \"created\": 1717778124,\n \"currency\"\
: null,\n \"default_source\": null,\n \"delinquent\": false,\n \"description\"\
: null,\n \"discount\": null,\n \"email\": \"my.billing.email@example.com\"\
,\n \"invoice_prefix\": \"046F772E\",\n \"invoice_settings\": {\n \"custom_fields\"\
: null,\n \"default_payment_method\": null,\n \"footer\": null,\n \"\
rendering_options\": null\n },\n \"livemode\": false,\n \"metadata\": {},\n\
\ \"name\": \"New Full Name\",\n \"phone\": null,\n \"preferred_locales\"\
: [],\n \"shipping\": null,\n \"tax_exempt\": \"none\",\n \"test_clock\"\
: null\n}"
content_type: text/plain
method: POST
status: 200
url: https://api.stripe.com/v1/customers
- response:
auto_calculate_content_length: false
body: "{\n \"id\": \"cs_test_a1hoP4Yj4ZmfghAwGoUtWJngVt1XreEVLGAj2n7U5o9BlvqhnDimuA07zh\"\
,\n \"object\": \"checkout.session\",\n \"after_expiration\": null,\n \"\
allow_promotion_codes\": null,\n \"amount_subtotal\": 990,\n \"amount_total\"\
: 990,\n \"automatic_tax\": {\n \"enabled\": false,\n \"liability\":\
\ null,\n \"status\": null\n },\n \"billing_address_collection\": null,\n\
\ \"cancel_url\": \"http://testserver/join/plan-variation/2/billing/\",\n \
\ \"client_reference_id\": null,\n \"client_secret\": null,\n \"consent\"\
: null,\n \"consent_collection\": null,\n \"created\": 1717778125,\n \"currency\"\
: \"eur\",\n \"currency_conversion\": null,\n \"custom_fields\": [],\n \"\
custom_text\": {\n \"after_submit\": null,\n \"shipping_address\": null,\n\
\ \"submit\": null,\n \"terms_of_service_acceptance\": null\n },\n \"\
customer\": \"cus_QFaxRrT6ZbD9NN\",\n \"customer_creation\": null,\n \"customer_details\"\
: {\n \"address\": null,\n \"email\": \"my.billing.email@example.com\"\
,\n \"name\": null,\n \"phone\": null,\n \"tax_exempt\": \"none\",\n\
\ \"tax_ids\": null\n },\n \"customer_email\": null,\n \"expires_at\"\
: 1717864524,\n \"invoice\": null,\n \"invoice_creation\": {\n \"enabled\"\
: false,\n \"invoice_data\": {\n \"account_tax_ids\": null,\n \"\
custom_fields\": null,\n \"description\": null,\n \"footer\": null,\n\
\ \"issuer\": null,\n \"metadata\": {},\n \"rendering_options\"\
: null\n }\n },\n \"livemode\": false,\n \"locale\": null,\n \"metadata\"\
: {},\n \"mode\": \"payment\",\n \"payment_intent\": null,\n \"payment_link\"\
: null,\n \"payment_method_collection\": \"if_required\",\n \"payment_method_configuration_details\"\
: null,\n \"payment_method_options\": {\n \"card\": {\n \"request_three_d_secure\"\
: \"automatic\"\n }\n },\n \"payment_method_types\": [\n \"card\",\n\
\ \"link\",\n \"paypal\"\n ],\n \"payment_status\": \"unpaid\",\n \"\
phone_number_collection\": {\n \"enabled\": false\n },\n \"recovered_from\"\
: null,\n \"saved_payment_method_options\": {\n \"allow_redisplay_filters\"\
: [\n \"always\"\n ],\n \"payment_method_remove\": null,\n \"\
payment_method_save\": null\n },\n \"setup_intent\": null,\n \"shipping_address_collection\"\
: null,\n \"shipping_cost\": null,\n \"shipping_details\": null,\n \"shipping_options\"\
: [],\n \"status\": \"open\",\n \"submit_type\": \"pay\",\n \"subscription\"\
: null,\n \"success_url\": \"http://testserver/looper/stripe_success/1/{CHECKOUT_SESSION_ID}\"\
,\n \"total_details\": {\n \"amount_discount\": 0,\n \"amount_shipping\"\
: 0,\n \"amount_tax\": 0\n },\n \"ui_mode\": \"hosted\",\n \"url\": \"\
https://checkout.stripe.com/c/pay/cs_test_a1hoP4Yj4ZmfghAwGoUtWJngVt1XreEVLGAj2n7U5o9BlvqhnDimuA07zh#fidkdWxOYHwnPyd1blpxYHZxWjA0VUpnNzNAMU5EUEcwYW92dWIwclxRMzQ8ZkxsUDRET2dPbTVidnBCNEJTdlBJQTRJYFF2c09BMEFBdlxVT19USGpWSXRSXFJwdm5UQXRpdVw2Rmp%2FZ11NNTU3fHdHUl1JTCcpJ2N3amhWYHdzYHcnP3F3cGApJ2lkfGpwcVF8dWAnPyd2bGtiaWBabHFgaCcpJ2BrZGdpYFVpZGZgbWppYWB3dic%2FcXdwYHgl\"\
\n}"
content_type: text/plain
method: POST
status: 200
url: https://api.stripe.com/v1/checkout/sessions

View File

@ -1,5 +1,4 @@
"""Views handling subscription management.""" """Views handling subscription management."""
from decimal import Decimal
import logging import logging
from django.contrib import messages from django.contrib import messages
@ -43,6 +42,24 @@ class JoinView(LoginRequiredMixin, FormView):
) )
return existing_subscriptions.first() return existing_subscriptions.first()
def _set_preferred_currency_and_redirect(self):
# If no country is set in the existing address, use GeoIP's
geoip_country = self.request.session.get(looper.middleware.COUNTRY_CODE_SESSION_KEY)
if geoip_country and (not self.customer or not self.customer.billing_address.country):
country = geoip_country
else:
country = self.customer.billing_address.country
currency = preferred_currency_for_country_code(country)
if self.plan_variation.currency != currency:
# If variation's currency doesn't match, redirect to another plan variation
plan_variation = self.plan_variation.in_other_currency(currency)
self.request.session[looper.middleware.PREFERRED_CURRENCY_SESSION_KEY] = currency
self.request.session.modified = True
return redirect(
'subscriptions:join-billing-details', plan_variation_id=plan_variation.pk
)
return None # nothing to do, no need to redirect
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
"""Redirect to login or to billing, or prepare plan variation.""" """Redirect to login or to billing, or prepare plan variation."""
if not request.user.is_authenticated: if not request.user.is_authenticated:
@ -62,6 +79,10 @@ class JoinView(LoginRequiredMixin, FormView):
self.user = request.user self.user = request.user
self.customer = self.user.customer self.customer = self.user.customer
self.subscription = self._get_existing_subscription() self.subscription = self._get_existing_subscription()
response_redirect = self._set_preferred_currency_and_redirect()
if response_redirect:
return response_redirect
return super().dispatch(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs)
def get_form_kwargs(self, *args, **kwargs): def get_form_kwargs(self, *args, **kwargs):
@ -164,27 +185,21 @@ class JoinView(LoginRequiredMixin, FormView):
form.save() form.save()
msg = 'Pricing has been updated to reflect changes to your billing details' msg = 'Pricing has been updated to reflect changes to your billing details'
new_country = self.customer.billing_address.country response_redirect = self._set_preferred_currency_and_redirect()
new_currency = preferred_currency_for_country_code(new_country) if response_redirect:
# Compare currency before and after the billing address is updated
if self.plan_variation.currency != new_currency:
# If currency has changed, find a matching plan variation for this new currency
plan_variation = self.plan_variation.in_other_currency(new_currency)
self.request.session[looper.middleware.PREFERRED_CURRENCY_SESSION_KEY] = new_currency
messages.add_message(self.request, messages.INFO, msg) messages.add_message(self.request, messages.INFO, msg)
return redirect( return response_redirect
'subscriptions:join-billing-details', plan_variation_id=plan_variation.pk
)
# Compare tax before and after the billing address is updated # Compare tax before and after the billing address is updated
new_tax = self.customer.get_tax(product_type=product_type) new_tax = self.customer.get_tax(product_type=product_type)
new_taxable = looper.taxes.Taxable(self.plan_variation.price, *new_tax) new_taxable = looper.taxes.Taxable(self.plan_variation.price, *new_tax)
if old_taxable != new_taxable: if old_taxable != new_taxable:
# If price has changed, stay on the same page and display a notification # If price has changed, stay on the same page and display a notification
messages.add_message(self.request, messages.INFO, msg)
return self.form_invalid(form) return self.form_invalid(form)
gateway = self.gateway_from_form(form) gateway = self.gateway_from_form(form)
price_cents = int(Decimal(form.cleaned_data['price']) * 100) price_cents = new_taxable.price.cents
subscription = self._get_or_create_subscription(gateway) subscription = self._get_or_create_subscription(gateway)
# Update the tax info stored on the subscription # Update the tax info stored on the subscription
subscription.update_tax() subscription.update_tax()

View File

@ -1,13 +1,15 @@
from typing import Tuple
from unittest.mock import patch from unittest.mock import patch
import os import os
import unittest import unittest
from django.conf import settings from django.conf import settings
from django.test import TestCase
from django.urls import reverse from django.urls import reverse
from freezegun import freeze_time from freezegun import freeze_time
import responses import responses
# from responses import _recorder
from looper.tests.test_preferred_currency import EURO_IPV4, USA_IPV4, SINGAPORE_IPV4 from looper.tests.test_preferred_currency import EURO_IPV4, USA_IPV4, SINGAPORE_IPV4
from looper.money import Money from looper.money import Money
import looper.models import looper.models
@ -41,7 +43,7 @@ def _get_default_variation(currency='USD'):
@freeze_time('2023-05-19 11:41:11') @freeze_time('2023-05-19 11:41:11')
class TestGETBillingDetailsView(BaseSubscriptionTestCase): class TestGETJoinView(BaseSubscriptionTestCase):
url_usd = reverse('subscriptions:join-billing-details', kwargs={'plan_variation_id': 1}) url_usd = reverse('subscriptions:join-billing-details', kwargs={'plan_variation_id': 1})
url = reverse('subscriptions:join-billing-details', kwargs={'plan_variation_id': 2}) url = reverse('subscriptions:join-billing-details', kwargs={'plan_variation_id': 2})
@ -65,7 +67,8 @@ class TestGETBillingDetailsView(BaseSubscriptionTestCase):
) )
def test_get_displays_total_and_billing_details_to_logged_in_nl(self): def test_get_displays_total_and_billing_details_to_logged_in_nl(self):
user = create_customer_with_billing_address(vat_number='', country='NL') customer = create_customer_with_billing_address(vat_number='', country='NL')
user = customer.user
self.client.force_login(user) self.client.force_login(user)
response = self.client.get(self.url, REMOTE_ADDR=EURO_IPV4) response = self.client.get(self.url, REMOTE_ADDR=EURO_IPV4)
@ -76,7 +79,8 @@ class TestGETBillingDetailsView(BaseSubscriptionTestCase):
self._assert_total_default_variation_selected_tax_21_eur(response) self._assert_total_default_variation_selected_tax_21_eur(response)
def test_get_displays_total_and_billing_details_to_logged_in_de(self): def test_get_displays_total_and_billing_details_to_logged_in_de(self):
user = create_customer_with_billing_address(vat_number='', country='DE') customer = create_customer_with_billing_address(vat_number='', country='DE')
user = customer.user
self.client.force_login(user) self.client.force_login(user)
response = self.client.get(self.url, REMOTE_ADDR=EURO_IPV4) response = self.client.get(self.url, REMOTE_ADDR=EURO_IPV4)
@ -86,9 +90,10 @@ class TestGETBillingDetailsView(BaseSubscriptionTestCase):
self._assert_total_default_variation_selected_tax_19_eur(response) self._assert_total_default_variation_selected_tax_19_eur(response)
def test_get_displays_total_and_billing_details_to_logged_in_us(self): def test_get_displays_total_and_billing_details_to_logged_in_us(self):
user = create_customer_with_billing_address( customer = create_customer_with_billing_address(
vat_number='', country='US', region='NY', postal_code='12001' vat_number='', country='US', region='NY', postal_code='12001'
) )
user = customer.user
self.client.force_login(user) self.client.force_login(user)
response = self.client.get(self.url_usd) response = self.client.get(self.url_usd)
@ -99,16 +104,23 @@ class TestGETBillingDetailsView(BaseSubscriptionTestCase):
self._assert_total_default_variation_selected_usd(response) self._assert_total_default_variation_selected_usd(response)
@unittest.skipUnless(os.path.exists(settings.GEOIP2_DB), 'GeoIP database file is required') @unittest.skipUnless(os.path.exists(settings.GEOIP2_DB), 'GeoIP database file is required')
def test_get_detects_country_us_sets_preferred_currency_usd_invalid_variation(self): def test_get_detects_country_us_sets_preferred_currency_usd_and_redirects(self):
user = create_customer_with_billing_address() customer = create_customer_with_billing_address()
user = customer.user
self.client.force_login(user) self.client.force_login(user)
self.assertTrue(looper.middleware.PREFERRED_CURRENCY_SESSION_KEY not in self.client.session)
response = self.client.get(self.url, REMOTE_ADDR=USA_IPV4) response = self.client.get(self.url, REMOTE_ADDR=USA_IPV4)
self.assertEqual(response.status_code, 404) self.assertEqual(response.status_code, 302)
self.assertEqual(response['Location'], self.url_usd)
self.assertEqual(
self.client.session[looper.middleware.PREFERRED_CURRENCY_SESSION_KEY], 'USD'
)
@unittest.skipUnless(os.path.exists(settings.GEOIP2_DB), 'GeoIP database file is required') @unittest.skipUnless(os.path.exists(settings.GEOIP2_DB), 'GeoIP database file is required')
def test_get_detects_country_us_sets_preferred_currency_usd(self): def test_get_detects_country_us_sets_preferred_currency_usd(self):
user = create_customer_with_billing_address() customer = create_customer_with_billing_address()
user = customer.user
self.client.force_login(user) self.client.force_login(user)
response = self.client.get(self.url_usd, REMOTE_ADDR=USA_IPV4) response = self.client.get(self.url_usd, REMOTE_ADDR=USA_IPV4)
@ -125,7 +137,8 @@ class TestGETBillingDetailsView(BaseSubscriptionTestCase):
@unittest.skipUnless(os.path.exists(settings.GEOIP2_DB), 'GeoIP database file is required') @unittest.skipUnless(os.path.exists(settings.GEOIP2_DB), 'GeoIP database file is required')
def test_get_detects_country_sg_sets_preferred_currency_eur(self): def test_get_detects_country_sg_sets_preferred_currency_eur(self):
user = create_customer_with_billing_address() customer = create_customer_with_billing_address()
user = customer.user
self.client.force_login(user) self.client.force_login(user)
response = self.client.get(self.url, REMOTE_ADDR=SINGAPORE_IPV4) response = self.client.get(self.url, REMOTE_ADDR=SINGAPORE_IPV4)
@ -142,7 +155,8 @@ class TestGETBillingDetailsView(BaseSubscriptionTestCase):
@unittest.skipUnless(os.path.exists(settings.GEOIP2_DB), 'GeoIP database file is required') @unittest.skipUnless(os.path.exists(settings.GEOIP2_DB), 'GeoIP database file is required')
def test_get_detects_country_nl_sets_preferred_currency_eur_displays_correct_vat(self): def test_get_detects_country_nl_sets_preferred_currency_eur_displays_correct_vat(self):
user = create_customer_with_billing_address() customer = create_customer_with_billing_address()
user = customer.user
self.client.force_login(user) self.client.force_login(user)
response = self.client.get(self.url, REMOTE_ADDR=EURO_IPV4) response = self.client.get(self.url, REMOTE_ADDR=EURO_IPV4)
@ -158,12 +172,15 @@ class TestGETBillingDetailsView(BaseSubscriptionTestCase):
@freeze_time('2023-05-19 11:41:11') @freeze_time('2023-05-19 11:41:11')
class TestPOSTBillingDetailsView(BaseSubscriptionTestCase): @responses.activate
class TestPOSTJoinView(BaseSubscriptionTestCase):
url_usd = reverse('subscriptions:join-billing-details', kwargs={'plan_variation_id': 1}) url_usd = reverse('subscriptions:join-billing-details', kwargs={'plan_variation_id': 1})
url = reverse('subscriptions:join-billing-details', kwargs={'plan_variation_id': 2}) url = reverse('subscriptions:join-billing-details', kwargs={'plan_variation_id': 2})
responses._add_from_file('stripe_create_checkout_session.yaml')
def test_post_updates_billing_address_and_customer_renders_next_form_de(self): def test_post_updates_billing_address_and_customer_renders_next_form_de(self):
user = create_customer_with_billing_address(vat_number='', country='DE') customer = create_customer_with_billing_address(vat_number='', country='DE')
user = customer.user
self.client.force_login(user) self.client.force_login(user)
selected_variation = ( selected_variation = (
@ -197,33 +214,21 @@ class TestPOSTBillingDetailsView(BaseSubscriptionTestCase):
self.assertContains(response, 'Manual ') self.assertContains(response, 'Manual ')
self.assertContains(response, '/ <span class="x-price-period">1 year</span>', html=True) self.assertContains(response, '/ <span class="x-price-period">1 year</span>', html=True)
# @_recorder.record(file_path='stripe_create_checkout_session.yaml')
def test_post_has_correct_price_field_value(self): def test_post_has_correct_price_field_value(self):
self.client.force_login(self.user) self.client.force_login(self.user)
default_variation = _get_default_variation('EUR')
data = required_address_data data = required_address_data
response = self.client.post(self.url, data, REMOTE_ADDR=EURO_IPV4) response = self.client.post(self.url, data, REMOTE_ADDR=EURO_IPV4)
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
self.assertEqual( self.assertEqual(
response['Location'], response['Location'],
reverse( 'https://checkout.stripe.com/c/pay/cs_test_a1hoP4Yj4ZmfghAwGoUtWJngVt1XreEVLGAj2'
'subscriptions:join-confirm-and-pay', 'n7U5o9BlvqhnDimuA07zh#fidkdWxOYHwnPyd1blpxYHZxWjA0VUpnNzNAMU5EUEcwYW92dWIwclxRMzQ8Zkx'
kwargs={'plan_variation_id': default_variation.pk}, 'sUDRET2dPbTVidnBCNEJTdlBJQTRJYFF2c09BMEFBdlxVT19USGpWSXRSXFJwdm5UQXRpdVw2Rmp'
), '%2FZ11NNTU3fHdHUl1JTCcpJ2N3amhWYHdzYHcnP3F3cGApJ2lkfGpwcVF8dWAnPyd2bGtiaWBabH'
) 'FgaCcpJ2BrZGdpYFVpZGZgbWppYWB3dic%2FcXdwYHgl',
# Follow the redirect to avoid "Couldn't retrieve content: Response code was 302 (expected 200)"
response = self.client.get(response['Location'])
# Check that we are no longer on the billing details page
self._assert_payment_form_displayed(response)
self._assert_total_default_variation_selected_tax_21_eur(response)
# The hidden price field must also be set to a matching amount
self.assertContains(
response,
'<input type="hidden" name="price" value="9.90" class="form-control" id="id_price">',
html=True,
) )
def test_post_updates_billing_address_and_customer_applies_reverse_charged_tax(self): def test_post_updates_billing_address_and_customer_applies_reverse_charged_tax(self):
@ -240,8 +245,8 @@ class TestPOSTBillingDetailsView(BaseSubscriptionTestCase):
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.user.refresh_from_db() self.user.refresh_from_db()
self.assertEqual(self.user.customer.vat_number, 'DE260543043')
address = self.user.customer.billing_address address = self.user.customer.billing_address
self.assertEqual(address.vat_number, 'DE260543043')
self.assertEqual(address.full_name, 'New Full Name') self.assertEqual(address.full_name, 'New Full Name')
self.assertEqual(address.postal_code, '11111') self.assertEqual(address.postal_code, '11111')
self.assertEqual(address.country, 'DE') self.assertEqual(address.country, 'DE')
@ -269,9 +274,10 @@ class TestPOSTBillingDetailsView(BaseSubscriptionTestCase):
) )
def test_post_changing_address_from_with_region_to_without_region_clears_region(self): def test_post_changing_address_from_with_region_to_without_region_clears_region(self):
user = create_customer_with_billing_address( customer = create_customer_with_billing_address(
vat_number='', country='US', region='NY', postal_code='12001' vat_number='', country='US', region='NY', postal_code='12001'
) )
user = customer.user
self.client.force_login(user) self.client.force_login(user)
response = self.client.get(self.url_usd) response = self.client.get(self.url_usd)
@ -304,22 +310,10 @@ class TestPOSTBillingDetailsView(BaseSubscriptionTestCase):
self.assertEqual(user.customer.billing_address.country, 'DE') self.assertEqual(user.customer.billing_address.country, 'DE')
self.assertEqual(user.customer.billing_address.postal_code, '11111') self.assertEqual(user.customer.billing_address.postal_code, '11111')
@freeze_time('2023-05-19 11:41:11')
class TestPOSTConfirmAndPayView(BaseSubscriptionTestCase):
def _get_url_for(self, **filter_params) -> Tuple[str, looper.models.PlanVariation]:
plan_variation = looper.models.PlanVariation.objects.active().get(**filter_params)
return (
reverse(
'subscriptions:join-confirm-and-pay',
kwargs={'plan_variation_id': plan_variation.pk},
),
plan_variation,
)
def test_plan_variation_does_not_match_detected_currency_usd_euro_ip(self): def test_plan_variation_does_not_match_detected_currency_usd_euro_ip(self):
url, _ = self._get_url_for(currency='USD', price=11900) url, _ = self._get_url_for(currency='USD', price=11900)
user = create_customer_with_billing_address(country='NL') customer = create_customer_with_billing_address(country='NL')
user = customer.user
self.client.force_login(user) self.client.force_login(user)
data = required_address_data data = required_address_data
@ -329,7 +323,8 @@ class TestPOSTConfirmAndPayView(BaseSubscriptionTestCase):
def test_plan_variation_matches_detected_currency_eur_non_eea_ip(self): def test_plan_variation_matches_detected_currency_eur_non_eea_ip(self):
url, _ = self._get_url_for(currency='EUR', price=990) url, _ = self._get_url_for(currency='EUR', price=990)
user = create_customer_with_billing_address() customer = create_customer_with_billing_address()
user = customer.user
self.client.force_login(user) self.client.force_login(user)
data = required_address_data data = required_address_data
@ -341,7 +336,8 @@ class TestPOSTConfirmAndPayView(BaseSubscriptionTestCase):
def test_billing_address_country_takes_precedence_over_geo_ip(self): def test_billing_address_country_takes_precedence_over_geo_ip(self):
url, _ = self._get_url_for(currency='EUR', price=990) url, _ = self._get_url_for(currency='EUR', price=990)
user = create_customer_with_billing_address(country='NL') customer = create_customer_with_billing_address(country='NL')
user = customer.user
self.client.force_login(user) self.client.force_login(user)
data = required_address_data data = required_address_data
@ -352,7 +348,8 @@ class TestPOSTConfirmAndPayView(BaseSubscriptionTestCase):
def test_invalid_missing_required_fields(self): def test_invalid_missing_required_fields(self):
url, _ = self._get_url_for(currency='EUR', price=990) url, _ = self._get_url_for(currency='EUR', price=990)
user = create_customer_with_billing_address(country='NL') customer = create_customer_with_billing_address(country='NL')
user = customer.user
self.client.force_login(user) self.client.force_login(user)
data = required_address_data data = required_address_data
@ -371,7 +368,8 @@ class TestPOSTConfirmAndPayView(BaseSubscriptionTestCase):
def test_invalid_price_does_not_match_selected_plan_variation(self): def test_invalid_price_does_not_match_selected_plan_variation(self):
url, selected_variation = self._get_url_for(currency='EUR', price=990) url, selected_variation = self._get_url_for(currency='EUR', price=990)
user = create_customer_with_billing_address(country='NL') customer = create_customer_with_billing_address(country='NL')
user = customer.user
self.client.force_login(user) self.client.force_login(user)
data = { data = {
@ -391,7 +389,8 @@ class TestPOSTConfirmAndPayView(BaseSubscriptionTestCase):
def test_invalid_bank_transfer_cannot_be_selected_for_automatic_payments(self): def test_invalid_bank_transfer_cannot_be_selected_for_automatic_payments(self):
url, selected_variation = self._get_url_for(currency='EUR', price=990) url, selected_variation = self._get_url_for(currency='EUR', price=990)
user = create_customer_with_billing_address(country='NL') customer = create_customer_with_billing_address(country='NL')
user = customer.user
self.client.force_login(user) self.client.force_login(user)
data = { data = {
@ -423,7 +422,8 @@ class TestPOSTConfirmAndPayView(BaseSubscriptionTestCase):
) )
@responses.activate @responses.activate
def test_pay_with_bank_transfer_creates_order_subscription_on_hold(self): def test_pay_with_bank_transfer_creates_order_subscription_on_hold(self):
user = create_customer_with_billing_address(country='NL', full_name='Jane Doe') customer = create_customer_with_billing_address(country='NL', full_name='Jane Doe')
user = customer.user
self.client.force_login(user) self.client.force_login(user)
util.mock_blender_id_badger_badger_response( util.mock_blender_id_badger_badger_response(
'grant', 'cloud_has_subscription', user.oauth_info.oauth_user_id 'grant', 'cloud_has_subscription', user.oauth_info.oauth_user_id
@ -500,9 +500,10 @@ class TestPOSTConfirmAndPayView(BaseSubscriptionTestCase):
def test_pay_with_bank_transfer_creates_order_subscription_on_hold_shows_reverse_charged_price( def test_pay_with_bank_transfer_creates_order_subscription_on_hold_shows_reverse_charged_price(
self, self,
): ):
user = create_customer_with_billing_address( customer = create_customer_with_billing_address(
country='ES', full_name='Jane Doe', vat_number='DE260543043' country='ES', full_name='Jane Doe', vat_number='DE260543043'
) )
user = customer.user
self.client.force_login(user) self.client.force_login(user)
util.mock_blender_id_badger_badger_response( util.mock_blender_id_badger_badger_response(
'grant', 'cloud_has_subscription', user.oauth_info.oauth_user_id 'grant', 'cloud_has_subscription', user.oauth_info.oauth_user_id
@ -584,7 +585,8 @@ class TestPOSTConfirmAndPayView(BaseSubscriptionTestCase):
@responses.activate @responses.activate
def test_pay_with_credit_card_creates_order_subscription_active(self): def test_pay_with_credit_card_creates_order_subscription_active(self):
url, selected_variation = self._get_url_for(currency='EUR', price=990) url, selected_variation = self._get_url_for(currency='EUR', price=990)
user = create_customer_with_billing_address(country='NL', full_name='Jane Doe') customer = create_customer_with_billing_address(country='NL', full_name='Jane Doe')
user = customer.user
self.client.force_login(user) self.client.force_login(user)
util.mock_blender_id_badger_badger_response( util.mock_blender_id_badger_badger_response(
'grant', 'cloud_has_subscription', user.oauth_info.oauth_user_id 'grant', 'cloud_has_subscription', user.oauth_info.oauth_user_id
@ -636,7 +638,8 @@ class TestPOSTConfirmAndPayView(BaseSubscriptionTestCase):
price=9000, price=9000,
plan__name='Automatic renewal, 15 seats', plan__name='Automatic renewal, 15 seats',
) )
user = create_customer_with_billing_address(country='NL', full_name='Jane Doe') customer = create_customer_with_billing_address(country='NL', full_name='Jane Doe')
user = customer.user
self.client.force_login(user) self.client.force_login(user)
util.mock_blender_id_badger_badger_response( util.mock_blender_id_badger_badger_response(
'grant', 'cloud_has_subscription', user.oauth_info.oauth_user_id 'grant', 'cloud_has_subscription', user.oauth_info.oauth_user_id
@ -655,7 +658,7 @@ class TestPOSTConfirmAndPayView(BaseSubscriptionTestCase):
self._assert_done_page_displayed(response) self._assert_done_page_displayed(response)
subscription = user.customer.subscription_set.first() subscription = customer.subscription_set.first()
order = subscription.latest_order() order = subscription.latest_order()
self.assertEqual(subscription.status, 'active') self.assertEqual(subscription.status, 'active')
self.assertEqual(subscription.price, Money('EUR', 9000)) self.assertEqual(subscription.price, Money('EUR', 9000))
@ -670,7 +673,8 @@ class TestPOSTConfirmAndPayView(BaseSubscriptionTestCase):
self._assert_team_subscription_activated_email_is_sent(subscription) self._assert_team_subscription_activated_email_is_sent(subscription)
def test_pay_with_credit_card_creates_order_subscription_active_business_de(self): def test_pay_with_credit_card_creates_order_subscription_active_business_de(self):
user = create_customer_with_billing_address(country='DE', vat_number='DE260543043') customer = create_customer_with_billing_address(country='DE', vat_number='DE260543043')
user = customer.user
self.client.force_login(user) self.client.force_login(user)
url, selected_variation = self._get_url_for( url, selected_variation = self._get_url_for(
@ -716,15 +720,17 @@ class TestPOSTConfirmAndPayView(BaseSubscriptionTestCase):
self.assertIsNotNone(order.number) self.assertIsNotNone(order.number)
class TestJoinConfirmAndPayLoggedInUserOnlyView(BaseSubscriptionTestCase): class TestJoinViewLoggedInUserOnly(TestCase):
url = reverse('subscriptions:join-confirm-and-pay', kwargs={'plan_variation_id': 8}) url = reverse('subscriptions:join-billing-details', kwargs={'plan_variation_id': 2})
def test_get_anonymous_403(self): def test_get_anonymous_redirects_to_login_with_next(self):
response = self.client.get(self.url) response = self.client.get(self.url)
self.assertEqual(response.status_code, 403) self.assertEqual(response.status_code, 302)
self.assertEqual(response['Location'], '/oauth/login?next=/join/plan-variation/2/billing/')
def test_join_post_anonymous_403(self): def test_post_anonymous_redirects_to_login_with_next(self):
response = self.client.post(self.url) response = self.client.post(self.url)
self.assertEqual(response.status_code, 403) self.assertEqual(response.status_code, 302)
self.assertEqual(response['Location'], '/oauth/login?next=/join/plan-variation/2/billing/')