Skip to content

Commit

Permalink
Merge branch 'master' into fork/Jamesking56/patch-1
Browse files Browse the repository at this point in the history
  • Loading branch information
Kyon147 committed Sep 24, 2024
2 parents 3589a86 + be2b1d9 commit 22b8bc4
Show file tree
Hide file tree
Showing 22 changed files with 379 additions and 27 deletions.
24 changes: 21 additions & 3 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,25 @@ jobs:
- name: Checkout the code
uses: actions/checkout@v2

- name: Check for code style violation with PHP-CS-Fixer
uses: OskarStark/[email protected]
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
coverage: none

- name: Get Composer cache directory
id: cache-composer
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT

- name: Restore Composer cache
uses: actions/cache@v3
with:
args: --diff --dry-run
path: ${{ steps.cache-composer.outputs.dir }}
key: ${{ runner.os }}-${{ github.ref_name }}-composer-${{ hashFiles('**/composer.lock') }}

- name: Install Composer dependencies
run: |
composer install --no-interaction --prefer-dist
- name: Check for code style violation with PHP-CS-Fixer
run: vendor/bin/php-cs-fixer fix --diff
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ Its best to:

If you're forking the repository and wish to keep your copy up-to-date with the master, ensure you run this command:

`git remote add upstream [email protected]:osiset/laravel-shopify.git`
`git remote add upstream [email protected]:Kyon147/laravel-shopify.git`

You can then update by simply running:

Expand Down
1 change: 1 addition & 0 deletions src/Actions/AfterAuthorize.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public function __invoke(ShopIdValue $shopId): bool
} else {
// Run later
$job::dispatch($shop)
->onConnection(Util::getShopifyConfig('job_connections')['after_authenticate'])
->onQueue(Util::getShopifyConfig('job_queues')['after_authenticate']);
}

Expand Down
3 changes: 2 additions & 1 deletion src/Actions/DispatchScripts.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ public function __invoke(ShopIdValue $shopId, bool $inline = false): bool
($this->jobClass)::dispatch(
$shop->getId(),
$scripttags
)->onQueue(Util::getShopifyConfig('job_queues')['scripttags']);
)->onConnection(Util::getShopifyConfig('job_connections')['scripttags'])
->onQueue(Util::getShopifyConfig('job_queues')['scripttags']);
}

return true;
Expand Down
3 changes: 2 additions & 1 deletion src/Actions/DispatchWebhooks.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ public function __invoke(ShopIdValue $shopId, bool $inline = false): bool
($this->jobClass)::dispatch(
$shop->getId(),
$webhooks
)->onQueue(Util::getShopifyConfig('job_queues')['webhooks']);
)->onConnection(Util::getShopifyConfig('job_connections')['webhooks'])
->onQueue(Util::getShopifyConfig('job_queues')['webhooks']);
}

return true;
Expand Down
1 change: 1 addition & 0 deletions src/Http/Middleware/Billable.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public function handle(Request $request, Closure $next)
array_merge($request->input(), [
'shop' => $shop->getDomain()->toNative(),
'host' => $request->get('host'),
'locale' => $request->get('locale'),
])
);
}
Expand Down
1 change: 1 addition & 0 deletions src/Http/Middleware/VerifyScopes.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public function handle(Request $request, Closure $next)
[
'shop' => $shop->getDomain()->toNative(),
'host' => $request->get('host'),
'locale' => $request->get('locale'),
]
);
}
Expand Down
3 changes: 2 additions & 1 deletion src/Http/Middleware/VerifyShopify.php
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,7 @@ protected function tokenRedirect(Request $request): RedirectResponse
'shop' => ShopDomain::fromRequest($request)->toNative(),
'target' => $target,
'host' => $request->get('host'),
'locale' => $request->get('locale'),
]
);
}
Expand All @@ -320,7 +321,7 @@ protected function installRedirect(ShopDomainValue $shopDomain): RedirectRespons
{
return Redirect::route(
Util::getShopifyConfig('route_names.authenticate'),
['shop' => $shopDomain->toNative(), 'host' => request('host')]
['shop' => $shopDomain->toNative(), 'host' => request('host'), 'locale' => request('locale')]
);
}

Expand Down
1 change: 1 addition & 0 deletions src/Macros/TokenUrl.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public function generateParams(string $route, array $params = [], bool $absolute
'shop' => ShopDomain::fromRequest(Request::instance())->toNative(),
'target' => URL::route($route, $params, $absolute),
'host' => Request::instance()->get('host'),
'locale' => Request::instance()->get('locale'),
],
];
}
Expand Down
31 changes: 31 additions & 0 deletions src/Objects/Enums/SessionTokenSource.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

namespace Osiset\ShopifyApp\Objects\Enums;

