Stripe checkout #104411

Merged
Anna Sirota merged 61 commits from stripe into main 2024-06-17 18:08:41 +02:00
5 changed files with 143 additions and 41 deletions
Showing only changes of commit 8271a8c0aa - Show all commits

View File

@ -0,0 +1,51 @@
responses:
- response:
auto_calculate_content_length: false
body: "{\n \"id\": \"cus_QGeKULCHd4p9o2\",\n \"object\": \"customer\",\n \"\
address\": null,\n \"balance\": 0,\n \"created\": 1718021356,\n \"currency\"\
: null,\n \"default_source\": null,\n \"delinquent\": false,\n \"description\"\
: null,\n \"discount\": null,\n \"email\": \"billing@example.com\",\n \"\
invoice_prefix\": \"0ABA8C57\",\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\": \"\u0410\u043B\u0435\u043A\u0441\u0435\u0439 \u041D.\",\n \"phone\"\
: null,\n \"preferred_locales\": [],\n \"shipping\": null,\n \"tax_exempt\"\
: \"none\",\n \"test_clock\": null\n}"
content_type: text/plain; charset=utf-8
method: POST
status: 200
url: https://api.stripe.com/v1/customers
- response:
auto_calculate_content_length: false
body: "{\n \"id\": \"cs_test_c1QeSt36UcbmnmrXJnEYZpWakr377WPMfbWLeR9d3ZBYJPWXRUJ3TQ0UG9\"\
,\n \"object\": \"checkout.session\",\n \"after_expiration\": null,\n \"\
allow_promotion_codes\": null,\n \"amount_subtotal\": null,\n \"amount_total\"\
: null,\n \"automatic_tax\": {\n \"enabled\": false,\n \"liability\"\
: null,\n \"status\": null\n },\n \"billing_address_collection\": null,\n\
\ \"cancel_url\": \"http://testserver/settings/billing/\",\n \"client_reference_id\"\
: null,\n \"client_secret\": null,\n \"consent\": null,\n \"consent_collection\"\
: null,\n \"created\": 1718021357,\n \"currency\": null,\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_QGeKULCHd4p9o2\",\n \"customer_creation\"\
: null,\n \"customer_details\": {\n \"address\": null,\n \"email\": \"\
billing@example.com\",\n \"name\": null,\n \"phone\": null,\n \"tax_exempt\"\
: null,\n \"tax_ids\": null\n },\n \"customer_email\": null,\n \"expires_at\"\
: 1718107757,\n \"invoice\": null,\n \"invoice_creation\": null,\n \"livemode\"\
: false,\n \"locale\": null,\n \"metadata\": {},\n \"mode\": \"setup\",\n\
\ \"payment_intent\": null,\n \"payment_link\": null,\n \"payment_method_collection\"\
: \"always\",\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\": \"no_payment_required\",\n \"phone_number_collection\"\
: {\n \"enabled\": false\n },\n \"recovered_from\": null,\n \"saved_payment_method_options\"\
: null,\n \"setup_intent\": \"seti_1PQ72HE4KAUB5djsPthAKRdZ\",\n \"shipping_address_collection\"\
: null,\n \"shipping_cost\": null,\n \"shipping_details\": null,\n \"shipping_options\"\
: [],\n \"status\": \"open\",\n \"submit_type\": null,\n \"subscription\"\
: null,\n \"success_url\": \"http://testserver/subscription/1/payment-method/change/{CHECKOUT_SESSION_ID}/\"\
,\n \"total_details\": null,\n \"ui_mode\": \"hosted\",\n \"url\": \"https://checkout.stripe.com/c/pay/cs_test_c1QeSt36UcbmnmrXJnEYZpWakr377WPMfbWLeR9d3ZBYJPWXRUJ3TQ0UG9#fidkdWxOYHwnPyd1blpxYHZxWjA0VUpnNzNAMU5EUEcwYW92dWIwclxRMzQ8ZkxsUDRET2dPbTVidnBCNEJTdlBJQTRJYFF2c09BMEFBdlxVT19USGpWSXRSXFJwdm5UQXRpdVw2Rmp%2FZ11NNTU3fHdHUl1JTCcpJ2N3amhWYHdzYHcnP3F3cGApJ2lkfGpwcVF8dWAnPyd2bGtiaWBaZmppcGhrJyknYGtkZ2lgVWlkZmBtamlhYHd2Jz9xd3BgeCUl\"\
\n}"
content_type: text/plain
method: POST
status: 200
url: https://api.stripe.com/v1/checkout/sessions

