diff --git a/.env.test b/.env.test index e1d7d47cc2..a52a6b10b4 100644 --- a/.env.test +++ b/.env.test @@ -4,6 +4,9 @@ SECRET_TOKEN="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" STRIPE_SECRET_TEST_API_KEY="bogus_key" STRIPE_CUSTOMER="bogus_customer" +STRIPE_ACCOUNT="bogus_account" +STRIPE_CLIENT_ID="bogus_client_id" +STRIPE_PUBLIC_TEST_API_KEY="bogus_stripe_publishable_key" SITE_URL="test.host" diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.3.0/StripeAccount/deauthorize_and_destroy/when_the_Stripe_API_disconnect_fails/destroys_the_record_and_notifies_Bugsnag.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.3.0/StripeAccount/deauthorize_and_destroy/when_the_Stripe_API_disconnect_fails/destroys_the_record_and_notifies_Bugsnag.yml new file mode 100644 index 0000000000..49aef67136 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.3.0/StripeAccount/deauthorize_and_destroy/when_the_Stripe_API_disconnect_fails/destroys_the_record_and_notifies_Bugsnag.yml @@ -0,0 +1,125 @@ +--- +http_interactions: +- request: + method: post + uri: https://connect.stripe.com/oauth/deauthorize + body: + encoding: UTF-8 + string: stripe_user_id=&client_id=bogus_client_id + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.3.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - '{"bindings_version":"10.3.0","lang":"ruby","lang_version":"3.1.4 p223 (2023-03-30)","platform":"x86_64-linux","engine":"ruby","publisher":"stripe","uname":"Linux + version 6.2.0-39-generic (buildd@lcy02-amd64-045) (x86_64-linux-gnu-gcc-11 + (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0, GNU ld (GNU Binutils for Ubuntu) 2.38) + #40~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Thu Nov 16 10:53:04 UTC 2","hostname":"ff-LAT"}' + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 401 + message: Unauthorized + headers: + Server: + - nginx + Date: + - Thu, 21 Dec 2023 10:45:56 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '96' + Connection: + - keep-alive + Cache-Control: + - max-age=0, no-cache, no-store, must-revalidate + Content-Security-Policy: + - report-uri /csp-report?p=%2Foauth%2Fdeauthorize;block-all-mixed-content;default-src + 'none' 'report-sample';base-uri 'none';form-action 'none';style-src 'unsafe-inline';frame-ancestors + 'self';connect-src 'self';img-src 'self' https://b.stripecdn.com + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Expires: + - '0' + Pragma: + - no-cache + Referrer-Policy: + - strict-origin-when-cross-origin + Request-Id: + - req_5I7fo8ZIT4iiOD + Set-Cookie: + - __Host-session=; path=/; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT; + secure; SameSite=None + - __stripe_orig_props=%7B%22referrer%22%3A%22%22%2C%22landing%22%3A%22https%3A%2F%2Fconnect.stripe.com%2Foauth%2Fdeauthorize%22%7D; + domain=stripe.com; path=/; expires=Fri, 20 Dec 2024 10:45:56 GMT; secure; + HttpOnly; SameSite=Lax + - machine_identifier=QgNDMZHR38PElyy9q592gsCebE6wwZpG2yGY42Pr9eBKa8VgTy3D6hYV68qrQ22Ss6M%3D; + domain=stripe.com; path=/; expires=Fri, 20 Dec 2024 10:45:56 GMT; secure; + HttpOnly; SameSite=Lax + - private_machine_identifier=I5FD6ty3P%2Fu3NcPXx%2FQWf0rlSSmmseIhF3Jjh1k0VDaE0r9pST2zC0QF5zOrj9mL8xU%3D; + domain=stripe.com; path=/; expires=Fri, 20 Dec 2024 10:45:56 GMT; secure; + HttpOnly; SameSite=None + - site-auth=; domain=stripe.com; path=/; max-age=0; expires=Thu, 01 Jan 1970 + 00:00:00 GMT; secure + - stripe.csrf=3pBovX-WeRInaYekIenxBP4HZPJUKPIapUKWzkq61RZGXY_L4XNwm0d3kPOaWycuCWRVZPILZHIl0E1ajDrp9jw-AYTZVJw3W5vZuecF9T6yl15939IAMLnS4TPsJz7sL-g2uJ-hMA%3D%3D; + domain=stripe.com; path=/; secure; HttpOnly; SameSite=None + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + - max-age=63072000; includeSubDomains; preload + Stripe-Kill-Route: + - "[]" + Stripe-Parent-Id: + - '0000000000000000' + Stripe-Span-Id: + - c9754f0ed47ef010 + Www-Authenticate: + - Bearer realm="Stripe" + X-Apiori-Intentional-Latency: + - 0s + X-Apiori-Reqid: + - dub1DIXA8nWJL2tSW8r7Vtw + X-Apiori-Server-Duration-Ms: + - '118' + X-Apiori-Upstream-Duration: + - 118.447951ms + X-Apiori-Upstream-Name: + - manage-srv + X-Apiori-Upstream-Region: + - northwest + X-Content-Type-Options: + - nosniff + X-Envoy-Attempt-Count: + - '1' + X-Envoy-Upstream-Service-Time: + - '235' + X-Robots-Tag: + - none + X-Stripe-Bg-Intended-Route-Color: + - blue + X-Stripe-C-Cost: + - '2' + X-Stripe-Client-Envoy-Start-Time-Us: + - '1703155556322583' + X-Stripe-Rpc-C-Cost-Report: + - Cg0IARIJY2VsbF8wMDA3Cg8IARILZ2xvYmFsX2NlbGw= + X-Stripe-Server-Envoy-Start-Time-Us: + - '1703155556323654' + X-Stripe-Server-Envoy-Upstream-Service-Time-Ms: + - '115' + body: + encoding: UTF-8 + string: |- + { + "error": "invalid_client", + "error_description": "No such application: 'bogus_client_id'" + } + recorded_at: Thu, 21 Dec 2023 10:45:56 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Stripe-v10.3.0/StripeAccount/deauthorize_and_destroy/when_the_Stripe_API_disconnect_succeeds/destroys_the_record.yml b/spec/fixtures/vcr_cassettes/Stripe-v10.3.0/StripeAccount/deauthorize_and_destroy/when_the_Stripe_API_disconnect_succeeds/destroys_the_record.yml new file mode 100644 index 0000000000..03de8ad4e2 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Stripe-v10.3.0/StripeAccount/deauthorize_and_destroy/when_the_Stripe_API_disconnect_succeeds/destroys_the_record.yml @@ -0,0 +1,326 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.stripe.com/v1/accounts + body: + encoding: UTF-8 + string: type=standard&country=AU&email=jumping.jack%40example.com + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.3.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - '{"bindings_version":"10.3.0","lang":"ruby","lang_version":"3.1.4 p223 (2023-03-30)","platform":"x86_64-linux","engine":"ruby","publisher":"stripe","uname":"Linux + version 6.2.0-39-generic (buildd@lcy02-amd64-045) (x86_64-linux-gnu-gcc-11 + (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0, GNU ld (GNU Binutils for Ubuntu) 2.38) + #40~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Thu Nov 16 10:53:04 UTC 2","hostname":"ff-LAT"}' + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Thu, 21 Dec 2023 10:45:58 GMT + Content-Type: + - application/json + Content-Length: + - '2916' + Connection: + - keep-alive + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET,HEAD,PUT,PATCH,POST,DELETE + Access-Control-Allow-Origin: + - "*" + Access-Control-Expose-Headers: + - Request-Id, Stripe-Manage-Version, Stripe-Should-Retry, X-Stripe-External-Auth-Required, + X-Stripe-Privileged-Session-Required + Access-Control-Max-Age: + - '300' + Cache-Control: + - no-cache, no-store + Content-Security-Policy: + - report-uri https://q.stripe.com/csp-report?p=v1%2Faccounts; block-all-mixed-content; + default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; + img-src 'self'; script-src 'self' 'report-sample'; style-src 'self' + Idempotency-Key: + - 4faf7619-9abc-418c-82ab-d4c1b0b2f5ca + Original-Request: + - req_akFJPnmmxbaXxd + Request-Id: + - req_akFJPnmmxbaXxd + Stripe-Should-Retry: + - 'false' + Stripe-Version: + - '2023-10-16' + Vary: + - Origin + X-Stripe-Routing-Context-Priority-Tier: + - api-testmode + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + body: + encoding: UTF-8 + string: |- + { + "id": "acct_1OPjlJQKtA57Vnxb", + "object": "account", + "business_profile": { + "mcc": null, + "name": null, + "product_description": null, + "support_address": null, + "support_email": null, + "support_phone": null, + "support_url": null, + "url": null + }, + "business_type": null, + "capabilities": {}, + "charges_enabled": false, + "controller": { + "is_controller": true, + "type": "application" + }, + "country": "AU", + "created": 1703155558, + "default_currency": "aud", + "details_submitted": false, + "email": "jumping.jack@example.com", + "external_accounts": { + "object": "list", + "data": [], + "has_more": false, + "total_count": 0, + "url": "/v1/accounts/acct_1OPjlJQKtA57Vnxb/external_accounts" + }, + "future_requirements": { + "alternatives": [], + "current_deadline": null, + "currently_due": [], + "disabled_reason": null, + "errors": [], + "eventually_due": [], + "past_due": [], + "pending_verification": [] + }, + "metadata": {}, + "payouts_enabled": false, + "requirements": { + "alternatives": [], + "current_deadline": null, + "currently_due": [ + "business_profile.product_description", + "business_profile.support_phone", + "business_profile.url", + "external_account", + "tos_acceptance.date", + "tos_acceptance.ip" + ], + "disabled_reason": "requirements.past_due", + "errors": [], + "eventually_due": [ + "business_profile.product_description", + "business_profile.support_phone", + "business_profile.url", + "external_account", + "tos_acceptance.date", + "tos_acceptance.ip" + ], + "past_due": [ + "external_account", + "tos_acceptance.date", + "tos_acceptance.ip" + ], + "pending_verification": [] + }, + "settings": { + "bacs_debit_payments": { + "display_name": null, + "service_user_number": null + }, + "branding": { + "icon": null, + "logo": null, + "primary_color": null, + "secondary_color": null + }, + "card_issuing": { + "tos_acceptance": { + "date": null, + "ip": null + } + }, + "card_payments": { + "decline_on": { + "avs_failure": false, + "cvc_failure": false + }, + "statement_descriptor_prefix": null, + "statement_descriptor_prefix_kana": null, + "statement_descriptor_prefix_kanji": null + }, + "dashboard": { + "display_name": null, + "timezone": "Etc/UTC" + }, + "payments": { + "statement_descriptor": null, + "statement_descriptor_kana": null, + "statement_descriptor_kanji": null + }, + "payouts": { + "debit_negative_balances": true, + "schedule": { + "delay_days": 2, + "interval": "daily" + }, + "statement_descriptor": null + }, + "sepa_debit_payments": {} + }, + "tos_acceptance": { + "date": null, + "ip": null, + "user_agent": null + }, + "type": "standard" + } + recorded_at: Thu, 21 Dec 2023 10:45:58 GMT +- request: + method: post + uri: https://connect.stripe.com/oauth/deauthorize + body: + encoding: UTF-8 + string: stripe_user_id=acct_1OPjlJQKtA57Vnxb&client_id=ca_MzG1xs6tZFDztUlak7uFxoUM36G6307W + headers: + User-Agent: + - Stripe/v1 RubyBindings/10.3.0 + Authorization: + - Bearer + Content-Type: + - application/x-www-form-urlencoded + X-Stripe-Client-Telemetry: + - '{"last_request_metrics":{"request_id":"req_akFJPnmmxbaXxd","request_duration_ms":1906}}' + Stripe-Version: + - '2023-10-16' + X-Stripe-Client-User-Agent: + - '{"bindings_version":"10.3.0","lang":"ruby","lang_version":"3.1.4 p223 (2023-03-30)","platform":"x86_64-linux","engine":"ruby","publisher":"stripe","uname":"Linux + version 6.2.0-39-generic (buildd@lcy02-amd64-045) (x86_64-linux-gnu-gcc-11 + (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0, GNU ld (GNU Binutils for Ubuntu) 2.38) + #40~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Thu Nov 16 10:53:04 UTC 2","hostname":"ff-LAT"}' + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Thu, 21 Dec 2023 10:45:59 GMT + Content-Type: + - application/json + Content-Length: + - '47' + Connection: + - keep-alive + Cache-Control: + - max-age=0, no-cache, no-store, must-revalidate + Content-Security-Policy: + - report-uri /csp-report?p=%2Foauth%2Fdeauthorize;block-all-mixed-content;default-src + 'none' 'report-sample';base-uri 'none';form-action 'none';style-src 'unsafe-inline';frame-ancestors + 'self';connect-src 'self';img-src 'self' https://b.stripecdn.com + Cross-Origin-Opener-Policy-Report-Only: + - same-origin; report-to=https://q.stripe.com/coop-report + Expires: + - '0' + Pragma: + - no-cache + Referrer-Policy: + - strict-origin-when-cross-origin + Request-Id: + - req_GI4KuC138PCJAi + Set-Cookie: + - __Host-session=; path=/; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT; + secure; SameSite=None + - __stripe_orig_props=%7B%22referrer%22%3A%22%22%2C%22landing%22%3A%22https%3A%2F%2Fconnect.stripe.com%2Foauth%2Fdeauthorize%22%7D; + domain=stripe.com; path=/; expires=Fri, 20 Dec 2024 10:45:59 GMT; secure; + HttpOnly; SameSite=Lax + - machine_identifier=HKDeDB5hKqj4clQzAFDSsv1CYfwTn24qrrs%2BEPZII8GCVRJ%2FXlImUJxiAZyJEtIZ%2Fmw%3D; + domain=stripe.com; path=/; expires=Fri, 20 Dec 2024 10:45:59 GMT; secure; + HttpOnly; SameSite=Lax + - private_machine_identifier=MtWBDVmUQlK99LHORT57Q4I%2BFQVz1NgOm8YwuEQ1hcLBZg%2B0065RWGEpjtHqd73XO9g%3D; + domain=stripe.com; path=/; expires=Fri, 20 Dec 2024 10:45:59 GMT; secure; + HttpOnly; SameSite=None + - site-auth=; domain=stripe.com; path=/; max-age=0; expires=Thu, 01 Jan 1970 + 00:00:00 GMT; secure + - stripe.csrf=2JHhBnTu9M9rT2X5ddKYKiGrldjy1_TlDZkBH0QYBxLbuX5pF9u2LvZHkuwLr3S96XXp0ZHdgqJRzdhyPTMYmDw-AYTZVJziZaudEZeavUdznjQfqSpNTprhJdapun0wOEWmMxs7zQ%3D%3D; + domain=stripe.com; path=/; secure; HttpOnly; SameSite=None + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + - max-age=63072000; includeSubDomains; preload + Stripe-Kill-Route: + - "[]" + Stripe-Parent-Id: + - '0000000000000000' + Stripe-Span-Id: + - 7c70338af8eb59d9 + X-Apiori-Intentional-Latency: + - 0s + X-Apiori-Reqid: + - dub1DIXA981iCMFaeUWou2O + X-Apiori-Server-Duration-Ms: + - '226' + X-Apiori-Upstream-Duration: + - 225.729973ms + X-Apiori-Upstream-Name: + - manage-srv + X-Apiori-Upstream-Region: + - northwest + X-Content-Type-Options: + - nosniff + X-Envoy-Attempt-Count: + - '1' + X-Envoy-Upstream-Service-Time: + - '347' + X-Robots-Tag: + - none + X-Stripe-Bg-Intended-Route-Color: + - blue + X-Stripe-C-Cost: + - '22' + X-Stripe-Client-Envoy-Start-Time-Us: + - '1703155559193296' + X-Stripe-Rpc-C-Cost-Report: + - Cg0IFBIJY2VsbF8wMDA3Cg8IAhILZ2xvYmFsX2NlbGw= + X-Stripe-Server-Envoy-Start-Time-Us: + - '1703155559194276' + X-Stripe-Server-Envoy-Upstream-Service-Time-Ms: + - '223' + Stripe-Action-Id: + - dub1DIXA981iCMFaeUWou2O + body: + encoding: UTF-8 + string: |- + { + "stripe_user_id": "acct_1OPjlJQKtA57Vnxb" + } + recorded_at: Thu, 21 Dec 2023 10:45:59 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/models/stripe_account_spec.rb b/spec/models/stripe_account_spec.rb index f798cbc56a..d890152bb7 100644 --- a/spec/models/stripe_account_spec.rb +++ b/spec/models/stripe_account_spec.rb @@ -4,45 +4,58 @@ require 'spec_helper' require 'stripe/oauth' describe StripeAccount do - describe "deauthorize_and_destroy" do + describe "deauthorize_and_destroy", :vcr, :stripe_version do let!(:enterprise) { create(:enterprise) } let!(:enterprise2) { create(:enterprise) } - let(:client_id) { 'ca_abc123' } - let(:stripe_user_id) { 'acct_abc123' } + let(:client_id) { ENV.fetch('STRIPE_CLIENT_ID', nil) } + let(:stripe_user_id) { ENV.fetch('STRIPE_ACCOUNT', nil) } + let(:stripe_publishable_key) { ENV.fetch('STRIPE_PUBLIC_TEST_API_KEY', nil) } + let(:secret) { ENV.fetch('STRIPE_SECRET_TEST_API_KEY', nil) } + let!(:stripe_account) { create(:stripe_account, enterprise:, stripe_user_id:) } before do - Stripe.api_key = "sk_test_12345" - Stripe.client_id = client_id + Stripe.api_key = secret end context "when the Stripe API disconnect fails" do - before do - stub_request(:post, "https://connect.stripe.com/oauth/deauthorize"). - with(body: { "client_id" => client_id, "stripe_user_id" => stripe_user_id }). - to_return(status: 400, body: JSON.generate(error: 'invalid_grant', - error_description: "Some Message")) - end + before { Stripe.client_id = "bogus_client_id" } it "destroys the record and notifies Bugsnag" do - expect(Bugsnag).to receive(:notify) - stripe_account.deauthorize_and_destroy - expect(StripeAccount.all).to_not include(stripe_account) + # returns status 401 + expect(Bugsnag).to receive(:notify) # and receives Bugsnag notification + expect { + stripe_account.deauthorize_and_destroy + }.to change( + StripeAccount.where(stripe_user_id:), :count + ).from(1).to(0) end end context "when the Stripe API disconnect succeeds" do + let!(:connected_account) do + Stripe::Account.create({ + type: 'standard', + country: 'AU', + email: 'jumping.jack@example.com' + }) + end + before do - stub_request(:post, "https://connect.stripe.com/oauth/deauthorize"). - with(body: { "client_id" => client_id, "stripe_user_id" => stripe_user_id }). - to_return(status: 200, body: JSON.generate(stripe_user_id:)) + Stripe.client_id = client_id + stripe_account.update!(stripe_publishable_key:, stripe_user_id: connected_account.id) end it "destroys the record" do - stripe_account.deauthorize_and_destroy - expect(StripeAccount.all).not_to include(stripe_account) + # returns status 200 + expect(Bugsnag).to_not receive(:notify) # and does not receive Bugsnag notification + expect { + stripe_account.deauthorize_and_destroy + }.to change( + StripeAccount.where(stripe_user_id: connected_account.id), :count + ).from(1).to(0) end end diff --git a/spec/support/vcr_setup.rb b/spec/support/vcr_setup.rb index 7cb9d1b63d..2f944fb235 100644 --- a/spec/support/vcr_setup.rb +++ b/spec/support/vcr_setup.rb @@ -9,5 +9,6 @@ VCR.configure do |config| config.configure_rspec_metadata! config.filter_sensitive_data('') { ENV.fetch('STRIPE_SECRET_TEST_API_KEY', nil) } config.filter_sensitive_data('') { ENV.fetch('STRIPE_CUSTOMER', nil) } + config.filter_sensitive_data('') { ENV.fetch('STRIPE_ACCOUNT', nil) } config.ignore_hosts('localhost', '127.0.0.1', '0.0.0.0', 'api.knapsackpro.com') end