use Funeralzone\ValueObjects\Enums\EnumTrait;
use Funeralzone\ValueObjects\ValueObject;

/**
* API call method types.
*
* @method static SessionTokenSource APP()
* @method static SessionTokenSource CHECKOUT_EXTENSION()
*/
final class SessionTokenSource implements ValueObject
{
use EnumTrait;

/**
* Token form Shopify App
*
* @var int
*/
public const APP = 0;

/**
* Token from UI extension
*
* @var int
*/
public const CHECKOUT_EXTENSION = 1;
}
50 changes: 42 additions & 8 deletions src/Objects/Values/SessionToken.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
use Assert\AssertionFailedException;
use Funeralzone\ValueObjects\Scalars\StringTrait;
use Illuminate\Support\Carbon;
use Illuminate\Support\Str;
use Osiset\ShopifyApp\Contracts\Objects\Values\SessionToken as SessionTokenValue;
use Osiset\ShopifyApp\Contracts\Objects\Values\ShopDomain as ShopDomainValue;
use Osiset\ShopifyApp\Objects\Enums\SessionTokenSource;
use Osiset\ShopifyApp\Util;

/**
Expand Down Expand Up @@ -129,6 +131,13 @@ final class SessionToken implements SessionTokenValue
*/
protected $shopDomain;

/**
* Shopify has multiple session tokens, slightly differing in format.
*
* @var string
*/
protected $tokenSource = SessionTokenSource::APP;

/**
* Constructor.
*
Expand Down Expand Up @@ -167,32 +176,38 @@ protected function decodeToken(): void
$this->parts = explode('.', $this->string);
$body = json_decode(Util::base64UrlDecode($this->parts[1]), true);

$this->tokenSource = $this->determineTokenSource($body);

// Confirm token is not malformed
Assert::thatAll([
$body['iss'],
$body['dest'],
$body['aud'],
$body['sub'],
$body['exp'],
$body['nbf'],
$body['iat'],
$body['jti'],
$body['sid'],
... $this->tokenSource === SessionTokenSource::APP
? [
$body['iss'],
$body['sub'],
$body['sid'],
]
: [],
])->notNull(self::EXCEPTION_MALFORMED);

// Format the values
$this->iss = $body['iss'];
$this->iss = $body['iss'] ?? '';
$this->dest = $body['dest'];
$this->aud = $body['aud'];
$this->sub = $body['sub'];
$this->sub = $body['sub'] ?? '';
$this->jti = $body['jti'];
$this->sid = SessionId::fromNative($body['sid']);
$this->sid = SessionId::fromNative($body['sid'] ?? '');
$this->exp = new Carbon($body['exp']);
$this->nbf = new Carbon($body['nbf']);
$this->iat = new Carbon($body['iat']);

// Parse the shop domain from the destination
$host = parse_url($body['dest'], PHP_URL_HOST);
$host = $this->findHost($body['dest']);
$this->shopDomain = NullableShopDomain::fromNative($host);
}

Expand Down Expand Up @@ -357,7 +372,10 @@ protected function verifySignature(): void
*/
protected function verifyValidity(): void
{
Assert::that($this->iss)->contains($this->dest, self::EXCEPTION_INVALID);
if ($this->tokenSource === SessionTokenSource::APP) {
Assert::that($this->iss)->contains($this->dest, self::EXCEPTION_INVALID);
}

Assert::that($this->aud)->eq(Util::getShopifyConfig('api_key', $this->getShopDomain()), self::EXCEPTION_INVALID);
}

Expand All @@ -377,4 +395,20 @@ protected function verifyExpiration(): void
$now->lessThan($this->getLeewayIssuedAt()),
])->false(self::EXCEPTION_EXPIRED);
}

protected function determineTokenSource(array $body): int
{
if (!isset($body['iss']) && !isset($body['sid'])) {
return SessionTokenSource::CHECKOUT_EXTENSION;
}

return SessionTokenSource::APP;
}

protected function findHost(string $destination): ?string
{
return Str::startsWith($destination, 'https')
? parse_url($destination, PHP_URL_HOST)
: $destination;
}
}
9 changes: 8 additions & 1 deletion src/Traits/AuthController.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public function authenticate(Request $request, AuthenticateShop $authShop)
'authUrl' => $result['url'],
'host' => $request->get('host'),
'shopDomain' => $shopDomain,
'locale' => $request->get('locale'),
]
);
} else {
Expand All @@ -77,6 +78,7 @@ public function authenticate(Request $request, AuthenticateShop $authShop)
[
'shop' => $shopDomain->toNative(),
'host' => $request->get('host'),
'locale' => $request->get('locale'),
]
);
}
Expand All @@ -100,11 +102,16 @@ public function token(Request $request)
$params = Util::parseQueryString($query);
$params['shop'] = $params['shop'] ?? $shopDomain->toNative() ?? '';
$params['host'] = $request->get('host');
$params['locale'] = $request->get('locale');
unset($params['token']);