View File

@ -0,0 +1,67 @@
responses:
- response:
auto_calculate_content_length: false
body: "{\n \"id\": \"cs_test_c1QeSt36UcbmnmrXJnEYZpWakr377WPMfbWLeR9d3ZBYJPWXRUJ3TQ0UG9\"\
,\n \"object\": \"checkout.session\",\n \"after_expiration\": null,\n \"\
allow_promotion_codes\": null,\n \"amount_subtotal\": null,\n \"amount_total\"\
: null,\n \"automatic_tax\": {\n \"enabled\": false,\n \"liability\"\
: null,\n \"status\": null\n },\n \"billing_address_collection\": null,\n\
\ \"cancel_url\": \"http://testserver/settings/billing/\",\n \"client_reference_id\"\
: null,\n \"client_secret\": null,\n \"consent\": null,\n \"consent_collection\"\
: null,\n \"created\": 1718021357,\n \"currency\": null,\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_QGeKULCHd4p9o2\",\n \"customer_creation\"\
: null,\n \"customer_details\": {\n \"address\": null,\n \"email\": \"\
billing@example.com\",\n \"name\": \"\u0410\u043B\u0435\u043A\u0441\u0435\
\u0439 \u041D.\",\n \"phone\": null,\n \"tax_exempt\": null,\n \"tax_ids\"\
: []\n },\n \"customer_email\": null,\n \"expires_at\": 1718107757,\n \"\
invoice\": null,\n \"invoice_creation\": null,\n \"livemode\": false,\n \"\
locale\": null,\n \"metadata\": {},\n \"mode\": \"setup\",\n \"payment_intent\"\
: null,\n \"payment_link\": null,\n \"payment_method_collection\": \"always\"\
,\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\": \"no_payment_required\",\n \"phone_number_collection\"\
: {\n \"enabled\": false\n },\n \"recovered_from\": null,\n \"saved_payment_method_options\"\
: null,\n \"setup_intent\": {\n \"id\": \"seti_1PQ72HE4KAUB5djsPthAKRdZ\"\
,\n \"object\": \"setup_intent\",\n \"application\": null,\n \"automatic_payment_methods\"\
: null,\n \"cancellation_reason\": null,\n \"client_secret\": \"seti_1PQ72HE4KAUB5djsPthAKRdZ_secret_QGeKD74KJNAouKtjbevE1O3dwVKxxt7\"\
,\n \"created\": 1718021357,\n \"customer\": \"cus_QGeKULCHd4p9o2\",\n\
\ \"description\": null,\n \"flow_directions\": null,\n \"last_setup_error\"\
: null,\n \"latest_attempt\": \"setatt_1PQ7DuE4KAUB5djsZtPVk4XB\",\n \"\
livemode\": false,\n \"mandate\": null,\n \"metadata\": {\n \"subscription_id\"\
: \"1\"\n },\n \"next_action\": null,\n \"on_behalf_of\": null,\n \
\ \"payment_method\": {\n \"id\": \"pm_1PQ7DsE4KAUB5djsw4z0K5PS\",\n\
\ \"object\": \"payment_method\",\n \"allow_redisplay\": \"always\"\
,\n \"billing_details\": {\n \"address\": {\n \"city\"\
: null,\n \"country\": \"NL\",\n \"line1\": null,\n \
\ \"line2\": null,\n \"postal_code\": null,\n \"state\"\
: null\n },\n \"email\": \"billing@example.com\",\n \"\
name\": \"John Smith\",\n \"phone\": null\n },\n \"card\":\
\ {\n \"brand\": \"visa\",\n \"checks\": {\n \"address_line1_check\"\
: null,\n \"address_postal_code_check\": null,\n \"cvc_check\"\
: \"pass\"\n },\n \"country\": \"US\",\n \"display_brand\"\
: \"visa\",\n \"exp_month\": 12,\n \"exp_year\": 2033,\n \
\ \"fingerprint\": \"YcmpGi38fZZuBsh4\",\n \"funding\": \"credit\"\
,\n \"generated_from\": null,\n \"last4\": \"4242\",\n \
\ \"networks\": {\n \"available\": [\n \"visa\"\n \
\ ],\n \"preferred\": null\n },\n \"three_d_secure_usage\"\
: {\n \"supported\": true\n },\n \"wallet\": null\n \
\ },\n \"created\": 1718022076,\n \"customer\": \"cus_QGeKULCHd4p9o2\"\
,\n \"livemode\": false,\n \"metadata\": {},\n \"type\": \"card\"\
\n },\n \"payment_method_configuration_details\": null,\n \"payment_method_options\"\
: {\n \"card\": {\n \"mandate_options\": null,\n \"network\"\
: null,\n \"request_three_d_secure\": \"automatic\"\n }\n },\n\
\ \"payment_method_types\": [\n \"card\",\n \"link\",\n \"\
paypal\"\n ],\n \"single_use_mandate\": null,\n \"status\": \"succeeded\"\
,\n \"usage\": \"off_session\"\n },\n \"shipping_address_collection\":\
\ null,\n \"shipping_cost\": null,\n \"shipping_details\": null,\n \"shipping_options\"\
: [],\n \"status\": \"complete\",\n \"submit_type\": null,\n \"subscription\"\
: null,\n \"success_url\": \"http://testserver/subscription/1/payment-method/change/{CHECKOUT_SESSION_ID}/\"\
,\n \"total_details\": null,\n \"ui_mode\": \"hosted\",\n \"url\": null\n\
}"
content_type: text/plain; charset=utf-8
method: GET
status: 200
url: https://api.stripe.com/v1/checkout/sessions/cs_test_c1QeSt36UcbmnmrXJnEYZpWakr377WPMfbWLeR9d3ZBYJPWXRUJ3TQ0UG9?expand%5B0%5D=setup_intent&expand%5B1%5D=setup_intent.payment_method

