From 8ee778ce2a59b3757b6c6d7fe41bbb3bc920200c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Mon, 3 Jun 2024 20:00:21 +0200 Subject: [PATCH 01/22] add gocardless env keys --- .env.example | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.env.example b/.env.example index e9d4cec8ed9a..18621f58ab14 100644 --- a/.env.example +++ b/.env.example @@ -63,3 +63,6 @@ APPLE_REDIRECT_URI= NORDIGEN_SECRET_ID= NORDIGEN_SECRET_KEY= + +GOCARDLESS_CLIENT_ID= +GOCARDLESS_CLIENT_SECRET= From 2447ef9d7dfc9ec2de482e4aaae7c3e777b2e554 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Mon, 3 Jun 2024 20:00:34 +0200 Subject: [PATCH 02/22] request for initial connect --- .../GoCardless/OAuthConnectConfirmRequest.php | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 app/Http/Requests/GoCardless/OAuthConnectConfirmRequest.php diff --git a/app/Http/Requests/GoCardless/OAuthConnectConfirmRequest.php b/app/Http/Requests/GoCardless/OAuthConnectConfirmRequest.php new file mode 100644 index 000000000000..4661e99d0cef --- /dev/null +++ b/app/Http/Requests/GoCardless/OAuthConnectConfirmRequest.php @@ -0,0 +1,46 @@ +|string> + */ + public function rules(): array + { + return [ + // + ]; + } + + public function getCompany(): \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Builder|\App\Models\BaseModel + { + MultiDB::findAndSetDbByCompanyKey( + $this->company_key, + ); + + return Company::query() + ->where('company_key', $this->company_key) + ->firstOrFail(); + } +} From f1ac41a5c4e1dc5515a99024d1b343ac0719614b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Mon, 3 Jun 2024 20:00:50 +0200 Subject: [PATCH 03/22] request for confirmation connect --- .../GoCardless/OAuthConnectRequest.php | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 app/Http/Requests/GoCardless/OAuthConnectRequest.php diff --git a/app/Http/Requests/GoCardless/OAuthConnectRequest.php b/app/Http/Requests/GoCardless/OAuthConnectRequest.php new file mode 100644 index 000000000000..32b746621a9d --- /dev/null +++ b/app/Http/Requests/GoCardless/OAuthConnectRequest.php @@ -0,0 +1,46 @@ +|string> + */ + public function rules(): array + { + return [ + // + ]; + } + + public function getCompany(): \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Builder|\App\Models\BaseModel + { + MultiDB::findAndSetDbByCompanyKey( + $this->company_key, + ); + + return Company::query() + ->where('company_key', $this->company_key) + ->firstOrFail(); + } +} From 0f0c5a7de8d4034a90cf10d614eb1e2264444113 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Mon, 3 Jun 2024 20:01:01 +0200 Subject: [PATCH 04/22] gocardless oauth connection --- .../Gateways/GoCardlessOAuthController.php | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 app/Http/Controllers/Gateways/GoCardlessOAuthController.php diff --git a/app/Http/Controllers/Gateways/GoCardlessOAuthController.php b/app/Http/Controllers/Gateways/GoCardlessOAuthController.php new file mode 100644 index 000000000000..1dd1c094920a --- /dev/null +++ b/app/Http/Controllers/Gateways/GoCardlessOAuthController.php @@ -0,0 +1,54 @@ + config('services.gocardless.client_id'), + 'redirect_uri' => route('gocardless.oauth.confirm', ['token' => $request->getCompany()->company_key]), + 'scope' => 'read_write', + 'response_type' => 'code', + 'prefill[email]' => 'ben@invoiceninja.com', + 'prefill[given_name]' => 'Ben', + 'prefill[family_name]' => 'The Ninja', + 'prefill[organisation_name]' => 'Fishing Store', + 'prefill[country_code]' => 'GB', + ]; + + return redirect()->to( + sprintf('https://connect-sandbox.gocardless.com/oauth/authorize?%s', http_build_query($params)) + ); + } + + public function confirm(OAuthConnectRequest $request): \Illuminate\Foundation\Application|\Illuminate\Routing\Redirector|\Illuminate\Http\RedirectResponse|\Illuminate\Contracts\Foundation\Application + { + $code = $request->query('code'); + + $response = Http::post('https://connect-sandbox.gocardless.com/oauth/access_token', [ + 'client_id' => config('services.gocardless.client_id'), + 'client_secret' => config('services.gocardless.client_secret'), + 'grant_type' => 'authorization_code', + 'code' => $code, + ]); + + dd($response->body()); + } +} From fa37dd62a974c8af586ce5af1f588110f3e50807 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Mon, 3 Jun 2024 20:01:07 +0200 Subject: [PATCH 05/22] routes & config --- config/services.php | 7 ++++++- routes/web.php | 4 ++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/config/services.php b/config/services.php index ae434aba6575..003b8b519d26 100644 --- a/config/services.php +++ b/config/services.php @@ -44,7 +44,7 @@ return [ 'address' => env('POSTMARK_OUTLOOK_FROM_ADDRESS', '') ], ], - + 'microsoft' => [ 'client_id' => env('MICROSOFT_CLIENT_ID'), 'client_secret' => env('MICROSOFT_CLIENT_SECRET'), @@ -120,5 +120,10 @@ return [ 'chorus' => [ 'client_id' => env('CHORUS_CLIENT_ID', false), 'secret' => env('CHORUS_SECRET', false), + ], + 'gocardless' => [ + 'client_id' => env('GOCARDLESS_CLIENT_ID', null), + 'client_secret' => env('GOCARDLESS_CLIENT_SECRET', null), + 'environment' => env('GOCARDLESS_ENVIRONMENT', 'production'), ] ]; diff --git a/routes/web.php b/routes/web.php index 1d56f5d88390..c5bc63c80c5f 100644 --- a/routes/web.php +++ b/routes/web.php @@ -9,6 +9,7 @@ use App\Http\Controllers\BaseController; use App\Http\Controllers\ClientPortal\ApplePayDomainController; use App\Http\Controllers\Gateways\Checkout3dsController; use App\Http\Controllers\Gateways\GoCardlessController; +use App\Http\Controllers\Gateways\GoCardlessOAuthController; use App\Http\Controllers\Gateways\Mollie3dsController; use App\Http\Controllers\SetupController; use App\Http\Controllers\StripeConnectController; @@ -49,3 +50,6 @@ Route::get('checkout/3ds_redirect/{company_key}/{company_gateway_id}/{hash}', [C Route::get('mollie/3ds_redirect/{company_key}/{company_gateway_id}/{hash}', [Mollie3dsController::class, 'index'])->middleware('domain_db')->name('mollie.3ds_redirect'); Route::get('gocardless/ibp_redirect/{company_key}/{company_gateway_id}/{hash}', [GoCardlessController::class, 'ibpRedirect'])->middleware('domain_db')->name('gocardless.ibp_redirect'); Route::get('.well-known/apple-developer-merchantid-domain-association', [ApplePayDomainController::class, 'showAppleMerchantId']); + +Route::get('gocardless/oauth/connect/{company_key}', [GoCardlessOAuthController::class, 'connect']); +Route::get('gocardless/oauth/connect/{company_key}/confirm', [GoCardlessOAuthController::class, 'confirm'])->name('gocardless.oauth.confirm'); From 46be0bf268965beebcc94c9b7a32326216747020 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Wed, 5 Jun 2024 19:26:00 +0200 Subject: [PATCH 06/22] remove token from confirmation route --- app/Http/Controllers/Gateways/GoCardlessOAuthController.php | 2 +- routes/web.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/Gateways/GoCardlessOAuthController.php b/app/Http/Controllers/Gateways/GoCardlessOAuthController.php index 1dd1c094920a..8a0a931a7993 100644 --- a/app/Http/Controllers/Gateways/GoCardlessOAuthController.php +++ b/app/Http/Controllers/Gateways/GoCardlessOAuthController.php @@ -23,7 +23,7 @@ class GoCardlessOAuthController extends Controller { $params = [ 'client_id' => config('services.gocardless.client_id'), - 'redirect_uri' => route('gocardless.oauth.confirm', ['token' => $request->getCompany()->company_key]), + 'redirect_uri' => route('gocardless.oauth.confirm'), 'scope' => 'read_write', 'response_type' => 'code', 'prefill[email]' => 'ben@invoiceninja.com', diff --git a/routes/web.php b/routes/web.php index c5bc63c80c5f..8e5f31b39d7a 100644 --- a/routes/web.php +++ b/routes/web.php @@ -52,4 +52,4 @@ Route::get('gocardless/ibp_redirect/{company_key}/{company_gateway_id}/{hash}', Route::get('.well-known/apple-developer-merchantid-domain-association', [ApplePayDomainController::class, 'showAppleMerchantId']); Route::get('gocardless/oauth/connect/{company_key}', [GoCardlessOAuthController::class, 'connect']); -Route::get('gocardless/oauth/connect/{company_key}/confirm', [GoCardlessOAuthController::class, 'confirm'])->name('gocardless.oauth.confirm'); +Route::get('gocardless/oauth/connect/confirm', [GoCardlessOAuthController::class, 'confirm'])->name('gocardless.oauth.confirm'); From b0383312c12c52e1c38f2a85531263f72650cde2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Tue, 11 Jun 2024 19:50:00 +0200 Subject: [PATCH 07/22] Add route for GoCardless OAuth connect confirmation --- routes/web.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/web.php b/routes/web.php index 8e5f31b39d7a..cc6df34bcc0a 100644 --- a/routes/web.php +++ b/routes/web.php @@ -51,5 +51,5 @@ Route::get('mollie/3ds_redirect/{company_key}/{company_gateway_id}/{hash}', [Mol Route::get('gocardless/ibp_redirect/{company_key}/{company_gateway_id}/{hash}', [GoCardlessController::class, 'ibpRedirect'])->middleware('domain_db')->name('gocardless.ibp_redirect'); Route::get('.well-known/apple-developer-merchantid-domain-association', [ApplePayDomainController::class, 'showAppleMerchantId']); -Route::get('gocardless/oauth/connect/{company_key}', [GoCardlessOAuthController::class, 'connect']); Route::get('gocardless/oauth/connect/confirm', [GoCardlessOAuthController::class, 'confirm'])->name('gocardless.oauth.confirm'); +Route::get('gocardless/oauth/connect/{company_key}', [GoCardlessOAuthController::class, 'connect']); From 8f8ab095bba7cd024eb4ced762abeeeea5b39caa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Tue, 11 Jun 2024 19:50:08 +0200 Subject: [PATCH 08/22] Add completed GoCardless connection view template --- .../gocardless_connect/completed.blade.php | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 resources/views/auth/gocardless_connect/completed.blade.php diff --git a/resources/views/auth/gocardless_connect/completed.blade.php b/resources/views/auth/gocardless_connect/completed.blade.php new file mode 100644 index 000000000000..85f8a111eb7d --- /dev/null +++ b/resources/views/auth/gocardless_connect/completed.blade.php @@ -0,0 +1,31 @@ +@extends('layouts.ninja') +@section('meta_title', ctrans('texts.success')) + +@section('body') +
+
+ + + + + +
+ +

Connecting your account using GoCardless has been successfully completed.

+ Click here to + continue. +
+@endsection \ No newline at end of file From 5878340523a2e9abdab2f4115fd10d6dde5ed41f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Tue, 11 Jun 2024 19:50:15 +0200 Subject: [PATCH 09/22] Add new view for GoCardless access denied page --- .../access_denied.blade.php | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 resources/views/auth/gocardless_connect/access_denied.blade.php diff --git a/resources/views/auth/gocardless_connect/access_denied.blade.php b/resources/views/auth/gocardless_connect/access_denied.blade.php new file mode 100644 index 000000000000..2ed1404d122a --- /dev/null +++ b/resources/views/auth/gocardless_connect/access_denied.blade.php @@ -0,0 +1,30 @@ +@extends('layouts.ninja') +@section('meta_title', ctrans('texts.error_title')) + +@section('body') +
+
+ + + + + +
+ +

We were unable to connect to GoCardless as access was denied.

+ Click here to + continue. +
+@endsection \ No newline at end of file From bffed73117ffd3a1f8bf3c5c6ab95600a27d7cbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Tue, 11 Jun 2024 19:50:21 +0200 Subject: [PATCH 10/22] Add Gocardless redirect URI to services configuration --- config/services.php | 1 + 1 file changed, 1 insertion(+) diff --git a/config/services.php b/config/services.php index 003b8b519d26..e49fd934ea47 100644 --- a/config/services.php +++ b/config/services.php @@ -125,5 +125,6 @@ return [ 'client_id' => env('GOCARDLESS_CLIENT_ID', null), 'client_secret' => env('GOCARDLESS_CLIENT_SECRET', null), 'environment' => env('GOCARDLESS_ENVIRONMENT', 'production'), + 'redirect_uri' => env('GOCARDLESS_REDIRECT_URI', 'https://invoicing.co/gocardless/oauth/connect/confirm'), ] ]; From e5e72123f0a47c7100c1147df5183382ea307e50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Tue, 11 Jun 2024 19:50:28 +0200 Subject: [PATCH 11/22] Update OAuthConnectConfirmRequest validation rules --- .../Requests/GoCardless/OAuthConnectConfirmRequest.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/Http/Requests/GoCardless/OAuthConnectConfirmRequest.php b/app/Http/Requests/GoCardless/OAuthConnectConfirmRequest.php index 4661e99d0cef..45db2feac5f3 100644 --- a/app/Http/Requests/GoCardless/OAuthConnectConfirmRequest.php +++ b/app/Http/Requests/GoCardless/OAuthConnectConfirmRequest.php @@ -29,18 +29,19 @@ class OAuthConnectConfirmRequest extends FormRequest public function rules(): array { return [ - // + 'state' => ['required', 'string'], + 'code' => ['required','string'], ]; } public function getCompany(): \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Builder|\App\Models\BaseModel { MultiDB::findAndSetDbByCompanyKey( - $this->company_key, + $this->query('state'), ); return Company::query() - ->where('company_key', $this->company_key) + ->where('company_key', $this->query('state')) ->firstOrFail(); } } From f15fb4c412fa0d3022a7efbf9d5038dab5f7054b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Tue, 11 Jun 2024 19:50:34 +0200 Subject: [PATCH 12/22] Add GoCardless webhook controller for processing events --- .../GoCardlessOAuthWebhookController.php | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 app/Http/Controllers/Gateways/GoCardlessOAuthWebhookController.php diff --git a/app/Http/Controllers/Gateways/GoCardlessOAuthWebhookController.php b/app/Http/Controllers/Gateways/GoCardlessOAuthWebhookController.php new file mode 100644 index 000000000000..b0a4286e3e85 --- /dev/null +++ b/app/Http/Controllers/Gateways/GoCardlessOAuthWebhookController.php @@ -0,0 +1,31 @@ +events as $event) { + match ($event['details']['cause']) { + 'app_disconnected' => info($event), + default => nlog('Not acting on this event type: ' . $event['details']['cause']), + }; + } + } +} From fa27afa6cd6763d35ad70635e34b43c9502c727a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Tue, 11 Jun 2024 19:50:41 +0200 Subject: [PATCH 13/22] Update GoCardless OAuth Controller with company details --- .../Gateways/GoCardlessOAuthController.php | 79 ++++++++++++++++--- 1 file changed, 66 insertions(+), 13 deletions(-) diff --git a/app/Http/Controllers/Gateways/GoCardlessOAuthController.php b/app/Http/Controllers/Gateways/GoCardlessOAuthController.php index 8a0a931a7993..0c0b0d68c988 100644 --- a/app/Http/Controllers/Gateways/GoCardlessOAuthController.php +++ b/app/Http/Controllers/Gateways/GoCardlessOAuthController.php @@ -13,42 +13,95 @@ namespace App\Http\Controllers\Gateways; +use App\DataMapper\FeesAndLimits; +use App\Factory\CompanyGatewayFactory; use App\Http\Controllers\Controller; +use App\Http\Requests\GoCardless\OAuthConnectConfirmRequest; use App\Http\Requests\GoCardless\OAuthConnectRequest; +use App\Models\CompanyGateway; +use App\Models\GatewayType; use Illuminate\Support\Facades\Http; class GoCardlessOAuthController extends Controller { - public function connect(OAuthConnectRequest $request): \Illuminate\Foundation\Application|\Illuminate\Routing\Redirector|\Illuminate\Http\RedirectResponse|\Illuminate\Contracts\Foundation\Application + public function connect(OAuthConnectRequest $request): \Illuminate\Http\RedirectResponse { + /** @var \App\Models\Company $company */ + $company = $request->getCompany(); + $params = [ 'client_id' => config('services.gocardless.client_id'), - 'redirect_uri' => route('gocardless.oauth.confirm'), + 'redirect_uri' => config('services.gocardless.redirect_uri'), 'scope' => 'read_write', 'response_type' => 'code', - 'prefill[email]' => 'ben@invoiceninja.com', - 'prefill[given_name]' => 'Ben', - 'prefill[family_name]' => 'The Ninja', - 'prefill[organisation_name]' => 'Fishing Store', - 'prefill[country_code]' => 'GB', + 'state' => $company->company_key, + 'prefill[email]' => $company->settings->email, + 'prefill[organisation_name]' => $company->settings->name, + 'prefill[country_code]' => $company->country()->iso_3166_2, ]; + $url = config('services.gocardless.environment') === 'production' + ? 'https://connect.gocardless.com/oauth/authorize?%s' + : 'https://connect-sandbox.gocardless.com/oauth/authorize?%s'; + + return redirect()->to( - sprintf('https://connect-sandbox.gocardless.com/oauth/authorize?%s', http_build_query($params)) + sprintf($url, http_build_query($params)) ); } - public function confirm(OAuthConnectRequest $request): \Illuminate\Foundation\Application|\Illuminate\Routing\Redirector|\Illuminate\Http\RedirectResponse|\Illuminate\Contracts\Foundation\Application + public function confirm(OAuthConnectConfirmRequest $request): \Illuminate\Http\RedirectResponse|\Illuminate\View\View { - $code = $request->query('code'); + /** @var \App\Models\Company $company */ + $company = $request->getCompany(); - $response = Http::post('https://connect-sandbox.gocardless.com/oauth/access_token', [ + // LBo0v_561xgFGnFUae6uEQEfrWoSEMnZ&state=5O2O85C8dPv1Gp1UPVq0xs4FVTZdq5dO + // https://invoicing.co/gocardless/oauth/connect/confirm?code=sH55_xb-2s1JtuEw-j7W0hT0Z1sFkM7l + + $url = config('services.gocardless.environment') === 'production' + ? 'https://connect.gocardless.com/oauth/access_token' + : 'https://connect-sandbox.gocardless.com/oauth/access_token'; + + $response = Http::post($url, [ 'client_id' => config('services.gocardless.client_id'), 'client_secret' => config('services.gocardless.client_secret'), 'grant_type' => 'authorization_code', - 'code' => $code, + 'code' => $request->query('code'), + 'redirect_uri' => config('services.gocardless.redirect_uri'), ]); - dd($response->body()); + if ($response->failed()) { + return view('auth.gocardless_connect.access_denied'); + } + + $company_gateway = CompanyGateway::query() + ->where('gateway_key', 'b9886f9257f0c6ee7c302f1c74475f6c') + ->where('company_id', $company->id) + ->first(); + + if ($company_gateway === null) { + $company_gateway = CompanyGatewayFactory::create($company->id, $company->owner()->id); + $fees_and_limits = new \stdClass(); + $fees_and_limits->{GatewayType::INSTANT_BANK_PAY} = new FeesAndLimits(); + $company_gateway->gateway_key = 'b9886f9257f0c6ee7c302f1c74475f6c'; + $company_gateway->fees_and_limits = $fees_and_limits; + $company_gateway->setConfig([]); + $company_gateway->token_billing = 'always'; // @todo: Double check + } + + $response = $response->json(); + + $payload = [ + 'account_id' => $response['organisation_id'], + 'token_type' => $response['token_type'], + 'scope' => $response['scope'], + 'livemode' => $response['active'], + 'access_token' => $response['access_token'], + ]; + + $company_gateway->setConfig($payload); + $company_gateway->save(); + + return view('auth.gocardless_connect.completed'); } } From 1f179aedaa582303d4bbe3b2afb4fe3a2f6dd325 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Wed, 12 Jun 2024 19:33:26 +0200 Subject: [PATCH 14/22] Update payload with current config in GoCardlessOAuthController --- app/Http/Controllers/Gateways/GoCardlessOAuthController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/Gateways/GoCardlessOAuthController.php b/app/Http/Controllers/Gateways/GoCardlessOAuthController.php index 0c0b0d68c988..7b2c9fd6369f 100644 --- a/app/Http/Controllers/Gateways/GoCardlessOAuthController.php +++ b/app/Http/Controllers/Gateways/GoCardlessOAuthController.php @@ -86,12 +86,12 @@ class GoCardlessOAuthController extends Controller $company_gateway->gateway_key = 'b9886f9257f0c6ee7c302f1c74475f6c'; $company_gateway->fees_and_limits = $fees_and_limits; $company_gateway->setConfig([]); - $company_gateway->token_billing = 'always'; // @todo: Double check } $response = $response->json(); $payload = [ + '__current' => $company_gateway->getConfig(), 'account_id' => $response['organisation_id'], 'token_type' => $response['token_type'], 'scope' => $response['scope'], From 19ef27b5e5ab124aa1a6e962906a43f90ffc57f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Wed, 12 Jun 2024 19:33:33 +0200 Subject: [PATCH 15/22] Refactor GoCardlessOAuthWebhookController for WebhookRequest --- .../GoCardlessOAuthWebhookController.php | 42 ++++++++++++++----- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/app/Http/Controllers/Gateways/GoCardlessOAuthWebhookController.php b/app/Http/Controllers/Gateways/GoCardlessOAuthWebhookController.php index b0a4286e3e85..e9461594f52c 100644 --- a/app/Http/Controllers/Gateways/GoCardlessOAuthWebhookController.php +++ b/app/Http/Controllers/Gateways/GoCardlessOAuthWebhookController.php @@ -10,22 +10,44 @@ * @license https://www.elastic.co/licensing/elastic-license */ -// @todo: Double check if this should be in admin module - namespace App\Http\Controllers\Gateways; use App\Http\Controllers\Controller; -use Illuminate\Http\Request; +use App\Http\Requests\GoCardless\WebhookRequest; +use App\Models\CompanyGateway; +use App\Repositories\CompanyRepository; +use Illuminate\Support\Arr; -class GoCardlessOAuthController extends Controller -{ - public function __invoke(Request $request) +class GoCardlessOAuthWebhookController extends Controller +{ + public function __construct( + protected CompanyRepository $company_repository, + ) { + } + + public function __invoke(WebhookRequest $request) { foreach ($request->events as $event) { - match ($event['details']['cause']) { - 'app_disconnected' => info($event), - default => nlog('Not acting on this event type: ' . $event['details']['cause']), - }; + nlog($event['action']); + + $e = Arr::dot($event); + + if ($event['action'] === 'disconnected') { + + /** @var \App\Models\CompanyGateway $company_gateway */ + $company_gateway = CompanyGateway::query() + ->whereJsonCotains('config->account_id', $e['links.organisation']) + ->firstOrFail(); + + $current = $company_gateway->getConfig('__current'); + + if ($current) { + $company_gateway->setConfig($current); + $company_gateway->save(); + } + + $this->company_repository->archive($company_gateway); + } } } } From b5495416f5087949681ebd5ae3c52482f4c95ef3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Wed, 12 Jun 2024 19:33:39 +0200 Subject: [PATCH 16/22] Add GoCardless webhook request validation rules --- .../Requests/GoCardless/WebhookRequest.php | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 app/Http/Requests/GoCardless/WebhookRequest.php diff --git a/app/Http/Requests/GoCardless/WebhookRequest.php b/app/Http/Requests/GoCardless/WebhookRequest.php new file mode 100644 index 000000000000..41d5b709f4c5 --- /dev/null +++ b/app/Http/Requests/GoCardless/WebhookRequest.php @@ -0,0 +1,34 @@ +|string> + */ + public function rules(): array + { + return [ + 'events' => ['required', 'array'], + 'events.*.resource_type' => ['required', 'string'], + ]; + } +} From 5d7b3231cce11d2ebbc79fa44e61daef1e309d83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Wed, 12 Jun 2024 19:33:45 +0200 Subject: [PATCH 17/22] Add GoCardless OAuth webhook controller for webhook handling --- routes/web.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/routes/web.php b/routes/web.php index cc6df34bcc0a..f2bc963fa468 100644 --- a/routes/web.php +++ b/routes/web.php @@ -10,6 +10,7 @@ use App\Http\Controllers\ClientPortal\ApplePayDomainController; use App\Http\Controllers\Gateways\Checkout3dsController; use App\Http\Controllers\Gateways\GoCardlessController; use App\Http\Controllers\Gateways\GoCardlessOAuthController; +use App\Http\Controllers\Gateways\GoCardlessOAuthWebhookController; use App\Http\Controllers\Gateways\Mollie3dsController; use App\Http\Controllers\SetupController; use App\Http\Controllers\StripeConnectController; @@ -52,4 +53,5 @@ Route::get('gocardless/ibp_redirect/{company_key}/{company_gateway_id}/{hash}', Route::get('.well-known/apple-developer-merchantid-domain-association', [ApplePayDomainController::class, 'showAppleMerchantId']); Route::get('gocardless/oauth/connect/confirm', [GoCardlessOAuthController::class, 'confirm'])->name('gocardless.oauth.confirm'); +Route::post('gocardless/oauth/connect/webhook', GoCardlessOAuthWebhookController::class)->name('gocardless.oauth.webhook'); Route::get('gocardless/oauth/connect/{company_key}', [GoCardlessOAuthController::class, 'connect']); From afe62f0aa80ab79e1bcc6a68ad9a2e3ef3410d11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Fri, 14 Jun 2024 18:18:21 +0200 Subject: [PATCH 18/22] Refactor access token and active status assignment in GoCardlessOAuthController --- app/Http/Controllers/Gateways/GoCardlessOAuthController.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/Gateways/GoCardlessOAuthController.php b/app/Http/Controllers/Gateways/GoCardlessOAuthController.php index 7b2c9fd6369f..6631eaef6944 100644 --- a/app/Http/Controllers/Gateways/GoCardlessOAuthController.php +++ b/app/Http/Controllers/Gateways/GoCardlessOAuthController.php @@ -95,8 +95,9 @@ class GoCardlessOAuthController extends Controller 'account_id' => $response['organisation_id'], 'token_type' => $response['token_type'], 'scope' => $response['scope'], - 'livemode' => $response['active'], - 'access_token' => $response['access_token'], + 'active' => $response['active'], + 'accessToken' => $response['access_token'], + 'testMode' => $company_gateway->getConfigField('testMode'), ]; $company_gateway->setConfig($payload); From bddb5d4b28d9d442ab0d1a0d23a00e62ed36dc50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Fri, 14 Jun 2024 18:20:01 +0200 Subject: [PATCH 19/22] Refactor GoCardlessOAuthController to enable OAuth2 integration and set oauth2 flag to true. --- app/Http/Controllers/Gateways/GoCardlessOAuthController.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Http/Controllers/Gateways/GoCardlessOAuthController.php b/app/Http/Controllers/Gateways/GoCardlessOAuthController.php index 6631eaef6944..b4aa0688fa9b 100644 --- a/app/Http/Controllers/Gateways/GoCardlessOAuthController.php +++ b/app/Http/Controllers/Gateways/GoCardlessOAuthController.php @@ -98,6 +98,7 @@ class GoCardlessOAuthController extends Controller 'active' => $response['active'], 'accessToken' => $response['access_token'], 'testMode' => $company_gateway->getConfigField('testMode'), + 'oauth2' => true, ]; $company_gateway->setConfig($payload); From 1c93e7bd4185de775a38b442871df202274d2391 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Fri, 14 Jun 2024 19:13:01 +0200 Subject: [PATCH 20/22] Refactor GoCardless OAuth connect route to use token instead of company key --- routes/web.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/web.php b/routes/web.php index f2bc963fa468..3e4c95620748 100644 --- a/routes/web.php +++ b/routes/web.php @@ -54,4 +54,4 @@ Route::get('.well-known/apple-developer-merchantid-domain-association', [ApplePa Route::get('gocardless/oauth/connect/confirm', [GoCardlessOAuthController::class, 'confirm'])->name('gocardless.oauth.confirm'); Route::post('gocardless/oauth/connect/webhook', GoCardlessOAuthWebhookController::class)->name('gocardless.oauth.webhook'); -Route::get('gocardless/oauth/connect/{company_key}', [GoCardlessOAuthController::class, 'connect']); +Route::get('gocardless/oauth/connect/{token}', [GoCardlessOAuthController::class, 'connect']); From 647ac08e801b410992c44a6a2ad192bbf36e062b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Fri, 14 Jun 2024 19:14:07 +0200 Subject: [PATCH 21/22] Fixed issue with retrieving company key by adding Cache facade and using stored data for company key instead of token --- app/Http/Requests/GoCardless/OAuthConnectRequest.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/Http/Requests/GoCardless/OAuthConnectRequest.php b/app/Http/Requests/GoCardless/OAuthConnectRequest.php index 32b746621a9d..56722de8912d 100644 --- a/app/Http/Requests/GoCardless/OAuthConnectRequest.php +++ b/app/Http/Requests/GoCardless/OAuthConnectRequest.php @@ -15,6 +15,7 @@ namespace App\Http\Requests\GoCardless; use App\Libraries\MultiDB; use App\Models\Company; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Cache; class OAuthConnectRequest extends FormRequest { @@ -35,12 +36,16 @@ class OAuthConnectRequest extends FormRequest public function getCompany(): \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Builder|\App\Models\BaseModel { + $data = Cache::get( + key: $this->token, + ); + MultiDB::findAndSetDbByCompanyKey( - $this->company_key, + company_key: $data['company_key'], ); return Company::query() - ->where('company_key', $this->company_key) + ->where('company_key', $data['company_key']) ->firstOrFail(); } } From d34efa963f7b2bb1636d007a79609857976e0aaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Sat, 15 Jun 2024 00:14:03 +0200 Subject: [PATCH 22/22] Update GoCardlessOAuthWebhookController.php --- .../Controllers/Gateways/GoCardlessOAuthWebhookController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/Gateways/GoCardlessOAuthWebhookController.php b/app/Http/Controllers/Gateways/GoCardlessOAuthWebhookController.php index e9461594f52c..0d91a288225f 100644 --- a/app/Http/Controllers/Gateways/GoCardlessOAuthWebhookController.php +++ b/app/Http/Controllers/Gateways/GoCardlessOAuthWebhookController.php @@ -36,7 +36,7 @@ class GoCardlessOAuthWebhookController extends Controller /** @var \App\Models\CompanyGateway $company_gateway */ $company_gateway = CompanyGateway::query() - ->whereJsonCotains('config->account_id', $e['links.organisation']) + ->whereJsonContains('config->account_id', $e['links.organisation']) ->firstOrFail(); $current = $company_gateway->getConfig('__current');