diff --git a/.env.example b/.env.example index 59a44b9..e07ab29 100644 --- a/.env.example +++ b/.env.example @@ -68,6 +68,7 @@ P2G_CLIENT_ID= P2G_CLIENT_SECRET= P2G_API_AUTH_URL= P2G_API_BASE_URL=https://www.parcel2go.com/api +P2G_API_SCOPE=public-api ETSY_KEYSTRING= ETSY_SHARED_SECRET= diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 50cf8a2..e40c2c8 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -205,7 +205,14 @@ jobs: - name: Install production PHP dependencies working-directory: ${{ env.DEPLOY_ROOT }}/${{ env.PROJECT_NAME }} run: | + rm -f \ + /tmp/TechOdyssey_Designs_Dashboard.packages.php \ + /tmp/TechOdyssey_Designs_Dashboard.services.php "$COMPOSER_CMD" install --no-dev --no-interaction --prefer-dist --optimize-autoloader + rm -f \ + /tmp/TechOdyssey_Designs_Dashboard.packages.php \ + /tmp/TechOdyssey_Designs_Dashboard.services.php + "$PHP_CMD" artisan package:discover --ansi --no-interaction - name: Clear compiled Laravel files working-directory: ${{ env.DEPLOY_ROOT }}/${{ env.PROJECT_NAME }} diff --git a/app/Http/Controllers/DashboardController.php b/app/Http/Controllers/DashboardController.php index 94f0564..86828cc 100644 --- a/app/Http/Controllers/DashboardController.php +++ b/app/Http/Controllers/DashboardController.php @@ -46,8 +46,13 @@ class DashboardController extends Controller $shippingConfig = $settings->for('parcel2go'); if ($isAdmin && filled($shippingConfig['client_id'] ?? null) && filled($shippingConfig['client_secret'] ?? null) && filled($shippingConfig['auth_url'] ?? null)) { try { - $shipments = array_slice($parcel2Go->fetch('me/orders/detail')['Orders'] ?? [], 0, 4); - $shippingStatus = 'Live account'; + if ($parcel2Go->hasScope('my-profile')) { + $shipments = array_slice($parcel2Go->fetch('me/orders/detail')['Orders'] ?? [], 0, 4); + $shippingStatus = 'Live account'; + } else { + $parcel2Go->verifyCredentials(); + $shippingStatus = 'Quotes available'; + } } catch (Throwable $exception) { report($exception); $shippingStatus = 'Temporarily unavailable'; diff --git a/app/Http/Controllers/EtsySettingsController.php b/app/Http/Controllers/EtsySettingsController.php index ee6a297..173ce8d 100644 --- a/app/Http/Controllers/EtsySettingsController.php +++ b/app/Http/Controllers/EtsySettingsController.php @@ -35,6 +35,7 @@ class EtsySettingsController extends Controller 'configured' => filled($parcel2go['client_id'] ?? null) && filled($parcel2go['client_secret'] ?? null) && filled($parcel2go['auth_url'] ?? null), 'auth_url' => $parcel2go['auth_url'] ?? '', 'base_url' => $parcel2go['base_url'] ?? '', + 'scope' => $parcel2go['scope'] ?? 'public-api', ], 'bambuddy' => [ 'stored' => in_array('bambuddy', $storedIntegrations, true), diff --git a/app/Http/Controllers/IntegrationSettingsController.php b/app/Http/Controllers/IntegrationSettingsController.php index 4d42bea..788b053 100644 --- a/app/Http/Controllers/IntegrationSettingsController.php +++ b/app/Http/Controllers/IntegrationSettingsController.php @@ -20,6 +20,7 @@ class IntegrationSettingsController extends Controller 'client_secret' => ['nullable', 'string', 'max:255'], 'auth_url' => ['nullable', 'url:https', 'max:2048'], 'base_url' => ['nullable', 'url:https', 'max:2048'], + 'scope' => ['nullable', 'string', 'in:public-api,public-api my-profile,public-api payment,public-api payment my-profile'], ], default => [ 'url' => ['nullable', 'url:https', 'max:2048'], diff --git a/app/Http/Controllers/ShippingController.php b/app/Http/Controllers/ShippingController.php index 85dc2ef..cc22ca1 100644 --- a/app/Http/Controllers/ShippingController.php +++ b/app/Http/Controllers/ShippingController.php @@ -15,11 +15,27 @@ class ShippingController extends Controller { abort_unless($request->user()->role === 'admin', 403); try { - $account = $parcel2Go->fetch('me/detail'); - $balance = $parcel2Go->fetch('me/prepay/balance'); - $orders = $parcel2Go->fetch('me/orders/detail'); + $account = []; + $balance = []; + $orders = []; + $profileAvailable = $parcel2Go->hasScope('my-profile'); + $profileNotice = null; - return view('shipping', compact('account', 'balance', 'orders') + [ + if ($profileAvailable) { + try { + $account = $parcel2Go->fetch('me/detail'); + $balance = $parcel2Go->fetch('me/prepay/balance'); + $orders = $parcel2Go->fetch('me/orders/detail'); + } catch (Throwable $exception) { + report($exception); + $profileNotice = 'Parcel2Go connected, but account history is unavailable. Check that this client has my-profile access.'; + } + } else { + $parcel2Go->verifyCredentials(); + $profileNotice = 'Parcel2Go connected for quotes and collections. Add my-profile access in Parcel2Go to show account history and labels.'; + } + + return view('shipping', compact('account', 'balance', 'orders', 'profileAvailable', 'profileNotice') + [ 'quotes' => $request->session()->get('shipping_quotes', []), 'collectionDates' => $request->session()->get('shipping_collection_dates', []), 'selectedService' => $request->session()->get('shipping_selected_service'), @@ -31,6 +47,8 @@ class ShippingController extends Controller return view('shipping', [ 'account' => [], 'balance' => [], 'orders' => [], + 'profileAvailable' => false, + 'profileNotice' => null, 'quotes' => $request->session()->get('shipping_quotes', []), 'collectionDates' => $request->session()->get('shipping_collection_dates', []), 'selectedService' => $request->session()->get('shipping_selected_service'), diff --git a/app/Services/Parcel2Go.php b/app/Services/Parcel2Go.php index b900057..6acc594 100644 --- a/app/Services/Parcel2Go.php +++ b/app/Services/Parcel2Go.php @@ -10,6 +10,16 @@ class Parcel2Go { public function __construct(private IntegrationSettings $settings) {} + public function hasScope(string $scope): bool + { + return in_array($scope, explode(' ', (string) ($this->settings->for('parcel2go')['scope'] ?? 'public-api')), true); + } + + public function verifyCredentials(): void + { + $this->accessToken(); + } + public function fetch(string $path): array { return Http::withToken($this->accessToken())->acceptJson()->connectTimeout(3)->timeout(15) @@ -104,7 +114,7 @@ class Parcel2Go $token = Http::asForm()->timeout(10)->post($settings['auth_url'], [ 'grant_type' => 'client_credentials', - 'scope' => 'public-api my-profile', + 'scope' => $settings['scope'] ?? 'public-api', 'client_id' => $settings['client_id'], 'client_secret' => $settings['client_secret'], ])->throw()->json('access_token'); diff --git a/config/services.php b/config/services.php index 51e3f07..f49b9e4 100644 --- a/config/services.php +++ b/config/services.php @@ -40,6 +40,7 @@ return [ 'client_secret' => env('P2G_CLIENT_SECRET'), 'auth_url' => env('P2G_API_AUTH_URL'), 'base_url' => env('P2G_API_BASE_URL', 'https://www.parcel2go.com/api'), + 'scope' => env('P2G_API_SCOPE', 'public-api'), ], 'etsy' => [ 'keystring' => env('ETSY_KEYSTRING'), diff --git a/resources/views/settings/index.blade.php b/resources/views/settings/index.blade.php index acb7b09..0c7bc13 100644 --- a/resources/views/settings/index.blade.php +++ b/resources/views/settings/index.blade.php @@ -55,6 +55,14 @@ + +
Choose account history only if Parcel2Go granted the my-profile scope to this client. An ungranted scope prevents sign in.
Dispatch
Compare services, arrange a collection and download labels.
The last booking request had no confirmed result. Check your account for the collection order, then reset this form.
Total: £{{ number_format((float) ($bookingResult['TotalPrice'] ?? 0), 2) }}. Payment and collection confirmation are completed with Parcel2Go.
@if($paymentHost && ($paymentHost === 'parcel2go.com' || str_ends_with($paymentHost, '.parcel2go.com')))Complete payment ↗@endifAccount
{{ trim(($account['Forename'] ?? '').' '.($account['Surname'] ?? '')) ?: 'Unavailable' }}
{{ $account['Email'] ?? '' }}
Prepay balance
£{{ number_format((float) ($balance['Balance'] ?? 0), 2) }}
Account
{{ trim(($account['Forename'] ?? '').' '.($account['Surname'] ?? '')) ?: 'Unavailable' }}
{{ $account['Email'] ?? '' }}
Prepay balance
{{ isset($balance['Balance']) ? '£'.number_format((float) $balance['Balance'], 2) : 'Unavailable' }}
Enter a UK parcel. Dimensions are centimetres and weight is kilograms.