$cleanTarget = trim(explode('?', $target)[0].'?'.http_build_query($params), '?');
} else {
$params = ['shop' => $shopDomain->toNative() ?? '', 'host' => $request->get('host')];
$params = [
'shop' => $shopDomain->toNative() ?? '',
'host' => $request->get('host'),
'locale' => $request->get('locale'),
];
$cleanTarget = trim(explode('?', $target)[0].'?'.http_build_query($params), '?');
}

Expand Down
20 changes: 13 additions & 7 deletions src/Traits/BillingController.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public function index(
[
'url' => $url,
'host' => $host,
'locale' => $request->get('locale'),
'apiKey' => Util::getShopifyConfig('api_key', ShopDomain::fromNative($request->get('shop'))),
]
);
Expand Down Expand Up @@ -89,6 +90,7 @@ public function process(
return Redirect::route(Util::getShopifyConfig('route_names.home'), [
'shop' => $shop->getDomain()->toNative(),
'host' => $host,
'locale' => $request->get('locale'),
]);
}
// Activate the plan and save
Expand All @@ -99,16 +101,20 @@ public function process(
$host
);

$data = [
'shop' => $shop->getDomain()->toNative(),
'host' => $host,
'locale' => $request->get('locale'),
];

if (!Util::useNativeAppBridge()) {
$data['billing'] = $result ? 'success' : 'failure';
}

// Go to homepage of app
return Redirect::route(
Util::getShopifyConfig('route_names.home'),
array_merge([
'shop' => $shop->getDomain()->toNative(),
'host' => $host,
], Util::useNativeAppBridge() ? [] : [
'host' => $host,
'billing' => $result ? 'success' : 'failure',
])
$data
)->with(
$result ? 'success' : 'failure',
'billing'
Expand Down
3 changes: 2 additions & 1 deletion src/Traits/WebhookController.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ public function handle(string $type, Request $request): ResponseResponse
$jobClass::dispatch(
$request->header('x-shopify-shop-domain'),
$jobData
)->onQueue(Util::getShopifyConfig('job_queues')['webhooks']);
)->onConnection(Util::getShopifyConfig('job_connections')['webhooks'])
->onQueue(Util::getShopifyConfig('job_queues')['webhooks']);

return Response::make('', ResponseResponse::HTTP_CREATED);
}
Expand Down
29 changes: 28 additions & 1 deletion src/resources/config/shopify-app.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,19 @@

'manual_migrations' => (bool) env('SHOPIFY_MANUAL_MIGRATIONS', false),

/*
|--------------------------------------------------------------------------
| Sub Domain
|--------------------------------------------------------------------------
|
| This is the subdomain where Shopify will be accessible from. If the
| setting is null, Shopify will reside under the same domain as the
| application. Otherwise, this value will be used as the subdomain.
|
*/

'domain' => env('SHOPIFY_DOMAIN'),

/*
|--------------------------------------------------------------------------
| Manual routes
Expand Down Expand Up @@ -351,7 +364,7 @@
| in this configuration file is unnecessary.
|
| If you register the listeners manually again here, the listener will be called twice.
|
|
| If you plan to store your listeners in a different directory like `App\Shopify\Listeners`
| or within multiple directories, then you should register them here.
|
Expand Down Expand Up @@ -468,7 +481,21 @@
'scripttags' => env('SCRIPTTAGS_JOB_QUEUE', null),
'after_authenticate' => env('AFTER_AUTHENTICATE_JOB_QUEUE', null),
],
/*
|--------------------------------------------------------------------------
| Job Connections
|--------------------------------------------------------------------------
|
| This option is for setting a specific job connection for webhooks, scripttags
| and after_authenticate_job.
|
*/

'job_connections' => [
'webhooks' => env('WEBHOOKS_JOB_CONNECTION', null),
'scripttags' => env('SCRIPTTAGS_JOB_CONNECTION', null),
'after_authenticate' => env('AFTER_AUTHENTICATE_JOB_CONNECTION', null),
],
/*
|--------------------------------------------------------------------------
| Config API Callback
Expand Down
5 changes: 4 additions & 1 deletion src/resources/routes/api.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@
$manualRoutes = explode(',', $manualRoutes);
}

Route::group(['middleware' => ['api']], function () use ($manualRoutes) {
Route::group([
'domain' => Util::getShopifyConfig('domain'),
'middleware' => ['api']
], function () use ($manualRoutes) {
/*
|--------------------------------------------------------------------------
| API Routes
Expand Down
Loading

0 comments on commit 22b8bc4

Please sign in to comment.