From 6bdcd25aa4fbfe638492f58b6351ddd9d1c43940 Mon Sep 17 00:00:00 2001 From: Simon Hamp Date: Fri, 10 Apr 2026 01:24:50 +0100 Subject: [PATCH] Remove unsupported Stripe Connect countries and add graceful error handling Remove IN, TW, KR, NG, and NA from the supported countries list as Stripe does not support Express Connect accounts for these countries from US-based platforms (cross-border restrictions or missing card_payments capability). Also adds a try/catch around account creation to handle any future Stripe rejections gracefully instead of showing a 500 error. Closes #45 Co-Authored-By: Claude Opus 4.6 --- .../DeveloperOnboardingController.php | 14 ++++- app/Support/StripeConnectCountries.php | 10 ---- tests/Feature/DeveloperTermsTest.php | 53 +++++++++++++++++++ tests/Unit/StripeConnectCountriesTest.php | 39 +++++++++++++- 4 files changed, 103 insertions(+), 13 deletions(-) diff --git a/app/Http/Controllers/DeveloperOnboardingController.php b/app/Http/Controllers/DeveloperOnboardingController.php index 57ea3e52..994414ef 100644 --- a/app/Http/Controllers/DeveloperOnboardingController.php +++ b/app/Http/Controllers/DeveloperOnboardingController.php @@ -7,7 +7,9 @@ use App\Support\StripeConnectCountries; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; +use Illuminate\Support\Facades\Log; use Illuminate\Validation\Rule; +use Stripe\Exception\InvalidRequestException; class DeveloperOnboardingController extends Controller { @@ -38,7 +40,17 @@ public function start(Request $request): RedirectResponse $developerAccount = $user->developerAccount; if (! $developerAccount) { - $developerAccount = $this->stripeConnectService->createConnectAccount($user, $country, $payoutCurrency); + try { + $developerAccount = $this->stripeConnectService->createConnectAccount($user, $country, $payoutCurrency); + } catch (InvalidRequestException $e) { + Log::warning('Stripe Connect account creation failed', [ + 'user_id' => $user->id, + 'country' => $country, + 'error' => $e->getMessage(), + ]); + + return back()->withErrors(['country' => 'Stripe does not currently support developer accounts in your selected country. Please choose a different country or contact support for assistance.']); + } } else { $developerAccount->update([ 'country' => $country, diff --git a/app/Support/StripeConnectCountries.php b/app/Support/StripeConnectCountries.php index 824029f1..40e9bd5d 100644 --- a/app/Support/StripeConnectCountries.php +++ b/app/Support/StripeConnectCountries.php @@ -56,7 +56,6 @@ class StripeConnectCountries 'ID' => ['name' => 'Indonesia', 'flag' => "\u{1F1EE}\u{1F1E9}", 'default_currency' => 'IDR', 'currencies' => ['IDR']], 'IE' => ['name' => 'Ireland', 'flag' => "\u{1F1EE}\u{1F1EA}", 'default_currency' => 'EUR', 'currencies' => ['EUR', 'GBP', 'USD', 'CHF', 'DKK', 'NOK', 'SEK']], 'IL' => ['name' => 'Israel', 'flag' => "\u{1F1EE}\u{1F1F1}", 'default_currency' => 'ILS', 'currencies' => ['ILS']], - 'IN' => ['name' => 'India', 'flag' => "\u{1F1EE}\u{1F1F3}", 'default_currency' => 'INR', 'currencies' => ['INR']], 'IS' => ['name' => 'Iceland', 'flag' => "\u{1F1EE}\u{1F1F8}", 'default_currency' => 'ISK', 'currencies' => ['ISK', 'EUR', 'GBP', 'USD', 'CHF', 'DKK', 'NOK', 'SEK']], 'IT' => ['name' => 'Italy', 'flag' => "\u{1F1EE}\u{1F1F9}", 'default_currency' => 'EUR', 'currencies' => ['EUR', 'GBP', 'USD', 'CHF', 'DKK', 'NOK', 'SEK']], 'JM' => ['name' => 'Jamaica', 'flag' => "\u{1F1EF}\u{1F1F2}", 'default_currency' => 'JMD', 'currencies' => ['JMD']], @@ -64,7 +63,6 @@ class StripeConnectCountries 'JP' => ['name' => 'Japan', 'flag' => "\u{1F1EF}\u{1F1F5}", 'default_currency' => 'JPY', 'currencies' => ['JPY']], 'KE' => ['name' => 'Kenya', 'flag' => "\u{1F1F0}\u{1F1EA}", 'default_currency' => 'KES', 'currencies' => ['KES']], 'KH' => ['name' => 'Cambodia', 'flag' => "\u{1F1F0}\u{1F1ED}", 'default_currency' => 'USD', 'currencies' => ['USD']], - 'KR' => ['name' => 'South Korea', 'flag' => "\u{1F1F0}\u{1F1F7}", 'default_currency' => 'KRW', 'currencies' => ['KRW']], 'KW' => ['name' => 'Kuwait', 'flag' => "\u{1F1F0}\u{1F1FC}", 'default_currency' => 'KWD', 'currencies' => ['KWD']], 'LC' => ['name' => 'St. Lucia', 'flag' => "\u{1F1F1}\u{1F1E8}", 'default_currency' => 'XCD', 'currencies' => ['XCD']], 'LI' => ['name' => 'Liechtenstein', 'flag' => "\u{1F1F1}\u{1F1EE}", 'default_currency' => 'CHF', 'currencies' => ['CHF', 'EUR', 'GBP', 'USD', 'DKK', 'NOK', 'SEK']], @@ -83,8 +81,6 @@ class StripeConnectCountries 'MU' => ['name' => 'Mauritius', 'flag' => "\u{1F1F2}\u{1F1FA}", 'default_currency' => 'MUR', 'currencies' => ['MUR']], 'MX' => ['name' => 'Mexico', 'flag' => "\u{1F1F2}\u{1F1FD}", 'default_currency' => 'MXN', 'currencies' => ['MXN']], 'MY' => ['name' => 'Malaysia', 'flag' => "\u{1F1F2}\u{1F1FE}", 'default_currency' => 'MYR', 'currencies' => ['MYR']], - 'NA' => ['name' => 'Namibia', 'flag' => "\u{1F1F3}\u{1F1E6}", 'default_currency' => 'NAD', 'currencies' => ['NAD']], - 'NG' => ['name' => 'Nigeria', 'flag' => "\u{1F1F3}\u{1F1EC}", 'default_currency' => 'NGN', 'currencies' => ['NGN']], 'NL' => ['name' => 'Netherlands', 'flag' => "\u{1F1F3}\u{1F1F1}", 'default_currency' => 'EUR', 'currencies' => ['EUR', 'GBP', 'USD', 'CHF', 'DKK', 'NOK', 'SEK']], 'NO' => ['name' => 'Norway', 'flag' => "\u{1F1F3}\u{1F1F4}", 'default_currency' => 'NOK', 'currencies' => ['NOK', 'EUR', 'GBP', 'USD', 'CHF', 'DKK', 'SEK']], 'NZ' => ['name' => 'New Zealand', 'flag' => "\u{1F1F3}\u{1F1FF}", 'default_currency' => 'NZD', 'currencies' => ['NZD']], @@ -111,7 +107,6 @@ class StripeConnectCountries 'TN' => ['name' => 'Tunisia', 'flag' => "\u{1F1F9}\u{1F1F3}", 'default_currency' => 'TND', 'currencies' => ['TND']], 'TR' => ['name' => "T\u{00FC}rkiye", 'flag' => "\u{1F1F9}\u{1F1F7}", 'default_currency' => 'TRY', 'currencies' => ['TRY']], 'TT' => ['name' => 'Trinidad & Tobago', 'flag' => "\u{1F1F9}\u{1F1F9}", 'default_currency' => 'TTD', 'currencies' => ['TTD']], - 'TW' => ['name' => 'Taiwan', 'flag' => "\u{1F1F9}\u{1F1FC}", 'default_currency' => 'TWD', 'currencies' => ['TWD']], 'TZ' => ['name' => 'Tanzania', 'flag' => "\u{1F1F9}\u{1F1FF}", 'default_currency' => 'TZS', 'currencies' => ['TZS']], 'US' => ['name' => 'United States', 'flag' => "\u{1F1FA}\u{1F1F8}", 'default_currency' => 'USD', 'currencies' => ['USD']], 'UY' => ['name' => 'Uruguay', 'flag' => "\u{1F1FA}\u{1F1FE}", 'default_currency' => 'UYU', 'currencies' => ['UYU']], @@ -155,13 +150,11 @@ class StripeConnectCountries 'HUF' => 'Hungarian Forint', 'IDR' => 'Indonesian Rupiah', 'ILS' => 'Israeli Shekel', - 'INR' => 'Indian Rupee', 'ISK' => 'Icelandic Krona', 'JMD' => 'Jamaican Dollar', 'JOD' => 'Jordanian Dinar', 'JPY' => 'Japanese Yen', 'KES' => 'Kenyan Shilling', - 'KRW' => 'South Korean Won', 'KWD' => 'Kuwaiti Dinar', 'LKR' => 'Sri Lankan Rupee', 'MAD' => 'Moroccan Dirham', @@ -173,8 +166,6 @@ class StripeConnectCountries 'MUR' => 'Mauritian Rupee', 'MXN' => 'Mexican Peso', 'MYR' => 'Malaysian Ringgit', - 'NAD' => 'Namibian Dollar', - 'NGN' => 'Nigerian Naira', 'NOK' => 'Norwegian Krone', 'NZD' => 'New Zealand Dollar', 'OMR' => 'Omani Rial', @@ -194,7 +185,6 @@ class StripeConnectCountries 'TND' => 'Tunisian Dinar', 'TRY' => 'Turkish Lira', 'TTD' => 'Trinidad & Tobago Dollar', - 'TWD' => 'New Taiwan Dollar', 'TZS' => 'Tanzanian Shilling', 'USD' => 'US Dollar', 'UYU' => 'Uruguayan Peso', diff --git a/tests/Feature/DeveloperTermsTest.php b/tests/Feature/DeveloperTermsTest.php index f7986f2e..1c52422a 100644 --- a/tests/Feature/DeveloperTermsTest.php +++ b/tests/Feature/DeveloperTermsTest.php @@ -13,6 +13,7 @@ use Laravel\Pennant\Feature; use Livewire\Livewire; use Mockery; +use Stripe\Exception\InvalidRequestException; use Tests\TestCase; class DeveloperTermsTest extends TestCase @@ -291,6 +292,58 @@ public function onboarding_start_requires_payout_currency(): void $response->assertSessionHasErrors('payout_currency'); } + /** @test */ + public function onboarding_start_rejects_india_as_unsupported_country(): void + { + $user = User::factory()->create(); + + $response = $this->actingAs($user) + ->post(route('customer.developer.onboarding.start'), [ + 'accepted_plugin_terms' => '1', + 'country' => 'IN', + 'payout_currency' => 'INR', + ]); + + $response->assertSessionHasErrors('country'); + } + + /** @test */ + public function onboarding_start_rejects_taiwan_as_unsupported_country(): void + { + $user = User::factory()->create(); + + $response = $this->actingAs($user) + ->post(route('customer.developer.onboarding.start'), [ + 'accepted_plugin_terms' => '1', + 'country' => 'TW', + 'payout_currency' => 'TWD', + ]); + + $response->assertSessionHasErrors('country'); + } + + /** @test */ + public function onboarding_start_handles_stripe_error_gracefully(): void + { + $user = User::factory()->create(); + + $mockService = Mockery::mock(StripeConnectService::class); + $mockService->shouldReceive('createConnectAccount') + ->once() + ->andThrow(new InvalidRequestException('Connected accounts in XX cannot be created by platforms in US.')); + + $this->app->instance(StripeConnectService::class, $mockService); + + $response = $this->actingAs($user) + ->post(route('customer.developer.onboarding.start'), [ + 'accepted_plugin_terms' => '1', + 'country' => 'US', + 'payout_currency' => 'USD', + ]); + + $response->assertSessionHasErrors('country'); + } + /** @test */ public function onboarding_start_rejects_invalid_currency_for_country(): void { diff --git a/tests/Unit/StripeConnectCountriesTest.php b/tests/Unit/StripeConnectCountriesTest.php index a8d1fb30..8bd9ebb6 100644 --- a/tests/Unit/StripeConnectCountriesTest.php +++ b/tests/Unit/StripeConnectCountriesTest.php @@ -12,7 +12,7 @@ public function all_returns_all_supported_countries(): void { $countries = StripeConnectCountries::all(); - $this->assertCount(108, $countries); + $this->assertCount(103, $countries); $this->assertArrayHasKey('US', $countries); $this->assertArrayHasKey('GB', $countries); $this->assertArrayHasKey('DE', $countries); @@ -98,7 +98,7 @@ public function supported_country_codes_returns_array_of_codes(): void $this->assertContains('US', $codes); $this->assertContains('GB', $codes); - $this->assertCount(108, $codes); + $this->assertCount(103, $codes); } /** @test */ @@ -128,6 +128,41 @@ public function every_used_currency_has_a_name(): void } } + /** @test */ + public function india_is_not_in_supported_countries(): void + { + $this->assertFalse(StripeConnectCountries::isSupported('IN')); + $this->assertArrayNotHasKey('IN', StripeConnectCountries::all()); + } + + /** @test */ + public function taiwan_is_not_in_supported_countries(): void + { + $this->assertFalse(StripeConnectCountries::isSupported('TW')); + $this->assertArrayNotHasKey('TW', StripeConnectCountries::all()); + } + + /** @test */ + public function south_korea_is_not_in_supported_countries(): void + { + $this->assertFalse(StripeConnectCountries::isSupported('KR')); + $this->assertArrayNotHasKey('KR', StripeConnectCountries::all()); + } + + /** @test */ + public function nigeria_is_not_in_supported_countries(): void + { + $this->assertFalse(StripeConnectCountries::isSupported('NG')); + $this->assertArrayNotHasKey('NG', StripeConnectCountries::all()); + } + + /** @test */ + public function namibia_is_not_in_supported_countries(): void + { + $this->assertFalse(StripeConnectCountries::isSupported('NA')); + $this->assertArrayNotHasKey('NA', StripeConnectCountries::all()); + } + /** @test */ public function each_country_has_required_keys(): void {