View File

@ -21,6 +21,7 @@ import subscriptions.tasks
import users.tasks
import users.tests.util as util
responses_dir = 'subscriptions/tests/_responses/'
required_address_data = {
'country': 'NL',
'email': 'my.billing.email@example.com',
@ -176,7 +177,7 @@ class TestGETJoinView(BaseSubscriptionTestCase):
class TestPOSTJoinView(BaseSubscriptionTestCase):
url_usd = reverse('subscriptions:join-billing-details', kwargs={'plan_variation_id': 1})
url = reverse('subscriptions:join-billing-details', kwargs={'plan_variation_id': 2})
responses._add_from_file('stripe_create_checkout_session.yaml')
responses._add_from_file(f'{responses_dir}stripe_create_checkout_session.yaml')
def test_post_updates_billing_address_and_customer_renders_next_form_de(self):
customer = create_customer_with_billing_address(vat_number='', country='DE')

View File

@ -5,11 +5,15 @@ from django.urls import reverse
from looper.models import PaymentMethod, PaymentMethodAuthentication, Gateway
from looper.money import Money
from looper.tests.factories import SubscriptionFactory
import responses
# from responses import _recorder
from common.tests.factories.users import UserFactory
from subscriptions.tests.base import BaseSubscriptionTestCase
import subscriptions.tasks
responses_dir = 'subscriptions/tests/_responses/'
required_address_data = {
'country': 'NL',
'email': 'my.billing.email@example.com',
@ -99,6 +103,8 @@ class TestSubscriptionSettingsChangePaymentMethod(BaseSubscriptionTestCase):
url_name = 'subscriptions:payment-method-change'
success_url_name = 'user-settings-billing'
# @_recorder.record(file_path=f'{responses_dir}stripe_create_checkout_session_setup.yaml')
# @_recorder.record(file_path=f'{responses_dir}stripe_retrieve_checkout_session_setup.yaml')
def test_can_change_payment_method_from_bank_to_credit_card_with_sca(self):
bank = Gateway.objects.get(name='bank')
subscription = SubscriptionFactory(
@ -112,15 +118,23 @@ class TestSubscriptionSettingsChangePaymentMethod(BaseSubscriptionTestCase):
self.client.force_login(self.user)
url = reverse(self.url_name, kwargs={'subscription_id': subscription.pk})
data = {
**self.shared_payment_form_data,
'gateway': 'braintree',
'payment_method_nonce': 'fake-three-d-secure-visa-full-authentication-nonce',
}
response = self.client.post(url, data=data)
with responses.RequestsMock() as rsps:
rsps._add_from_file(f'{responses_dir}stripe_create_checkout_session_setup.yaml')
response = self.client.post(url)
self.assertEqual(response.status_code, 302)
self.assertEqual(response['Location'], reverse(self.success_url_name))
expected_redirect_url = 'https://checkout.stripe.com/c/pay/cs_test_c1QeSt36UcbmnmrXJnEYZpWakr377WPMfbWLeR9d3ZBYJPWXRUJ3TQ0UG9#fidkdWxOYHwnPyd1blpxYHZxWjA0VUpnNzNAMU5EUEcwYW92dWIwclxRMzQ8ZkxsUDRET2dPbTVidnBCNEJTdlBJQTRJYFF2c09BMEFBdlxVT19USGpWSXRSXFJwdm5UQXRpdVw2Rmp%2FZ11NNTU3fHdHUl1JTCcpJ2N3amhWYHdzYHcnP3F3cGApJ2lkfGpwcVF8dWAnPyd2bGtiaWBaZmppcGhrJyknYGtkZ2lgVWlkZmBtamlhYHd2Jz9xd3BgeCUl'
self.assertEqual(response['Location'], expected_redirect_url, response['Location'])
# Pretend that checkout session was completed and we've returned to the success page with its ID:
checkout_session_id = 'cs_test_c1QeSt36UcbmnmrXJnEYZpWakr377WPMfbWLeR9d3ZBYJPWXRUJ3TQ0UG9'
success_url = url + f'{checkout_session_id}/'
with responses.RequestsMock() as rsps:
rsps._add_from_file(f'{responses_dir}stripe_retrieve_checkout_session_setup.yaml')
response = self.client.get(success_url)
self.assertEqual(response.status_code, 302)
self.assertEqual(response['Location'], '/subscription/1/manage/')
# New payment method was created
self.assertEqual(PaymentMethod.objects.count(), 2)
@ -130,40 +144,9 @@ class TestSubscriptionSettingsChangePaymentMethod(BaseSubscriptionTestCase):
self.assertNotEqual(subscription.payment_method_id, payment_method.pk)
self.assertEqual(
str(subscription.payment_method),
'Visa credit card ending in 0002',
'visa credit card ending in 4242',
)
# SCA was stored
self.assertIsNotNone(PaymentMethodAuthentication.objects.first())
def test_can_change_payment_method_from_credit_card_to_bank(self):
braintree = Gateway.objects.get(name='braintree')
subscription = SubscriptionFactory(
customer=self.user.customer,
payment_method__customer_id=self.user.customer.pk,
payment_method__gateway=braintree,
)
self.assertEqual(PaymentMethod.objects.count(), 1)
payment_method = subscription.payment_method
self.client.force_login(self.user)
url = reverse(self.url_name, kwargs={'subscription_id': subscription.pk})
data = {
**self.shared_payment_form_data,
'gateway': 'bank',
'payment_method_nonce': 'unused',
}
response = self.client.post(url, data=data)
self.assertEqual(response.status_code, 302)
self.assertEqual(response['Location'], reverse(self.success_url_name))
# New payment method was created
self.assertEqual(PaymentMethod.objects.count(), 2)
subscription.refresh_from_db()
subscription.payment_method.refresh_from_db()
# Subscription's payment method was changed to bank transfer
self.assertNotEqual(subscription.payment_method_id, payment_method.pk)
self.assertEqual(str(subscription.payment_method), 'Bank Transfer')
# SCA isn't stored for Stripe payment methods
self.assertIsNone(PaymentMethodAuthentication.objects.first())