Stripe checkout #104411
@ -508,7 +508,7 @@ GATEWAYS = {
|
|||||||
'api_publishable_key': _get('STRIPE_API_PUBLISHABLE_KEY'),
|
'api_publishable_key': _get('STRIPE_API_PUBLISHABLE_KEY'),
|
||||||
'api_secret_key': _get('STRIPE_API_SECRET_KEY'),
|
'api_secret_key': _get('STRIPE_API_SECRET_KEY'),
|
||||||
'endpoint_secret': _get('STRIPE_ENDPOINT_SECRET'),
|
'endpoint_secret': _get('STRIPE_ENDPOINT_SECRET'),
|
||||||
'supported_collection_methods': {'automatic'},
|
'supported_collection_methods': {'automatic', 'manual'},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,14 +161,10 @@ class PaymentForm(BillingAddressForm):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
# These are used when a payment fails, so that the next attempt to pay can reuse
|
|
||||||
# the already-created subscription and order.
|
|
||||||
subscription_pk = forms.CharField(widget=forms.HiddenInput(), required=False)
|
|
||||||
order_pk = forms.CharField(widget=forms.HiddenInput(), required=False)
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
"""Pre-fill additional initial data from request."""
|
"""Pre-fill additional initial data from request."""
|
||||||
self.request = kwargs.pop('request', None)
|
self.request = kwargs.pop('request', None)
|
||||||
|
self.plan_variation = kwargs.pop('plan_variation', None)
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
@ -190,6 +186,16 @@ class PaymentForm(BillingAddressForm):
|
|||||||
if self.request.user.full_name:
|
if self.request.user.full_name:
|
||||||
self.initial['full_name'] = self.request.user.full_name
|
self.initial['full_name'] = self.request.user.full_name
|
||||||
|
|
||||||
|
def clean_gateway(self):
|
||||||
|
"""Validate gateway against selected plan variation."""
|
||||||
|
gw = self.cleaned_data['gateway']
|
||||||
|
if not self.plan_variation:
|
||||||
|
return gw
|
||||||
|
if self.plan_variation.collection_method not in gw.provider.supported_collection_methods:
|
||||||
|
msg = self.fields['gateway'].default_error_messages['invalid_choice']
|
||||||
|
self.add_error('gateway', msg)
|
||||||
|
return gw
|
||||||
|
|
||||||
|
|
||||||
class SelectPlanVariationForm(forms.Form):
|
class SelectPlanVariationForm(forms.Form):
|
||||||
"""Form used in the plan selector."""
|
"""Form used in the plan selector."""
|
||||||
|
158
subscriptions/tests/_responses/stripe_get_cs_eur.yaml
Normal file
158
subscriptions/tests/_responses/stripe_get_cs_eur.yaml
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
responses:
|
||||||
|
- response:
|
||||||
|
auto_calculate_content_length: false
|
||||||
|
body: "{\n \"id\": \"cs_test_a19GMh7hJXYbh9OhEln16y1M8hfrdu0ySIpjRX4HQJqSVvpe9a9UP30bWW\"\
|
||||||
|
,\n \"object\": \"checkout.session\",\n \"after_expiration\": null,\n \"\
|
||||||
|
allow_promotion_codes\": null,\n \"amount_subtotal\": 1252,\n \"amount_total\"\
|
||||||
|
: 1252,\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/10/billing/\",\n\
|
||||||
|
\ \"client_reference_id\": null,\n \"client_secret\": null,\n \"consent\"\
|
||||||
|
: null,\n \"consent_collection\": null,\n \"created\": 1718354548,\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_QI5uhaeNfeXwYP\",\n \"customer_creation\": null,\n \"customer_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\": \"my.billing.email@example.com\"\
|
||||||
|
,\n \"name\": \"New Full Name\",\n \"phone\": null,\n \"tax_exempt\"\
|
||||||
|
: \"none\",\n \"tax_ids\": []\n },\n \"customer_email\": null,\n \"expires_at\"\
|
||||||
|
: 1718440948,\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\": {\n \"id\": \"pi_3PRVj3E4KAUB5djs1xbsGdDP\"\
|
||||||
|
,\n \"object\": \"payment_intent\",\n \"amount\": 1252,\n \"amount_capturable\"\
|
||||||
|
: 0,\n \"amount_details\": {\n \"tip\": {}\n },\n \"amount_received\"\
|
||||||
|
: 1252,\n \"application\": null,\n \"application_fee_amount\": null,\n\
|
||||||
|
\ \"automatic_payment_methods\": null,\n \"canceled_at\": null,\n \"\
|
||||||
|
cancellation_reason\": null,\n \"capture_method\": \"automatic\",\n \"\
|
||||||
|
client_secret\": \"pi_3PRVj3E4KAUB5djs1xbsGdDP_secret_BClI8ssVIUSjybh2UvIeFa6v0\"\
|
||||||
|
,\n \"confirmation_method\": \"automatic\",\n \"created\": 1718354593,\n\
|
||||||
|
\ \"currency\": \"eur\",\n \"customer\": {\n \"id\": \"cus_QI5uhaeNfeXwYP\"\
|
||||||
|
,\n \"object\": \"customer\",\n \"address\": null,\n \"balance\"\
|
||||||
|
: 0,\n \"created\": 1718354548,\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\": \"8D38744D\",\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\
|
||||||
|
\ },\n \"description\": null,\n \"invoice\": null,\n \"last_payment_error\"\
|
||||||
|
: null,\n \"latest_charge\": {\n \"id\": \"ch_3PRVj3E4KAUB5djs16uXWpi8\"\
|
||||||
|
,\n \"object\": \"charge\",\n \"amount\": 1252,\n \"amount_captured\"\
|
||||||
|
: 1252,\n \"amount_refunded\": 0,\n \"application\": null,\n \
|
||||||
|
\ \"application_fee\": null,\n \"application_fee_amount\": null,\n \
|
||||||
|
\ \"balance_transaction\": \"txn_3PRVj3E4KAUB5djs1sxkERoM\",\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\": \"my.billing.email@example.com\",\n \"name\": \"Jane Doe\",\n\
|
||||||
|
\ \"phone\": null\n },\n \"calculated_statement_descriptor\"\
|
||||||
|
: \"BLENDER STUDIO\",\n \"captured\": true,\n \"created\": 1718354593,\n\
|
||||||
|
\ \"currency\": \"eur\",\n \"customer\": \"cus_QI5uhaeNfeXwYP\",\n\
|
||||||
|
\ \"description\": null,\n \"destination\": null,\n \"dispute\"\
|
||||||
|
: null,\n \"disputed\": false,\n \"failure_balance_transaction\":\
|
||||||
|
\ null,\n \"failure_code\": null,\n \"failure_message\": null,\n \
|
||||||
|
\ \"fraud_details\": {},\n \"invoice\": null,\n \"livemode\":\
|
||||||
|
\ false,\n \"metadata\": {\n \"order_id\": \"1\"\n },\n \
|
||||||
|
\ \"on_behalf_of\": null,\n \"order\": null,\n \"outcome\": {\n\
|
||||||
|
\ \"network_status\": \"approved_by_network\",\n \"reason\": null,\n\
|
||||||
|
\ \"risk_level\": \"normal\",\n \"risk_score\": 16,\n \"\
|
||||||
|
seller_message\": \"Payment complete.\",\n \"type\": \"authorized\"\n\
|
||||||
|
\ },\n \"paid\": true,\n \"payment_intent\": \"pi_3PRVj3E4KAUB5djs1xbsGdDP\"\
|
||||||
|
,\n \"payment_method\": \"pm_1PRVj2E4KAUB5djsNQr0k105\",\n \"payment_method_details\"\
|
||||||
|
: {\n \"card\": {\n \"amount_authorized\": 1252,\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 \"exp_month\"\
|
||||||
|
: 12,\n \"exp_year\": 2033,\n \"extended_authorization\":\
|
||||||
|
\ {\n \"status\": \"disabled\"\n },\n \"fingerprint\"\
|
||||||
|
: \"YcmpGi38fZZuBsh4\",\n \"funding\": \"credit\",\n \"incremental_authorization\"\
|
||||||
|
: {\n \"status\": \"unavailable\"\n },\n \"installments\"\
|
||||||
|
: null,\n \"last4\": \"4242\",\n \"mandate\": null,\n \
|
||||||
|
\ \"multicapture\": {\n \"status\": \"unavailable\"\n \
|
||||||
|
\ },\n \"network\": \"visa\",\n \"network_token\": {\n\
|
||||||
|
\ \"used\": false\n },\n \"overcapture\": {\n \
|
||||||
|
\ \"maximum_amount_capturable\": 1252,\n \"status\": \"\
|
||||||
|
unavailable\"\n },\n \"three_d_secure\": null,\n \
|
||||||
|
\ \"wallet\": null\n },\n \"type\": \"card\"\n },\n \
|
||||||
|
\ \"radar_options\": {},\n \"receipt_email\": null,\n \"receipt_number\"\
|
||||||
|
: null,\n \"receipt_url\": \"https://pay.stripe.com/receipts/payment/CAcaFwoVYWNjdF8xUE9iMjZFNEtBVUI1ZGpzKPOJsLMGMgbH3ZicbJc6LBYdfpRMBgTew5GCKCPV-K4DC44oir_sd03RTaqsJpsM5qstpEWJU0oqii03\"\
|
||||||
|
,\n \"refunded\": false,\n \"review\": null,\n \"shipping\":\
|
||||||
|
\ null,\n \"source\": null,\n \"source_transfer\": null,\n \"\
|
||||||
|
statement_descriptor\": null,\n \"statement_descriptor_suffix\": null,\n\
|
||||||
|
\ \"status\": \"succeeded\",\n \"transfer_data\": null,\n \"\
|
||||||
|
transfer_group\": null\n },\n \"livemode\": false,\n \"metadata\":\
|
||||||
|
\ {\n \"order_id\": \"1\"\n },\n \"next_action\": null,\n \"on_behalf_of\"\
|
||||||
|
: null,\n \"payment_method\": {\n \"id\": \"pm_1PRVj2E4KAUB5djsNQr0k105\"\
|
||||||
|
,\n \"object\": \"payment_method\",\n \"allow_redisplay\": \"limited\"\
|
||||||
|
,\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\": \"my.billing.email@example.com\",\n \
|
||||||
|
\ \"name\": \"Jane Doe\",\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\": 1718354592,\n\
|
||||||
|
\ \"customer\": \"cus_QI5uhaeNfeXwYP\",\n \"livemode\": false,\n \
|
||||||
|
\ \"metadata\": {},\n \"type\": \"card\"\n },\n \"payment_method_configuration_details\"\
|
||||||
|
: null,\n \"payment_method_options\": {\n \"card\": {\n \"installments\"\
|
||||||
|
: null,\n \"mandate_options\": null,\n \"network\": null,\n \
|
||||||
|
\ \"request_three_d_secure\": \"automatic\"\n }\n },\n \"payment_method_types\"\
|
||||||
|
: [\n \"card\"\n ],\n \"processing\": null,\n \"receipt_email\"\
|
||||||
|
: null,\n \"review\": null,\n \"setup_future_usage\": \"off_session\"\
|
||||||
|
,\n \"shipping\": null,\n \"source\": null,\n \"statement_descriptor\"\
|
||||||
|
: null,\n \"statement_descriptor_suffix\": null,\n \"status\": \"succeeded\"\
|
||||||
|
,\n \"transfer_data\": null,\n \"transfer_group\": null\n },\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\": \"paid\",\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\": \"complete\",\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\": null\n\
|
||||||
|
}"
|
||||||
|
content_type: text/plain
|
||||||
|
method: GET
|
||||||
|
status: 200
|
||||||
|
url: https://api.stripe.com/v1/checkout/sessions/cs_test_a19GMh7hJXYbh9OhEln16y1M8hfrdu0ySIpjRX4HQJqSVvpe9a9UP30bWW?expand%5B0%5D=payment_intent&expand%5B1%5D=payment_intent.customer&expand%5B2%5D=payment_intent.latest_charge&expand%5B3%5D=payment_intent.latest_charge.payment_method_details&expand%5B4%5D=payment_intent.payment_method
|
||||||
|
- response:
|
||||||
|
auto_calculate_content_length: false
|
||||||
|
body: "{\n \"id\": \"pm_1PRVj2E4KAUB5djsNQr0k105\",\n \"object\": \"payment_method\"\
|
||||||
|
,\n \"allow_redisplay\": \"limited\",\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\": \"my.billing.email@example.com\",\n \"name\": \"\
|
||||||
|
Jane Doe\",\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\": 1718354592,\n \"customer\"\
|
||||||
|
: \"cus_QI5uhaeNfeXwYP\",\n \"livemode\": false,\n \"metadata\": {},\n \"\
|
||||||
|
type\": \"card\"\n}"
|
||||||
|
content_type: text/plain
|
||||||
|
method: GET
|
||||||
|
status: 200
|
||||||
|
url: https://api.stripe.com/v1/payment_methods/pm_1PRVj2E4KAUB5djsNQr0k105
|
@ -1,11 +1,11 @@
|
|||||||
responses:
|
responses:
|
||||||
- response:
|
- response:
|
||||||
auto_calculate_content_length: false
|
auto_calculate_content_length: false
|
||||||
body: "{\n \"id\": \"cus_QFaxRrT6ZbD9NN\",\n \"object\": \"customer\",\n \"\
|
body: "{\n \"id\": \"cus_QI5uhaeNfeXwYP\",\n \"object\": \"customer\",\n \"\
|
||||||
address\": null,\n \"balance\": 0,\n \"created\": 1717778124,\n \"currency\"\
|
address\": null,\n \"balance\": 0,\n \"created\": 1718354548,\n \"currency\"\
|
||||||
: null,\n \"default_source\": null,\n \"delinquent\": false,\n \"description\"\
|
: null,\n \"default_source\": null,\n \"delinquent\": false,\n \"description\"\
|
||||||
: null,\n \"discount\": null,\n \"email\": \"my.billing.email@example.com\"\
|
: null,\n \"discount\": null,\n \"email\": \"my.billing.email@example.com\"\
|
||||||
,\n \"invoice_prefix\": \"046F772E\",\n \"invoice_settings\": {\n \"custom_fields\"\
|
,\n \"invoice_prefix\": \"8D38744D\",\n \"invoice_settings\": {\n \"custom_fields\"\
|
||||||
: null,\n \"default_payment_method\": null,\n \"footer\": null,\n \"\
|
: null,\n \"default_payment_method\": null,\n \"footer\": null,\n \"\
|
||||||
rendering_options\": null\n },\n \"livemode\": false,\n \"metadata\": {},\n\
|
rendering_options\": null\n },\n \"livemode\": false,\n \"metadata\": {},\n\
|
||||||
\ \"name\": \"New Full Name\",\n \"phone\": null,\n \"preferred_locales\"\
|
\ \"name\": \"New Full Name\",\n \"phone\": null,\n \"preferred_locales\"\
|
||||||
@ -17,22 +17,22 @@ responses:
|
|||||||
url: https://api.stripe.com/v1/customers
|
url: https://api.stripe.com/v1/customers
|
||||||
- response:
|
- response:
|
||||||
auto_calculate_content_length: false
|
auto_calculate_content_length: false
|
||||||
body: "{\n \"id\": \"cs_test_a1hoP4Yj4ZmfghAwGoUtWJngVt1XreEVLGAj2n7U5o9BlvqhnDimuA07zh\"\
|
body: "{\n \"id\": \"cs_test_a19GMh7hJXYbh9OhEln16y1M8hfrdu0ySIpjRX4HQJqSVvpe9a9UP30bWW\"\
|
||||||
,\n \"object\": \"checkout.session\",\n \"after_expiration\": null,\n \"\
|
,\n \"object\": \"checkout.session\",\n \"after_expiration\": null,\n \"\
|
||||||
allow_promotion_codes\": null,\n \"amount_subtotal\": 990,\n \"amount_total\"\
|
allow_promotion_codes\": null,\n \"amount_subtotal\": 1252,\n \"amount_total\"\
|
||||||
: 990,\n \"automatic_tax\": {\n \"enabled\": false,\n \"liability\":\
|
: 1252,\n \"automatic_tax\": {\n \"enabled\": false,\n \"liability\"\
|
||||||
\ null,\n \"status\": null\n },\n \"billing_address_collection\": null,\n\
|
: null,\n \"status\": null\n },\n \"billing_address_collection\": null,\n\
|
||||||
\ \"cancel_url\": \"http://testserver/join/plan-variation/2/billing/\",\n \
|
\ \"cancel_url\": \"http://testserver/join/plan-variation/10/billing/\",\n\
|
||||||
\ \"client_reference_id\": null,\n \"client_secret\": null,\n \"consent\"\
|
\ \"client_reference_id\": null,\n \"client_secret\": null,\n \"consent\"\
|
||||||
: null,\n \"consent_collection\": null,\n \"created\": 1717778125,\n \"currency\"\
|
: null,\n \"consent_collection\": null,\n \"created\": 1718354548,\n \"currency\"\
|
||||||
: \"eur\",\n \"currency_conversion\": null,\n \"custom_fields\": [],\n \"\
|
: \"eur\",\n \"currency_conversion\": null,\n \"custom_fields\": [],\n \"\
|
||||||
custom_text\": {\n \"after_submit\": null,\n \"shipping_address\": null,\n\
|
custom_text\": {\n \"after_submit\": null,\n \"shipping_address\": null,\n\
|
||||||
\ \"submit\": null,\n \"terms_of_service_acceptance\": null\n },\n \"\
|
\ \"submit\": null,\n \"terms_of_service_acceptance\": null\n },\n \"\
|
||||||
customer\": \"cus_QFaxRrT6ZbD9NN\",\n \"customer_creation\": null,\n \"customer_details\"\
|
customer\": \"cus_QI5uhaeNfeXwYP\",\n \"customer_creation\": null,\n \"customer_details\"\
|
||||||
: {\n \"address\": null,\n \"email\": \"my.billing.email@example.com\"\
|
: {\n \"address\": null,\n \"email\": \"my.billing.email@example.com\"\
|
||||||
,\n \"name\": null,\n \"phone\": null,\n \"tax_exempt\": \"none\",\n\
|
,\n \"name\": null,\n \"phone\": null,\n \"tax_exempt\": \"none\",\n\
|
||||||
\ \"tax_ids\": null\n },\n \"customer_email\": null,\n \"expires_at\"\
|
\ \"tax_ids\": null\n },\n \"customer_email\": null,\n \"expires_at\"\
|
||||||
: 1717864524,\n \"invoice\": null,\n \"invoice_creation\": {\n \"enabled\"\
|
: 1718440948,\n \"invoice\": null,\n \"invoice_creation\": {\n \"enabled\"\
|
||||||
: false,\n \"invoice_data\": {\n \"account_tax_ids\": null,\n \"\
|
: false,\n \"invoice_data\": {\n \"account_tax_ids\": null,\n \"\
|
||||||
custom_fields\": null,\n \"description\": null,\n \"footer\": null,\n\
|
custom_fields\": null,\n \"description\": null,\n \"footer\": null,\n\
|
||||||
\ \"issuer\": null,\n \"metadata\": {},\n \"rendering_options\"\
|
\ \"issuer\": null,\n \"metadata\": {},\n \"rendering_options\"\
|
||||||
@ -51,7 +51,7 @@ responses:
|
|||||||
: null,\n \"success_url\": \"http://testserver/looper/stripe_success/1/{CHECKOUT_SESSION_ID}\"\
|
: null,\n \"success_url\": \"http://testserver/looper/stripe_success/1/{CHECKOUT_SESSION_ID}\"\
|
||||||
,\n \"total_details\": {\n \"amount_discount\": 0,\n \"amount_shipping\"\
|
,\n \"total_details\": {\n \"amount_discount\": 0,\n \"amount_shipping\"\
|
||||||
: 0,\n \"amount_tax\": 0\n },\n \"ui_mode\": \"hosted\",\n \"url\": \"\
|
: 0,\n \"amount_tax\": 0\n },\n \"ui_mode\": \"hosted\",\n \"url\": \"\
|
||||||
https://checkout.stripe.com/c/pay/cs_test_a1hoP4Yj4ZmfghAwGoUtWJngVt1XreEVLGAj2n7U5o9BlvqhnDimuA07zh#fidkdWxOYHwnPyd1blpxYHZxWjA0VUpnNzNAMU5EUEcwYW92dWIwclxRMzQ8ZkxsUDRET2dPbTVidnBCNEJTdlBJQTRJYFF2c09BMEFBdlxVT19USGpWSXRSXFJwdm5UQXRpdVw2Rmp%2FZ11NNTU3fHdHUl1JTCcpJ2N3amhWYHdzYHcnP3F3cGApJ2lkfGpwcVF8dWAnPyd2bGtiaWBabHFgaCcpJ2BrZGdpYFVpZGZgbWppYWB3dic%2FcXdwYHgl\"\
|
https://checkout.stripe.com/c/pay/cs_test_a19GMh7hJXYbh9OhEln16y1M8hfrdu0ySIpjRX4HQJqSVvpe9a9UP30bWW#fidkdWxOYHwnPyd1blpxYHZxWjA0VUpnNzNAMU5EUEcwYW92dWIwclxRMzQ8ZkxsUDRET2dPbTVidnBCNEJTdlBJQTRJYFF2c09BMEFBdlxVT19USGpWSXRSXFJwdm5UQXRpdVw2Rmp%2FZ11NNTU3fHdHUl1JTCcpJ2N3amhWYHdzYHcnP3F3cGApJ2lkfGpwcVF8dWAnPyd2bGtiaWBabHFgaCcpJ2BrZGdpYFVpZGZgbWppYWB3dic%2FcXdwYHgl\"\
|
||||||
\n}"
|
\n}"
|
||||||
content_type: text/plain
|
content_type: text/plain
|
||||||
method: POST
|
method: POST
|
9
subscriptions/tests/_responses/vies_valid.yaml
Normal file
9
subscriptions/tests/_responses/vies_valid.yaml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
responses:
|
||||||
|
- response:
|
||||||
|
auto_calculate_content_length: false
|
||||||
|
body: <env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"><env:Header/><env:Body><ns2:checkVatResponse
|
||||||
|
xmlns:ns2="urn:ec.europa.eu:taxud:vies:services:checkVat:types"><ns2:countryCode>DE</ns2:countryCode><ns2:vatNumber>260543043</ns2:vatNumber><ns2:requestDate>2024-06-14+02:00</ns2:requestDate><ns2:valid>true</ns2:valid><ns2:name>---</ns2:name><ns2:address>---</ns2:address></ns2:checkVatResponse></env:Body></env:Envelope>
|
||||||
|
content_type: text/plain
|
||||||
|
method: POST
|
||||||
|
status: 200
|
||||||
|
url: https://ec.europa.eu/taxation_customs/vies/services/checkVatService
|
163
subscriptions/tests/_responses/vies_wsdl.yaml
Normal file
163
subscriptions/tests/_responses/vies_wsdl.yaml
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
responses:
|
||||||
|
- response:
|
||||||
|
auto_calculate_content_length: false
|
||||||
|
body: "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<wsdl:definitions targetNamespace=\"\
|
||||||
|
urn:ec.europa.eu:taxud:vies:services:checkVat\" xmlns:tns1=\"urn:ec.europa.eu:taxud:vies:services:checkVat:types\"\
|
||||||
|
\ xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\" xmlns:impl=\"\
|
||||||
|
urn:ec.europa.eu:taxud:vies:services:checkVat\" xmlns:apachesoap=\"http://xml.apache.org/xml-soap\"\
|
||||||
|
\ xmlns:wsdl=\"http://schemas.xmlsoap.org/wsdl/\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"\
|
||||||
|
\ xmlns:wsdlsoap=\"http://schemas.xmlsoap.org/wsdl/soap/\">\n <xsd:documentation>\n\
|
||||||
|
\t The objective of this Internet site is to allow persons involved in the intra-Community\
|
||||||
|
\ supply of goods or of services to obtain confirmation of the validity of the\
|
||||||
|
\ VAT identification number of any specified person, in accordance to article\
|
||||||
|
\ 31 of Council Regulation (EC) No. 904/2010 of 7 October 2010.\n Any other\
|
||||||
|
\ use and any extraction and use of the data which is not in conformity with\
|
||||||
|
\ the objective of this site is strictly forbidden. \n Any retransmission\
|
||||||
|
\ of the contents of this site, whether for a commercial purpose or otherwise,\
|
||||||
|
\ as well as any more general use other than as far as is necessary to support\
|
||||||
|
\ the activity of a legitimate user (for example: to draw up their own invoices)\
|
||||||
|
\ is expressly forbidden. In addition, any copying or reproduction of the contents\
|
||||||
|
\ of this site is strictly forbidden. \n The European Commission maintains\
|
||||||
|
\ this website to enhance the access by taxable persons making intra-Community\
|
||||||
|
\ supplies to verification of their customers' VAT identification numbers. Our\
|
||||||
|
\ goal is to supply instantaneous and accurate information. \n However the\
|
||||||
|
\ Commission accepts no responsibility or liability whatsoever with regard to\
|
||||||
|
\ the information obtained using this site. This information: \n - is obtained\
|
||||||
|
\ from Member States' databases over which the Commission services have no control\
|
||||||
|
\ and for which the Commission assumes no responsibility; it is the responsibility\
|
||||||
|
\ of the Member States to keep their databases complete, accurate and up to\
|
||||||
|
\ date; \n - is not professional or legal advice (if you need specific advice,\
|
||||||
|
\ you should always consult a suitably qualified professional); \n - does\
|
||||||
|
\ not in itself give a right to exempt intra-Community supplies from Value Added\
|
||||||
|
\ Tax; \n - does not change any obligations imposed on taxable persons in\
|
||||||
|
\ relation to intra-Community supplies. \n It is our goal to minimise disruption\
|
||||||
|
\ caused by technical errors. However some data or information on our site may\
|
||||||
|
\ have been created or structured in files or formats which are not error-free\
|
||||||
|
\ and we cannot guarantee that our service will not be interrupted or otherwise\
|
||||||
|
\ affected by such problems. The Commission accepts no responsibility with regard\
|
||||||
|
\ to such problems incurred as a result of using this site or any linked external\
|
||||||
|
\ sites. \n This disclaimer is not intended to limit the liability of the\
|
||||||
|
\ Commission in contravention of any requirements laid down in applicable national\
|
||||||
|
\ law nor to exclude its liability for matters which may not be excluded under\
|
||||||
|
\ that law. \n Collecting or handling personal data falls under the Data\
|
||||||
|
\ Protection Notice. This data protection declaration explains the Processing\
|
||||||
|
\ in the VIES-on-the-web Internet Website of VAT Identification Numbers for\
|
||||||
|
\ intra-Community Transaction on Goods or Services. Details of your legal rights\
|
||||||
|
\ associated with the collection, processing and use of this data are also provided:\
|
||||||
|
\ http://ec.europa.eu/dpo-register/details.htm?id=40647 . \n \n Usage:\
|
||||||
|
\ \n The countryCode input parameter must follow the pattern [A-Z]{2} \n\
|
||||||
|
\ The vatNumber input parameter must follow the pattern [0-9A-Za-z\\+\\*\\\
|
||||||
|
.]{2,12} \n In case of problems, the returned FaultString can take the following\
|
||||||
|
\ specific values: \n - INVALID_INPUT: The provided CountryCode is invalid\
|
||||||
|
\ or the VAT number is empty; \n - GLOBAL_MAX_CONCURRENT_REQ: Your Request\
|
||||||
|
\ for VAT validation has not been processed; the maximum number of concurrent\
|
||||||
|
\ requests has been reached. Please re-submit your request later or contact\
|
||||||
|
\ TAXUD-VIESWEB@ec.europa.eu for further information\": Your request cannot\
|
||||||
|
\ be processed due to high traffic on the web application. Please try again\
|
||||||
|
\ later; \n - MS_MAX_CONCURRENT_REQ: Your Request for VAT validation has\
|
||||||
|
\ not been processed; the maximum number of concurrent requests for this Member\
|
||||||
|
\ State has been reached. Please re-submit your request later or contact TAXUD-VIESWEB@ec.europa.eu\
|
||||||
|
\ for further information\": Your request cannot be processed due to high traffic\
|
||||||
|
\ towards the Member State you are trying to reach. Please try again later.\
|
||||||
|
\ \n - SERVICE_UNAVAILABLE: an error was encountered either at the network\
|
||||||
|
\ level or the Web application level, try again later; \n - MS_UNAVAILABLE:\
|
||||||
|
\ The application at the Member State is not replying or not available. Please\
|
||||||
|
\ refer to the Technical Information page to check the status of the requested\
|
||||||
|
\ Member State, try again later; \n - TIMEOUT: The application did not receive\
|
||||||
|
\ a reply within the allocated time period, try again later. \n\t</xsd:documentation>\n\
|
||||||
|
\ \n <wsdl:types>\n <xsd:schema attributeFormDefault=\"qualified\" elementFormDefault=\"\
|
||||||
|
qualified\" targetNamespace=\"urn:ec.europa.eu:taxud:vies:services:checkVat:types\"\
|
||||||
|
\ xmlns=\"urn:ec.europa.eu:taxud:vies:services:checkVat:types\">\n\t\t\t<xsd:element\
|
||||||
|
\ name=\"checkVat\">\n\t\t\t\t<xsd:complexType>\n\t\t\t\t\t<xsd:sequence>\n\t\
|
||||||
|
\t\t\t\t\t<xsd:element name=\"countryCode\" type=\"xsd:string\"/>\n\t\t\t\t\t\
|
||||||
|
\t<xsd:element name=\"vatNumber\" type=\"xsd:string\"/>\n\t\t\t\t\t</xsd:sequence>\n\
|
||||||
|
\t\t\t\t</xsd:complexType>\n\t\t\t</xsd:element>\n\t\t\t<xsd:element name=\"\
|
||||||
|
checkVatResponse\">\n\t\t\t\t<xsd:complexType>\n\t\t\t\t\t<xsd:sequence>\n\t\
|
||||||
|
\t\t\t\t\t<xsd:element name=\"countryCode\" type=\"xsd:string\"/>\n\t\t\t\t\t\
|
||||||
|
\t<xsd:element name=\"vatNumber\" type=\"xsd:string\"/>\n\t\t\t\t\t\t<xsd:element\
|
||||||
|
\ name=\"requestDate\" type=\"xsd:date\"/>\n\t\t\t\t\t\t<xsd:element name=\"\
|
||||||
|
valid\" type=\"xsd:boolean\"/>\n\t\t\t\t\t\t<xsd:element maxOccurs=\"1\" minOccurs=\"\
|
||||||
|
0\" name=\"name\" nillable=\"true\" type=\"xsd:string\"/>\n\t\t\t\t\t\t<xsd:element\
|
||||||
|
\ maxOccurs=\"1\" minOccurs=\"0\" name=\"address\" nillable=\"true\" type=\"\
|
||||||
|
xsd:string\"/>\n\t\t\t\t\t</xsd:sequence>\n\t\t\t\t</xsd:complexType>\n\t\t\t\
|
||||||
|
</xsd:element>\n\t\t\t<xsd:element name=\"checkVatApprox\">\n\t\t\t\t<xsd:complexType>\n\
|
||||||
|
\t\t\t\t\t<xsd:sequence>\n\t\t\t\t\t\t<xsd:element name=\"countryCode\" type=\"\
|
||||||
|
xsd:string\"/>\n\t\t\t\t\t\t<xsd:element name=\"vatNumber\" type=\"xsd:string\"\
|
||||||
|
/>\n\t\t\t\t\t\t<xsd:element maxOccurs=\"1\" minOccurs=\"0\" name=\"traderName\"\
|
||||||
|
\ type=\"xsd:string\"/>\n\t\t\t\t\t\t<xsd:element maxOccurs=\"1\" minOccurs=\"\
|
||||||
|
0\" name=\"traderCompanyType\" type=\"tns1:companyTypeCode\"/>\n\t\t\t\t\t\t\
|
||||||
|
<xsd:element maxOccurs=\"1\" minOccurs=\"0\" name=\"traderStreet\" type=\"xsd:string\"\
|
||||||
|
/>\n\t\t\t\t\t\t<xsd:element maxOccurs=\"1\" minOccurs=\"0\" name=\"traderPostcode\"\
|
||||||
|
\ type=\"xsd:string\"/>\n\t\t\t\t\t\t<xsd:element maxOccurs=\"1\" minOccurs=\"\
|
||||||
|
0\" name=\"traderCity\" type=\"xsd:string\"/>\n\t\t\t\t\t\t<xsd:element maxOccurs=\"\
|
||||||
|
1\" minOccurs=\"0\" name=\"requesterCountryCode\" type=\"xsd:string\"/>\n\t\t\
|
||||||
|
\t\t\t\t<xsd:element maxOccurs=\"1\" minOccurs=\"0\" name=\"requesterVatNumber\"\
|
||||||
|
\ type=\"xsd:string\"/>\n\t\t\t\t\t</xsd:sequence>\n\t\t\t\t</xsd:complexType>\n\
|
||||||
|
\t\t\t</xsd:element>\n\t\t\t<xsd:element name=\"checkVatApproxResponse\">\n\t\
|
||||||
|
\t\t\t<xsd:complexType>\n\t\t\t\t\t<xsd:sequence>\n\t\t\t\t\t\t<xsd:element\
|
||||||
|
\ name=\"countryCode\" type=\"xsd:string\"/>\n\t\t\t\t\t\t<xsd:element name=\"\
|
||||||
|
vatNumber\" type=\"xsd:string\"/>\n\t\t\t\t\t\t<xsd:element name=\"requestDate\"\
|
||||||
|
\ type=\"xsd:date\"/>\n\t\t\t\t\t\t<xsd:element name=\"valid\" type=\"xsd:boolean\"\
|
||||||
|
/>\n\t\t\t\t\t\t<xsd:element maxOccurs=\"1\" minOccurs=\"0\" name=\"traderName\"\
|
||||||
|
\ nillable=\"true\" type=\"xsd:string\"/>\n\t\t\t\t\t\t<xsd:element maxOccurs=\"\
|
||||||
|
1\" minOccurs=\"0\" name=\"traderCompanyType\" nillable=\"true\" type=\"tns1:companyTypeCode\"\
|
||||||
|
/>\n\t\t\t\t\t\t<xsd:element maxOccurs=\"1\" minOccurs=\"0\" name=\"traderAddress\"\
|
||||||
|
\ type=\"xsd:string\"/>\n\t\t\t\t\t\t<xsd:element maxOccurs=\"1\" minOccurs=\"\
|
||||||
|
0\" name=\"traderStreet\" type=\"xsd:string\"/>\n\t\t\t\t\t\t<xsd:element maxOccurs=\"\
|
||||||
|
1\" minOccurs=\"0\" name=\"traderPostcode\" type=\"xsd:string\"/>\n\t\t\t\t\t\
|
||||||
|
\t<xsd:element maxOccurs=\"1\" minOccurs=\"0\" name=\"traderCity\" type=\"xsd:string\"\
|
||||||
|
/>\n\t\t\t\t\t\t<xsd:element maxOccurs=\"1\" minOccurs=\"0\" name=\"traderNameMatch\"\
|
||||||
|
\ type=\"tns1:matchCode\"/>\n\t\t\t\t\t\t<xsd:element maxOccurs=\"1\" minOccurs=\"\
|
||||||
|
0\" name=\"traderCompanyTypeMatch\" type=\"tns1:matchCode\"/>\n\t\t\t\t\t\t\
|
||||||
|
<xsd:element maxOccurs=\"1\" minOccurs=\"0\" name=\"traderStreetMatch\" type=\"\
|
||||||
|
tns1:matchCode\"/>\n\t\t\t\t\t\t<xsd:element maxOccurs=\"1\" minOccurs=\"0\"\
|
||||||
|
\ name=\"traderPostcodeMatch\" type=\"tns1:matchCode\"/>\n\t\t\t\t\t\t<xsd:element\
|
||||||
|
\ maxOccurs=\"1\" minOccurs=\"0\" name=\"traderCityMatch\" type=\"tns1:matchCode\"\
|
||||||
|
/>\n\t\t\t\t\t\t<xsd:element name=\"requestIdentifier\" type=\"xsd:string\"\
|
||||||
|
/>\n\t\t\t\t\t</xsd:sequence>\n\t\t\t\t</xsd:complexType>\n\t\t\t</xsd:element>\n\
|
||||||
|
\t\t\t<xsd:simpleType name=\"companyTypeCode\">\n\t\t\t\t<xsd:restriction base=\"\
|
||||||
|
xsd:string\">\n\t\t\t\t\t<xsd:pattern value=\"[A-Z]{2}\\-[1-9][0-9]?\"/>\n\t\
|
||||||
|
\t\t\t</xsd:restriction>\n\t\t\t</xsd:simpleType>\n\t\t\t<xsd:simpleType name=\"\
|
||||||
|
matchCode\">\n\t\t\t\t<xsd:restriction base=\"xsd:string\">\n\t\t\t\t\t<xsd:enumeration\
|
||||||
|
\ value=\"1\">\n\t\t\t\t\t\t<xsd:annotation>\n\t\t\t\t\t\t\t<xsd:documentation>VALID</xsd:documentation>\n\
|
||||||
|
\t\t\t\t\t\t</xsd:annotation>\n\t\t\t\t\t</xsd:enumeration>\n\t\t\t\t\t<xsd:enumeration\
|
||||||
|
\ value=\"2\">\n <xsd:annotation>\n \
|
||||||
|
\ <xsd:documentation>INVALID</xsd:documentation>\n \
|
||||||
|
\ </xsd:annotation>\n </xsd:enumeration>\n \
|
||||||
|
\ <xsd:enumeration value=\"3\">\n <xsd:annotation>\n\
|
||||||
|
\ <xsd:documentation>NOT_PROCESSED</xsd:documentation>\n\
|
||||||
|
\ </xsd:annotation>\n </xsd:enumeration>\n\
|
||||||
|
\t\t\t\t</xsd:restriction>\n\t\t\t</xsd:simpleType>\n\t\t</xsd:schema>\n </wsdl:types>\n\
|
||||||
|
\ <wsdl:message name=\"checkVatRequest\">\n <wsdl:part name=\"parameters\"\
|
||||||
|
\ element=\"tns1:checkVat\">\n </wsdl:part>\n </wsdl:message>\n <wsdl:message\
|
||||||
|
\ name=\"checkVatApproxResponse\">\n <wsdl:part name=\"parameters\" element=\"\
|
||||||
|
tns1:checkVatApproxResponse\">\n </wsdl:part>\n </wsdl:message>\n <wsdl:message\
|
||||||
|
\ name=\"checkVatApproxRequest\">\n <wsdl:part name=\"parameters\" element=\"\
|
||||||
|
tns1:checkVatApprox\">\n </wsdl:part>\n </wsdl:message>\n <wsdl:message\
|
||||||
|
\ name=\"checkVatResponse\">\n <wsdl:part name=\"parameters\" element=\"\
|
||||||
|
tns1:checkVatResponse\">\n </wsdl:part>\n </wsdl:message>\n <wsdl:portType\
|
||||||
|
\ name=\"checkVatPortType\">\n <wsdl:operation name=\"checkVat\">\n \
|
||||||
|
\ <wsdl:input name=\"checkVatRequest\" message=\"impl:checkVatRequest\">\n \
|
||||||
|
\ </wsdl:input>\n <wsdl:output name=\"checkVatResponse\" message=\"impl:checkVatResponse\"\
|
||||||
|
>\n </wsdl:output>\n </wsdl:operation>\n <wsdl:operation name=\"checkVatApprox\"\
|
||||||
|
>\n <wsdl:input name=\"checkVatApproxRequest\" message=\"impl:checkVatApproxRequest\"\
|
||||||
|
>\n </wsdl:input>\n <wsdl:output name=\"checkVatApproxResponse\" message=\"\
|
||||||
|
impl:checkVatApproxResponse\">\n </wsdl:output>\n </wsdl:operation>\n\
|
||||||
|
\ </wsdl:portType>\n <wsdl:binding name=\"checkVatBinding\" type=\"impl:checkVatPortType\"\
|
||||||
|
>\n <wsdlsoap:binding style=\"document\" transport=\"http://schemas.xmlsoap.org/soap/http\"\
|
||||||
|
/>\n <wsdl:operation name=\"checkVat\">\n <wsdlsoap:operation soapAction=\"\
|
||||||
|
\"/>\n <wsdl:input name=\"checkVatRequest\">\n <wsdlsoap:body use=\"\
|
||||||
|
literal\"/>\n </wsdl:input>\n <wsdl:output name=\"checkVatResponse\"\
|
||||||
|
>\n <wsdlsoap:body use=\"literal\"/>\n </wsdl:output>\n </wsdl:operation>\n\
|
||||||
|
\ <wsdl:operation name=\"checkVatApprox\">\n <wsdlsoap:operation soapAction=\"\
|
||||||
|
\"/>\n <wsdl:input name=\"checkVatApproxRequest\">\n <wsdlsoap:body\
|
||||||
|
\ use=\"literal\"/>\n </wsdl:input>\n <wsdl:output name=\"checkVatApproxResponse\"\
|
||||||
|
>\n <wsdlsoap:body use=\"literal\"/>\n </wsdl:output>\n </wsdl:operation>\n\
|
||||||
|
\ </wsdl:binding>\n <wsdl:service name=\"checkVatService\">\n <wsdl:port\
|
||||||
|
\ name=\"checkVatPort\" binding=\"impl:checkVatBinding\">\n <wsdlsoap:address\
|
||||||
|
\ location=\"http://ec.europa.eu/taxation_customs/vies/services/checkVatService\"\
|
||||||
|
/>\n </wsdl:port>\n </wsdl:service>\n</wsdl:definitions>\n"
|
||||||
|
content_type: text/plain
|
||||||
|
method: GET
|
||||||
|
status: 200
|
||||||
|
url: https://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl
|
@ -13,6 +13,8 @@ import looper.models
|
|||||||
|
|
||||||
import users.tests.util as util
|
import users.tests.util as util
|
||||||
|
|
||||||
|
responses_dir = 'subscriptions/tests/_responses/'
|
||||||
|
|
||||||
|
|
||||||
def _write_mail(mail, index=0):
|
def _write_mail(mail, index=0):
|
||||||
email = mail.outbox[index]
|
email = mail.outbox[index]
|
||||||
@ -24,7 +26,20 @@ def _write_mail(mail, index=0):
|
|||||||
f.write(str(content))
|
f.write(str(content))
|
||||||
|
|
||||||
|
|
||||||
|
def responses_from_file(file_name: str, rsps=responses, order_id=None):
|
||||||
|
"""Add a response mock from file, override `order_id` metadata with a given one."""
|
||||||
|
rsps._add_from_file(f'{responses_dir}{file_name}')
|
||||||
|
# Replace metadata's "order_id" hardcoded in the response YAML with current order ID,
|
||||||
|
# because it differs depending on whether this test is run alone or with all the tests.
|
||||||
|
for _ in rsps.registered():
|
||||||
|
if '%5D=payment_intent' in _.url:
|
||||||
|
assert '\"order_id\": \"1' in _.body
|
||||||
|
_.body = _.body.replace('\"order_id\": \"1', f'\"order_id\": \"{order_id}')
|
||||||
|
|
||||||
|
|
||||||
class BaseSubscriptionTestCase(TestCase):
|
class BaseSubscriptionTestCase(TestCase):
|
||||||
|
fixtures = ['gateways']
|
||||||
|
|
||||||
def _get_url_for(self, **filter_params) -> Tuple[str, looper.models.PlanVariation]:
|
def _get_url_for(self, **filter_params) -> Tuple[str, looper.models.PlanVariation]:
|
||||||
plan_variation = looper.models.PlanVariation.objects.active().get(**filter_params)
|
plan_variation = looper.models.PlanVariation.objects.active().get(**filter_params)
|
||||||
return (
|
return (
|
||||||
@ -37,6 +52,8 @@ class BaseSubscriptionTestCase(TestCase):
|
|||||||
|
|
||||||
@factory.django.mute_signals(signals.pre_save, signals.post_save)
|
@factory.django.mute_signals(signals.pre_save, signals.post_save)
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
|
||||||
# Allow requests to Braintree Sandbox
|
# Allow requests to Braintree Sandbox
|
||||||
responses.add_passthru('https://api.sandbox.braintreegateway.com:443/')
|
responses.add_passthru('https://api.sandbox.braintreegateway.com:443/')
|
||||||
|
|
||||||
@ -58,6 +75,13 @@ class BaseSubscriptionTestCase(TestCase):
|
|||||||
self.user = self.customer.user
|
self.user = self.customer.user
|
||||||
self.billing_address = self.customer.billing_address
|
self.billing_address = self.customer.billing_address
|
||||||
|
|
||||||
|
responses._add_from_file(f'{responses_dir}stripe_new_cs_eur.yaml')
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
super().tearDown()
|
||||||
|
responses.stop()
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
def _mock_vies_response(self, is_valid=True, is_broken=False):
|
def _mock_vies_response(self, is_valid=True, is_broken=False):
|
||||||
path = os.path.abspath(__file__)
|
path = os.path.abspath(__file__)
|
||||||
dir_path = os.path.join(os.path.dirname(path), 'vies')
|
dir_path = os.path.join(os.path.dirname(path), 'vies')
|
||||||
@ -97,13 +121,11 @@ class BaseSubscriptionTestCase(TestCase):
|
|||||||
self.assertContains(response, 'id_street_address')
|
self.assertContains(response, 'id_street_address')
|
||||||
self.assertContains(response, 'id_full_name')
|
self.assertContains(response, 'id_full_name')
|
||||||
self.assertContains(response, 'name="gateway" value="stripe"')
|
self.assertContains(response, 'name="gateway" value="stripe"')
|
||||||
|
|
||||||
|
def _assert_pay_via_bank_not_displayed(self, response):
|
||||||
self.assertNotContains(response, 'name="gateway" value="bank"')
|
self.assertNotContains(response, 'name="gateway" value="bank"')
|
||||||
|
|
||||||
def _assert_billing_details_form_with_pay_via_bank_displayed(self, response):
|
def _assert_pay_via_bank_displayed(self, response):
|
||||||
self._assert_continue_to_payment_displayed(response)
|
|
||||||
self.assertContains(response, 'id_street_address')
|
|
||||||
self.assertContains(response, 'id_full_name')
|
|
||||||
self.assertContains(response, 'name="gateway" value="stripe"')
|
|
||||||
self.assertContains(response, 'name="gateway" value="bank"')
|
self.assertContains(response, 'name="gateway" value="bank"')
|
||||||
|
|
||||||
def _assert_pricing_has_been_updated(self, response):
|
def _assert_pricing_has_been_updated(self, response):
|
||||||
|
@ -51,6 +51,9 @@ class TestClockBraintree(BaseSubscriptionTestCase):
|
|||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
|
# Allow requests to Braintree Sandbox
|
||||||
|
responses.add_passthru('https://api.sandbox.braintreegateway.com:443/')
|
||||||
|
|
||||||
self.subscription = self._create_subscription_due_now()
|
self.subscription = self._create_subscription_due_now()
|
||||||
|
|
||||||
@patch(
|
@patch(
|
||||||
|
@ -90,6 +90,7 @@ class JoinView(LoginRequiredMixin, FormView):
|
|||||||
form_kwargs.update(
|
form_kwargs.update(
|
||||||
{
|
{
|
||||||
'request': self.request,
|
'request': self.request,
|
||||||
|
'plan_variation': self.plan_variation,
|
||||||
'instance': self.customer.billing_address,
|
'instance': self.customer.billing_address,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -103,11 +104,6 @@ class JoinView(LoginRequiredMixin, FormView):
|
|||||||
'subscription': self.subscription,
|
'subscription': self.subscription,
|
||||||
}
|
}
|
||||||
|
|
||||||
def gateway_from_form(self, form) -> looper.models.Gateway:
|
|
||||||
"""Use Stripe by default, but allow bank transfer payments for manual plan variations."""
|
|
||||||
self.gateway = looper.models.Gateway.objects.get(name=form.cleaned_data['gateway'])
|
|
||||||
return self.gateway
|
|
||||||
|
|
||||||
def _get_or_create_subscription(
|
def _get_or_create_subscription(
|
||||||
self, gateway: looper.models.Gateway
|
self, gateway: looper.models.Gateway
|
||||||
) -> looper.models.Subscription:
|
) -> looper.models.Subscription:
|
||||||
@ -116,8 +112,8 @@ class JoinView(LoginRequiredMixin, FormView):
|
|||||||
if not subscription:
|
if not subscription:
|
||||||
subscription = looper.models.Subscription(customer=self.customer)
|
subscription = looper.models.Subscription(customer=self.customer)
|
||||||
is_new = True
|
is_new = True
|
||||||
args = [self.customer.pk, gateway]
|
logger_args = [self.customer.pk, gateway]
|
||||||
logger.debug('Creating a new subscription for customer pk=%s, %s', *args)
|
logger.debug('Creating a new subscription for customer pk=%s, %s', *logger_args)
|
||||||
collection_method = self.plan_variation.collection_method
|
collection_method = self.plan_variation.collection_method
|
||||||
supported = set(gateway.provider.supported_collection_methods)
|
supported = set(gateway.provider.supported_collection_methods)
|
||||||
if collection_method not in supported:
|
if collection_method not in supported:
|
||||||
@ -136,6 +132,15 @@ class JoinView(LoginRequiredMixin, FormView):
|
|||||||
subscription.collection_method = collection_method
|
subscription.collection_method = collection_method
|
||||||
subscription.save()
|
subscription.save()
|
||||||
|
|
||||||
|
if gateway.name == 'bank':
|
||||||
|
payment_method = self.customer.payment_method_add(None, gateway)
|
||||||
|
if subscription.payment_method_id != payment_method.pk:
|
||||||
|
logger.info(
|
||||||
|
'Switching subscription pk=%d from payment method pk=%d to pk=%d',
|
||||||
|
*[subscription.pk, subscription.payment_method_id, payment_method.pk],
|
||||||
|
)
|
||||||
|
subscription.switch_payment_method(payment_method)
|
||||||
|
|
||||||
# Configure the team if this is a team plan
|
# Configure the team if this is a team plan
|
||||||
if hasattr(subscription.plan, 'team_properties'):
|
if hasattr(subscription.plan, 'team_properties'):
|
||||||
team_properties = subscription.plan.team_properties
|
team_properties = subscription.plan.team_properties
|
||||||
@ -185,7 +190,7 @@ class JoinView(LoginRequiredMixin, FormView):
|
|||||||
messages.add_message(self.request, messages.INFO, msg)
|
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 = form.cleaned_data['gateway']
|
||||||
price_cents = new_taxable.price.cents
|
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
|
||||||
@ -193,6 +198,8 @@ class JoinView(LoginRequiredMixin, FormView):
|
|||||||
|
|
||||||
order = self._fetch_or_create_order(form, subscription)
|
order = self._fetch_or_create_order(form, subscription)
|
||||||
# Update the order to take into account latest changes
|
# Update the order to take into account latest changes
|
||||||
|
if order.payment_method_id != subscription.payment_method_id:
|
||||||
|
order.switch_payment_method(subscription.payment_method)
|
||||||
order.update()
|
order.update()
|
||||||
# Make sure we are charging what we've displayed
|
# Make sure we are charging what we've displayed
|
||||||
price = looper.money.Money(order.price.currency, price_cents)
|
price = looper.money.Money(order.price.currency, price_cents)
|
||||||
|
@ -15,8 +15,8 @@ from looper.money import Money
|
|||||||
import looper.models
|
import looper.models
|
||||||
|
|
||||||
from looper.tests.factories import create_customer_with_billing_address
|
from looper.tests.factories import create_customer_with_billing_address
|
||||||
from common.tests.factories.users import UserFactory
|
from common.tests.factories.users import UserFactory, OAuthUserInfoFactory
|
||||||
from subscriptions.tests.base import BaseSubscriptionTestCase
|
from subscriptions.tests.base import BaseSubscriptionTestCase, responses_from_file
|
||||||
import subscriptions.tasks
|
import subscriptions.tasks
|
||||||
import users.tasks
|
import users.tasks
|
||||||
import users.tests.util as util
|
import users.tests.util as util
|
||||||
@ -58,7 +58,7 @@ class TestGETJoinView(BaseSubscriptionTestCase):
|
|||||||
response = self.client.get(url, REMOTE_ADDR=EURO_IPV4)
|
response = self.client.get(url, REMOTE_ADDR=EURO_IPV4)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self._assert_billing_details_form_with_pay_via_bank_displayed(response)
|
self._assert_pay_via_bank_displayed(response)
|
||||||
|
|
||||||
def test_get_prefills_full_name_and_billing_email_from_user(self):
|
def test_get_prefills_full_name_and_billing_email_from_user(self):
|
||||||
user = UserFactory(full_name="Jane До", email='jane.doe@example.com')
|
user = UserFactory(full_name="Jane До", email='jane.doe@example.com')
|
||||||
@ -68,6 +68,7 @@ class TestGETJoinView(BaseSubscriptionTestCase):
|
|||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self._assert_total_default_variation_selected_tax_21_eur(response)
|
self._assert_total_default_variation_selected_tax_21_eur(response)
|
||||||
|
self._assert_pay_via_bank_not_displayed(response)
|
||||||
self.assertContains(
|
self.assertContains(
|
||||||
response,
|
response,
|
||||||
'<input type="text" name="full_name" value="Jane До" maxlength="255" placeholder="Your Full Name" class="form-control" required id="id_full_name">',
|
'<input type="text" name="full_name" value="Jane До" maxlength="255" placeholder="Your Full Name" class="form-control" required id="id_full_name">',
|
||||||
@ -183,13 +184,38 @@ class TestGETJoinView(BaseSubscriptionTestCase):
|
|||||||
)
|
)
|
||||||
self._assert_total_default_variation_selected_tax_21_eur(response)
|
self._assert_total_default_variation_selected_tax_21_eur(response)
|
||||||
|
|
||||||
|
def test_plan_variation_matches_detected_currency_eur_non_eea_ip(self):
|
||||||
|
customer = create_customer_with_billing_address()
|
||||||
|
self.client.force_login(customer.user)
|
||||||
|
|
||||||
|
response = self.client.get(self.url, REMOTE_ADDR=SINGAPORE_IPV4)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
# Check that prices are in EUR and there is no tax
|
||||||
|
self._assert_total_default_variation_selected_no_tax_eur(response)
|
||||||
|
|
||||||
|
def test_billing_address_country_takes_precedence_over_geo_ip(self):
|
||||||
|
customer = create_customer_with_billing_address(country='NL')
|
||||||
|
self.client.force_login(customer.user)
|
||||||
|
|
||||||
|
response = self.client.get(self.url, REMOTE_ADDR=SINGAPORE_IPV4)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self._assert_total_default_variation_selected_tax_21_eur(response)
|
||||||
|
|
||||||
|
|
||||||
@freeze_time('2023-05-19 11:41:11')
|
@freeze_time('2023-05-19 11:41:11')
|
||||||
@responses.activate
|
|
||||||
class TestPOSTJoinView(BaseSubscriptionTestCase):
|
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(f'{responses_dir}stripe_create_checkout_session.yaml')
|
|
||||||
|
cs_url = 'https://checkout.stripe.com/c/pay/cs_test_a19GMh7hJXYbh9OhEln16y1M8hfrdu0ySIpjRX4HQJqSVvpe9a9UP30bWW#fidkdWxOYHwnPyd1blpxYHZxWjA0VUpnNzNAMU5EUEcwYW92dWIwclxRMzQ8ZkxsUDRET2dPbTVidnBCNEJTdlBJQTRJYFF2c09BMEFBdlxVT19USGpWSXRSXFJwdm5UQXRpdVw2Rmp%2FZ11NNTU3fHdHUl1JTCcpJ2N3amhWYHdzYHcnP3F3cGApJ2lkfGpwcVF8dWAnPyd2bGtiaWBabHFgaCcpJ2BrZGdpYFVpZGZgbWppYWB3dic%2FcXdwYHgl'
|
||||||
|
cs_id = 'cs_test_a19GMh7hJXYbh9OhEln16y1M8hfrdu0ySIpjRX4HQJqSVvpe9a9UP30bWW'
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
responses._add_from_file(f'{responses_dir}vies_wsdl.yaml')
|
||||||
|
responses._add_from_file(f'{responses_dir}stripe_new_cs_eur.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):
|
||||||
customer = create_customer_with_billing_address(vat_number='', country='DE')
|
customer = create_customer_with_billing_address(vat_number='', country='DE')
|
||||||
@ -204,7 +230,7 @@ class TestPOSTJoinView(BaseSubscriptionTestCase):
|
|||||||
)
|
)
|
||||||
.first()
|
.first()
|
||||||
)
|
)
|
||||||
data = full_billing_address_data
|
data = {**full_billing_address_data, 'gateway': 'stripe'}
|
||||||
url = reverse(
|
url = reverse(
|
||||||
'subscriptions:join-billing-details',
|
'subscriptions:join-billing-details',
|
||||||
kwargs={'plan_variation_id': selected_variation.pk},
|
kwargs={'plan_variation_id': selected_variation.pk},
|
||||||
@ -227,31 +253,27 @@ class TestPOSTJoinView(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')
|
@responses.activate
|
||||||
def test_post_has_correct_price_field_value(self):
|
def test_post_redirects_to_stripe_hosted_checkout(self):
|
||||||
self.client.force_login(self.user)
|
self.client.force_login(self.user)
|
||||||
|
|
||||||
data = required_address_data
|
data = {**required_address_data, 'gateway': 'stripe'}
|
||||||
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'], self.cs_url)
|
||||||
response['Location'],
|
|
||||||
'https://checkout.stripe.com/c/pay/cs_test_a1hoP4Yj4ZmfghAwGoUtWJngVt1XreEVLGAj2'
|
|
||||||
'n7U5o9BlvqhnDimuA07zh#fidkdWxOYHwnPyd1blpxYHZxWjA0VUpnNzNAMU5EUEcwYW92dWIwclxRMzQ8Zkx'
|
|
||||||
'sUDRET2dPbTVidnBCNEJTdlBJQTRJYFF2c09BMEFBdlxVT19USGpWSXRSXFJwdm5UQXRpdVw2Rmp'
|
|
||||||
'%2FZ11NNTU3fHdHUl1JTCcpJ2N3amhWYHdzYHcnP3F3cGApJ2lkfGpwcVF8dWAnPyd2bGtiaWBabH'
|
|
||||||
'FgaCcpJ2BrZGdpYFVpZGZgbWppYWB3dic%2FcXdwYHgl',
|
|
||||||
)
|
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
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):
|
||||||
|
responses._add_from_file(f'{responses_dir}vies_valid.yaml')
|
||||||
self.client.force_login(self.user)
|
self.client.force_login(self.user)
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
**required_address_data,
|
**required_address_data,
|
||||||
'vat_number': 'DE 260543043',
|
'gateway': 'stripe',
|
||||||
'country': 'DE',
|
'country': 'DE',
|
||||||
'postal_code': '11111',
|
'postal_code': '11111',
|
||||||
|
'vat_number': 'DE 260543043',
|
||||||
}
|
}
|
||||||
response = self.client.post(self.url, data, REMOTE_ADDR=EURO_IPV4)
|
response = self.client.post(self.url, data, REMOTE_ADDR=EURO_IPV4)
|
||||||
|
|
||||||
@ -259,6 +281,7 @@ class TestPOSTJoinView(BaseSubscriptionTestCase):
|
|||||||
|
|
||||||
self.user.refresh_from_db()
|
self.user.refresh_from_db()
|
||||||
address = self.user.customer.billing_address
|
address = self.user.customer.billing_address
|
||||||
|
address.refresh_from_db()
|
||||||
self.assertEqual(address.vat_number, 'DE260543043')
|
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')
|
||||||
@ -273,18 +296,8 @@ class TestPOSTJoinView(BaseSubscriptionTestCase):
|
|||||||
# Post the same form again
|
# Post the same form again
|
||||||
response = self.client.post(self.url, data)
|
response = self.client.post(self.url, data)
|
||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
# Follow the redirect to avoid unexpected assertion errors
|
|
||||||
response = self.client.get(response['Location'])
|
|
||||||
|
|
||||||
# Check that we are no longer on the billing details page
|
self.assertEqual(response['Location'], self.cs_url, response['Location'])
|
||||||
self._assert_payment_form_displayed(response)
|
|
||||||
|
|
||||||
# The hidden price field must also be set to a matching amount
|
|
||||||
self.assertContains(
|
|
||||||
response,
|
|
||||||
'<input type="hidden" name="price" value="8.32" class="form-control" id="id_price">',
|
|
||||||
html=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
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):
|
||||||
customer = create_customer_with_billing_address(
|
customer = create_customer_with_billing_address(
|
||||||
@ -304,6 +317,7 @@ class TestPOSTJoinView(BaseSubscriptionTestCase):
|
|||||||
# Post an new address that doesn't require a region
|
# Post an new address that doesn't require a region
|
||||||
data = {
|
data = {
|
||||||
**required_address_data,
|
**required_address_data,
|
||||||
|
'gateway': 'stripe',
|
||||||
'country': 'DE',
|
'country': 'DE',
|
||||||
'postal_code': '11111',
|
'postal_code': '11111',
|
||||||
}
|
}
|
||||||
@ -329,77 +343,41 @@ class TestPOSTJoinView(BaseSubscriptionTestCase):
|
|||||||
user = customer.user
|
user = customer.user
|
||||||
self.client.force_login(user)
|
self.client.force_login(user)
|
||||||
|
|
||||||
data = required_address_data
|
data = {**required_address_data, 'gateway': 'stripe'}
|
||||||
response = self.client.post(url, data, REMOTE_ADDR=EURO_IPV4)
|
response = self.client.post(url, data, REMOTE_ADDR=EURO_IPV4)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 404)
|
self.assertEqual(response.status_code, 302)
|
||||||
|
expected_url, _ = self._get_url_for(currency='EUR', price=10900)
|
||||||
def test_plan_variation_matches_detected_currency_eur_non_eea_ip(self):
|
self.assertEqual(response['Location'], expected_url)
|
||||||
url, _ = self._get_url_for(currency='EUR', price=990)
|
|
||||||
customer = create_customer_with_billing_address()
|
|
||||||
user = customer.user
|
|
||||||
self.client.force_login(user)
|
|
||||||
|
|
||||||
data = required_address_data
|
|
||||||
response = self.client.post(url, data, REMOTE_ADDR=SINGAPORE_IPV4)
|
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
# Check that prices are in EUR and there is no tax
|
|
||||||
self._assert_total_default_variation_selected_no_tax_eur(response)
|
|
||||||
|
|
||||||
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)
|
customer = create_customer_with_billing_address(country='GE')
|
||||||
customer = create_customer_with_billing_address(country='NL')
|
self.client.force_login(customer.user)
|
||||||
user = customer.user
|
|
||||||
self.client.force_login(user)
|
|
||||||
|
|
||||||
data = required_address_data
|
data = {**required_address_data, 'gateway': 'stripe'}
|
||||||
response = self.client.post(url, data, REMOTE_ADDR=SINGAPORE_IPV4)
|
response = self.client.post(self.url, data, REMOTE_ADDR=SINGAPORE_IPV4)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self._assert_total_default_variation_selected_tax_21_eur(response)
|
self._assert_total_default_variation_selected_tax_21_eur(response)
|
||||||
|
|
||||||
def test_invalid_missing_required_fields(self):
|
def test_invalid_missing_required_fields(self):
|
||||||
url, _ = self._get_url_for(currency='EUR', price=990)
|
|
||||||
customer = create_customer_with_billing_address(country='NL')
|
customer = create_customer_with_billing_address(country='NL')
|
||||||
user = customer.user
|
self.client.force_login(customer.user)
|
||||||
self.client.force_login(user)
|
|
||||||
|
|
||||||
data = required_address_data
|
response = self.client.post(self.url, {}, REMOTE_ADDR=EURO_IPV4)
|
||||||
response = self.client.post(url, data, REMOTE_ADDR=EURO_IPV4)
|
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self._assert_total_default_variation_selected_tax_21_eur(response)
|
self._assert_total_default_variation_selected_tax_21_eur(response)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
response.context['form'].errors,
|
response.context['form'].errors,
|
||||||
{
|
{
|
||||||
|
'country': ['This field is required.'],
|
||||||
|
'email': ['This field is required.'],
|
||||||
|
'full_name': ['This field is required.'],
|
||||||
'gateway': ['This field is required.'],
|
'gateway': ['This field is required.'],
|
||||||
'payment_method_nonce': ['This field is required.'],
|
|
||||||
'price': ['This field is required.'],
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_invalid_price_does_not_match_selected_plan_variation(self):
|
|
||||||
url, selected_variation = self._get_url_for(currency='EUR', price=990)
|
|
||||||
customer = create_customer_with_billing_address(country='NL')
|
|
||||||
user = customer.user
|
|
||||||
self.client.force_login(user)
|
|
||||||
|
|
||||||
data = {
|
|
||||||
**required_address_data,
|
|
||||||
'gateway': 'braintree',
|
|
||||||
'payment_method_nonce': 'fake-valid-nonce',
|
|
||||||
'price': '999.09',
|
|
||||||
}
|
|
||||||
response = self.client.post(url, data, REMOTE_ADDR=EURO_IPV4)
|
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
self._assert_total_default_variation_selected_tax_21_eur(response)
|
|
||||||
self.assertEqual(
|
|
||||||
response.context['form'].errors,
|
|
||||||
{'__all__': ['Payment failed: please reload the page and try again']},
|
|
||||||
)
|
|
||||||
|
|
||||||
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)
|
||||||
customer = create_customer_with_billing_address(country='NL')
|
customer = create_customer_with_billing_address(country='NL')
|
||||||
@ -435,8 +413,9 @@ class TestPOSTJoinView(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):
|
||||||
customer = create_customer_with_billing_address(country='NL', full_name='Jane Doe')
|
customer = create_customer_with_billing_address(country='NL')
|
||||||
user = customer.user
|
user = customer.user
|
||||||
|
OAuthUserInfoFactory(user=user, oauth_user_id=554433)
|
||||||
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
|
||||||
@ -448,12 +427,7 @@ class TestPOSTJoinView(BaseSubscriptionTestCase):
|
|||||||
interval_unit='month',
|
interval_unit='month',
|
||||||
plan__name='Manual renewal',
|
plan__name='Manual renewal',
|
||||||
)
|
)
|
||||||
data = {
|
data = {**required_address_data, 'full_name': 'Jane Doe', 'gateway': 'bank'}
|
||||||
**required_address_data,
|
|
||||||
'gateway': 'bank',
|
|
||||||
'payment_method_nonce': 'unused',
|
|
||||||
'price': '14.90',
|
|
||||||
}
|
|
||||||
response = self.client.post(url, data, REMOTE_ADDR=EURO_IPV4)
|
response = self.client.post(url, data, REMOTE_ADDR=EURO_IPV4)
|
||||||
|
|
||||||
self._assert_transactionless_done_page_displayed(response)
|
self._assert_transactionless_done_page_displayed(response)
|
||||||
@ -513,10 +487,17 @@ class TestPOSTJoinView(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,
|
||||||
):
|
):
|
||||||
customer = create_customer_with_billing_address(
|
responses._add_from_file(f'{responses_dir}vies_valid.yaml')
|
||||||
country='ES', full_name='Jane Doe', vat_number='DE260543043'
|
address = {
|
||||||
)
|
**required_address_data,
|
||||||
|
'country': 'ES',
|
||||||
|
'full_name': 'Jane Doe',
|
||||||
|
'postal_code': '11111',
|
||||||
|
'vat_number': 'ES A78374725',
|
||||||
|
}
|
||||||
|
customer = create_customer_with_billing_address(**address)
|
||||||
user = customer.user
|
user = customer.user
|
||||||
|
OAuthUserInfoFactory(user=user, oauth_user_id=554433)
|
||||||
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
|
||||||
@ -528,12 +509,7 @@ class TestPOSTJoinView(BaseSubscriptionTestCase):
|
|||||||
interval_length=3,
|
interval_length=3,
|
||||||
interval_unit='month',
|
interval_unit='month',
|
||||||
)
|
)
|
||||||
data = {
|
data = {'gateway': 'bank', **address}
|
||||||
**required_address_data,
|
|
||||||
'gateway': 'bank',
|
|
||||||
'payment_method_nonce': 'unused',
|
|
||||||
'price': '26.45',
|
|
||||||
}
|
|
||||||
response = self.client.post(url, data, REMOTE_ADDR=EURO_IPV4)
|
response = self.client.post(url, data, REMOTE_ADDR=EURO_IPV4)
|
||||||
|
|
||||||
self._assert_transactionless_done_page_displayed(response)
|
self._assert_transactionless_done_page_displayed(response)
|
||||||
@ -547,6 +523,8 @@ class TestPOSTJoinView(BaseSubscriptionTestCase):
|
|||||||
self.assertEqual(subscription.collection_method, selected_variation.collection_method)
|
self.assertEqual(subscription.collection_method, selected_variation.collection_method)
|
||||||
self.assertEqual(subscription.collection_method, 'manual')
|
self.assertEqual(subscription.collection_method, 'manual')
|
||||||
self.assertEqual(subscription.plan, selected_variation.plan)
|
self.assertEqual(subscription.plan, selected_variation.plan)
|
||||||
|
self.assertEqual(str(subscription.payment_method), 'Bank Transfer')
|
||||||
|
self.assertIsNone(subscription.payment_method.token)
|
||||||
|
|
||||||
order = subscription.latest_order()
|
order = subscription.latest_order()
|
||||||
self.assertEqual(order.status, 'created')
|
self.assertEqual(order.status, 'created')
|
||||||
@ -559,6 +537,8 @@ class TestPOSTJoinView(BaseSubscriptionTestCase):
|
|||||||
self.assertIsNotNone(order.display_number)
|
self.assertIsNotNone(order.display_number)
|
||||||
self.assertIsNotNone(order.vat_number)
|
self.assertIsNotNone(order.vat_number)
|
||||||
self.assertNotEqual(order.display_number, str(order.pk))
|
self.assertNotEqual(order.display_number, str(order.pk))
|
||||||
|
self.assertEqual(str(order.payment_method), 'Bank Transfer')
|
||||||
|
self.assertIsNone(order.payment_method.token)
|
||||||
|
|
||||||
self._assert_bank_transfer_email_is_sent(subscription)
|
self._assert_bank_transfer_email_is_sent(subscription)
|
||||||
self._assert_bank_transfer_email_is_sent_tax_21_eur_reverse_charged(subscription)
|
self._assert_bank_transfer_email_is_sent_tax_21_eur_reverse_charged(subscription)
|
||||||
@ -567,6 +547,7 @@ class TestPOSTJoinView(BaseSubscriptionTestCase):
|
|||||||
response = self.client.get(
|
response = self.client.get(
|
||||||
reverse('subscriptions:manage', kwargs={'subscription_id': subscription.pk})
|
reverse('subscriptions:manage', kwargs={'subscription_id': subscription.pk})
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertNotIn('32.00', response)
|
self.assertNotIn('32.00', response)
|
||||||
self.assertNotIn('21%', response)
|
self.assertNotIn('21%', response)
|
||||||
self.assertNotIn('Inc.', response)
|
self.assertNotIn('Inc.', response)
|
||||||
@ -595,35 +576,44 @@ class TestPOSTJoinView(BaseSubscriptionTestCase):
|
|||||||
'users.signals.tasks.grant_blender_id_role',
|
'users.signals.tasks.grant_blender_id_role',
|
||||||
new=users.tasks.grant_blender_id_role.task_function,
|
new=users.tasks.grant_blender_id_role.task_function,
|
||||||
)
|
)
|
||||||
@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)
|
||||||
customer = 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
|
user = customer.user
|
||||||
|
OAuthUserInfoFactory(user=user, oauth_user_id=554433)
|
||||||
self.client.force_login(user)
|
self.client.force_login(user)
|
||||||
util.mock_blender_id_badger_badger_response(
|
|
||||||
'grant', 'cloud_has_subscription', user.oauth_info.oauth_user_id
|
|
||||||
)
|
|
||||||
util.mock_blender_id_badger_badger_response(
|
|
||||||
'grant', 'cloud_subscriber', user.oauth_info.oauth_user_id
|
|
||||||
)
|
|
||||||
|
|
||||||
data = {
|
data = {**required_address_data, 'gateway': 'stripe'}
|
||||||
**required_address_data,
|
with responses.RequestsMock() as rsps:
|
||||||
'gateway': 'braintree',
|
rsps._add_from_file(f'{responses_dir}stripe_new_cs_eur.yaml')
|
||||||
# fake-three-d-secure-visa-full-authentication-nonce
|
response = self.client.post(url, data, REMOTE_ADDR=EURO_IPV4)
|
||||||
# causes the following error:
|
|
||||||
# Merchant account must match the 3D Secure authorization merchant account: code 91584
|
self.assertEqual(response.status_code, 302)
|
||||||
# TODO(anna): figure out if this is due to our settings or a quirk of the sandbox
|
self.assertEqual(response['Location'], self.cs_url, response['Location'])
|
||||||
'payment_method_nonce': 'fake-valid-nonce',
|
|
||||||
'price': '9.90',
|
# **N.B**: this flow happens in 2 different views separated by a Stripe payment page.
|
||||||
}
|
# Pretend that checkout session was completed and we've returned to the success page with its ID:
|
||||||
response = self.client.post(url, data, REMOTE_ADDR=EURO_IPV4)
|
subscription = user.customer.subscription_set.first()
|
||||||
|
order = subscription.latest_order()
|
||||||
|
url = reverse(
|
||||||
|
'looper:stripe_success',
|
||||||
|
kwargs={'pk': order.pk, 'stripe_session_id': 'CHECKOUT_SESSION_ID'},
|
||||||
|
)
|
||||||
|
url = url.replace('CHECKOUT_SESSION_ID', self.cs_id)
|
||||||
|
with responses.RequestsMock() as rsps:
|
||||||
|
responses_from_file('stripe_get_cs_eur.yaml', order_id=order.pk, rsps=rsps)
|
||||||
|
util.mock_blender_id_badger_badger_response(
|
||||||
|
'grant', 'cloud_has_subscription', user.oauth_info.oauth_user_id, rsps
|
||||||
|
)
|
||||||
|
util.mock_blender_id_badger_badger_response(
|
||||||
|
'grant', 'cloud_subscriber', user.oauth_info.oauth_user_id, rsps
|
||||||
|
)
|
||||||
|
response = self.client.get(url)
|
||||||
|
|
||||||
self._assert_done_page_displayed(response)
|
self._assert_done_page_displayed(response)
|
||||||
|
|
||||||
subscription = user.customer.subscription_set.first()
|
subscription.refresh_from_db()
|
||||||
order = subscription.latest_order()
|
order.refresh_from_db()
|
||||||
self.assertEqual(subscription.status, 'active')
|
self.assertEqual(subscription.status, 'active')
|
||||||
self.assertEqual(subscription.price, Money('EUR', 990))
|
self.assertEqual(subscription.price, Money('EUR', 990))
|
||||||
self.assertEqual(subscription.collection_method, selected_variation.collection_method)
|
self.assertEqual(subscription.collection_method, selected_variation.collection_method)
|
||||||
@ -644,7 +634,6 @@ class TestPOSTJoinView(BaseSubscriptionTestCase):
|
|||||||
'users.signals.tasks.grant_blender_id_role',
|
'users.signals.tasks.grant_blender_id_role',
|
||||||
new=users.tasks.grant_blender_id_role.task_function,
|
new=users.tasks.grant_blender_id_role.task_function,
|
||||||
)
|
)
|
||||||
@responses.activate
|
|
||||||
def test_pay_with_credit_card_creates_order_subscription_active_team(self):
|
def test_pay_with_credit_card_creates_order_subscription_active_team(self):
|
||||||
url, selected_variation = self._get_url_for(
|
url, selected_variation = self._get_url_for(
|
||||||
currency='EUR',
|
currency='EUR',
|
||||||
@ -653,21 +642,35 @@ class TestPOSTJoinView(BaseSubscriptionTestCase):
|
|||||||
)
|
)
|
||||||
customer = 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
|
user = customer.user
|
||||||
|
OAuthUserInfoFactory(user=user, oauth_user_id=554433)
|
||||||
self.client.force_login(user)
|
self.client.force_login(user)
|
||||||
util.mock_blender_id_badger_badger_response(
|
|
||||||
'grant', 'cloud_has_subscription', user.oauth_info.oauth_user_id
|
|
||||||
)
|
|
||||||
util.mock_blender_id_badger_badger_response(
|
|
||||||
'grant', 'cloud_subscriber', user.oauth_info.oauth_user_id
|
|
||||||
)
|
|
||||||
|
|
||||||
data = {
|
data = {**required_address_data, 'gateway': 'stripe'}
|
||||||
**required_address_data,
|
with responses.RequestsMock() as rsps:
|
||||||
'gateway': 'braintree',
|
rsps._add_from_file(f'{responses_dir}stripe_new_cs_eur.yaml')
|
||||||
'payment_method_nonce': 'fake-valid-nonce',
|
response = self.client.post(url, data, REMOTE_ADDR=EURO_IPV4)
|
||||||
'price': '90.00',
|
|
||||||
}
|
self.assertEqual(response.status_code, 302)
|
||||||
response = self.client.post(url, data, REMOTE_ADDR=EURO_IPV4)
|
self.assertEqual(response['Location'], self.cs_url, response['Location'])
|
||||||
|
|
||||||
|
# **N.B**: this flow happens in 2 different views separated by a Stripe payment page.
|
||||||
|
# Pretend that checkout session was completed and we've returned to the success page with its ID:
|
||||||
|
subscription = user.customer.subscription_set.first()
|
||||||
|
order = subscription.latest_order()
|
||||||
|
url = reverse(
|
||||||
|
'looper:stripe_success',
|
||||||
|
kwargs={'pk': order.pk, 'stripe_session_id': 'CHECKOUT_SESSION_ID'},
|
||||||
|
)
|
||||||
|
url = url.replace('CHECKOUT_SESSION_ID', self.cs_id)
|
||||||
|
with responses.RequestsMock() as rsps:
|
||||||
|
responses_from_file('stripe_get_cs_eur.yaml', order_id=order.pk, rsps=rsps)
|
||||||
|
util.mock_blender_id_badger_badger_response(
|
||||||
|
'grant', 'cloud_has_subscription', user.oauth_info.oauth_user_id, rsps
|
||||||
|
)
|
||||||
|
util.mock_blender_id_badger_badger_response(
|
||||||
|
'grant', 'cloud_subscriber', user.oauth_info.oauth_user_id, rsps
|
||||||
|
)
|
||||||
|
response = self.client.get(url)
|
||||||
|
|
||||||
self._assert_done_page_displayed(response)
|
self._assert_done_page_displayed(response)
|
||||||
|
|
||||||
@ -698,19 +701,46 @@ class TestPOSTJoinView(BaseSubscriptionTestCase):
|
|||||||
)
|
)
|
||||||
data = {
|
data = {
|
||||||
**required_address_data,
|
**required_address_data,
|
||||||
'vat_number': 'DE 260543043',
|
'gateway': 'stripe',
|
||||||
'country': 'DE',
|
'country': 'DE',
|
||||||
'postal_code': '11111',
|
'postal_code': '11111',
|
||||||
'gateway': 'braintree',
|
'vat_number': 'DE 260543043',
|
||||||
'payment_method_nonce': 'fake-valid-nonce',
|
|
||||||
# VAT is subtracted from the plan variation price:
|
|
||||||
'price': '12.52',
|
|
||||||
}
|
}
|
||||||
response = self.client.post(url, data, REMOTE_ADDR=EURO_IPV4)
|
# @_recorder.record(file_path=f'{responses_dir}stripe_new_cs_eur.yaml')
|
||||||
|
def _continue_to_payment(): # noqa: E306
|
||||||
|
return self.client.post(url, data, REMOTE_ADDR=EURO_IPV4)
|
||||||
|
|
||||||
|
with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps:
|
||||||
|
# request to wsdl doesn't happen on subsequent calls to the validator,
|
||||||
|
# hence assert_all_requests_are_fired = False.
|
||||||
|
rsps._add_from_file(f'{responses_dir}vies_wsdl.yaml')
|
||||||
|
rsps._add_from_file(f'{responses_dir}vies_valid.yaml')
|
||||||
|
rsps._add_from_file(f'{responses_dir}stripe_new_cs_eur.yaml')
|
||||||
|
response = _continue_to_payment()
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, 302)
|
||||||
|
self.assertEqual(response['Location'], self.cs_url, response['Location'])
|
||||||
|
|
||||||
|
# **N.B**: this flow happens in 2 different views separated by a Stripe payment page.
|
||||||
|
# Pretend that checkout session was completed and we've returned to the success page with its ID:
|
||||||
|
subscription = user.customer.subscription_set.first()
|
||||||
|
order = subscription.latest_order()
|
||||||
|
url = reverse(
|
||||||
|
'looper:stripe_success',
|
||||||
|
kwargs={'pk': order.pk, 'stripe_session_id': 'CHECKOUT_SESSION_ID'},
|
||||||
|
)
|
||||||
|
url = url.replace('CHECKOUT_SESSION_ID', self.cs_id)
|
||||||
|
# @_recorder.record(file_path=f'{responses_dir}stripe_get_cs_eur.yaml')
|
||||||
|
def _back_to_success_url(): # noqa: E306
|
||||||
|
return self.client.get(url)
|
||||||
|
|
||||||
|
with responses.RequestsMock() as rsps:
|
||||||
|
responses_from_file('stripe_get_cs_eur.yaml', order_id=order.pk, rsps=rsps)
|
||||||
|
response = _back_to_success_url()
|
||||||
|
|
||||||
self._assert_done_page_displayed(response)
|
self._assert_done_page_displayed(response)
|
||||||
|
|
||||||
subscription = user.customer.subscription_set.first()
|
subscription.refresh_from_db()
|
||||||
self.assertEqual(subscription.status, 'active')
|
self.assertEqual(subscription.status, 'active')
|
||||||
self.assertEqual(subscription.price, Money('EUR', 1490))
|
self.assertEqual(subscription.price, Money('EUR', 1490))
|
||||||
self.assertEqual(subscription.tax, Money('EUR', 0))
|
self.assertEqual(subscription.tax, Money('EUR', 0))
|
||||||
|
@ -10,7 +10,7 @@ import responses
|
|||||||
# from responses import _recorder
|
# from responses import _recorder
|
||||||
|
|
||||||
from common.tests.factories.users import UserFactory
|
from common.tests.factories.users import UserFactory
|
||||||
from subscriptions.tests.base import BaseSubscriptionTestCase
|
from subscriptions.tests.base import BaseSubscriptionTestCase, responses_from_file
|
||||||
import subscriptions.tasks
|
import subscriptions.tasks
|
||||||
|
|
||||||
responses_dir = 'subscriptions/tests/_responses/'
|
responses_dir = 'subscriptions/tests/_responses/'
|
||||||
@ -104,8 +104,8 @@ class TestSubscriptionSettingsChangePaymentMethod(BaseSubscriptionTestCase):
|
|||||||
url_name = 'subscriptions:payment-method-change'
|
url_name = 'subscriptions:payment-method-change'
|
||||||
success_url_name = 'user-settings-billing'
|
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_new_cs_setup.yaml')
|
||||||
# @_recorder.record(file_path=f'{responses_dir}stripe_retrieve_checkout_session_setup.yaml')
|
# @_recorder.record(file_path=f'{responses_dir}stripe_get_cs_setup.yaml')
|
||||||
def test_can_change_payment_method_from_bank_to_credit_card_with_sca(self):
|
def test_can_change_payment_method_from_bank_to_credit_card_with_sca(self):
|
||||||
bank = Gateway.objects.get(name='bank')
|
bank = Gateway.objects.get(name='bank')
|
||||||
subscription = SubscriptionFactory(
|
subscription = SubscriptionFactory(
|
||||||
@ -120,7 +120,7 @@ class TestSubscriptionSettingsChangePaymentMethod(BaseSubscriptionTestCase):
|
|||||||
|
|
||||||
url = reverse(self.url_name, kwargs={'subscription_id': subscription.pk})
|
url = reverse(self.url_name, kwargs={'subscription_id': subscription.pk})
|
||||||
with responses.RequestsMock() as rsps:
|
with responses.RequestsMock() as rsps:
|
||||||
rsps._add_from_file(f'{responses_dir}stripe_create_checkout_session_setup.yaml')
|
rsps._add_from_file(f'{responses_dir}stripe_new_cs_setup.yaml')
|
||||||
response = self.client.post(url)
|
response = self.client.post(url)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
@ -132,7 +132,7 @@ class TestSubscriptionSettingsChangePaymentMethod(BaseSubscriptionTestCase):
|
|||||||
checkout_session_id = 'cs_test_c1QeSt36UcbmnmrXJnEYZpWakr377WPMfbWLeR9d3ZBYJPWXRUJ3TQ0UG9'
|
checkout_session_id = 'cs_test_c1QeSt36UcbmnmrXJnEYZpWakr377WPMfbWLeR9d3ZBYJPWXRUJ3TQ0UG9'
|
||||||
success_url = url + f'{checkout_session_id}/'
|
success_url = url + f'{checkout_session_id}/'
|
||||||
with responses.RequestsMock() as rsps:
|
with responses.RequestsMock() as rsps:
|
||||||
rsps._add_from_file(f'{responses_dir}stripe_retrieve_checkout_session_setup.yaml')
|
rsps._add_from_file(f'{responses_dir}stripe_get_cs_setup.yaml')
|
||||||
response = self.client.get(success_url)
|
response = self.client.get(success_url)
|
||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
self.assertEqual(response['Location'], f'/subscription/{subscription.pk}/manage/')
|
self.assertEqual(response['Location'], f'/subscription/{subscription.pk}/manage/')
|
||||||
@ -257,8 +257,8 @@ class TestPayExistingOrder(BaseSubscriptionTestCase):
|
|||||||
|
|
||||||
self.assertEqual(response.status_code, 404)
|
self.assertEqual(response.status_code, 404)
|
||||||
|
|
||||||
# @_recorder.record(file_path=f'{responses_dir}stripe_create_checkout_session_usd.yaml')
|
# @_recorder.record(file_path=f'{responses_dir}stripe_new_cs_usd.yaml')
|
||||||
# @_recorder.record(file_path=f'{responses_dir}stripe_retrieve_checkout_session_usd.yaml')
|
# @_recorder.record(file_path=f'{responses_dir}stripe_get_cs_usd.yaml')
|
||||||
@patch(
|
@patch(
|
||||||
# Make sure background task is executed as a normal function
|
# Make sure background task is executed as a normal function
|
||||||
'subscriptions.signals.tasks.send_mail_subscription_status_changed',
|
'subscriptions.signals.tasks.send_mail_subscription_status_changed',
|
||||||
@ -280,7 +280,7 @@ class TestPayExistingOrder(BaseSubscriptionTestCase):
|
|||||||
|
|
||||||
url = reverse(self.url_name, kwargs={'order_id': order.pk})
|
url = reverse(self.url_name, kwargs={'order_id': order.pk})
|
||||||
with responses.RequestsMock() as rsps:
|
with responses.RequestsMock() as rsps:
|
||||||
rsps._add_from_file(f'{responses_dir}stripe_create_checkout_session_usd.yaml')
|
rsps._add_from_file(f'{responses_dir}stripe_new_cs_usd.yaml')
|
||||||
response = self.client.get(url)
|
response = self.client.get(url)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
@ -296,13 +296,7 @@ class TestPayExistingOrder(BaseSubscriptionTestCase):
|
|||||||
)
|
)
|
||||||
url = url.replace('CHECKOUT_SESSION_ID', checkout_session_id)
|
url = url.replace('CHECKOUT_SESSION_ID', checkout_session_id)
|
||||||
with responses.RequestsMock() as rsps:
|
with responses.RequestsMock() as rsps:
|
||||||
rsps._add_from_file(f'{responses_dir}stripe_retrieve_checkout_session_usd.yaml')
|
responses_from_file('stripe_get_cs_usd.yaml', order_id=order.pk, rsps=rsps)
|
||||||
# Replace metadata's "order_id" hardcoded in the response YAML with current order ID,
|
|
||||||
# because it differs depending on whether this test is run alone or with all the tests.
|
|
||||||
for _ in rsps.registered():
|
|
||||||
if '%5D=payment_intent' in _.url:
|
|
||||||
assert '\"order_id\": \"1' in _.body
|
|
||||||
_.body = _.body.replace('\"order_id\": \"1', f'\"order_id\": \"{order.pk}')
|
|
||||||
response = self.client.get(url)
|
response = self.client.get(url)
|
||||||
|
|
||||||
self.assertEqual(order.transaction_set.count(), 1)
|
self.assertEqual(order.transaction_set.count(), 1)
|
||||||
|
Loading…
Reference in New Issue
Block a user