From 8119832291024e3625ff439a41475ec75b8bac87 Mon Sep 17 00:00:00 2001 From: Nahid Bin Azhar Date: Thu, 25 Aug 2022 12:03:49 +0600 Subject: [PATCH 01/31] fixed issue when using custom table for authentication instead of users table --- src/resources/config/shopify-app.php | 5 +++++ .../migrations/2020_01_29_230905_create_shops_table.php | 8 ++++---- .../migrations/2020_01_29_231006_create_charges_table.php | 7 ++++--- ...4_21_103633_add_password_updated_at_to_users_table.php | 5 +++-- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/resources/config/shopify-app.php b/src/resources/config/shopify-app.php index 13338313..804a71b9 100644 --- a/src/resources/config/shopify-app.php +++ b/src/resources/config/shopify-app.php @@ -470,5 +470,10 @@ * The table name for Plan model. */ 'plans' => 'plans', + + /* + * The table name for the Shop. + */ + 'shops' => 'users', ] ]; diff --git a/src/resources/database/migrations/2020_01_29_230905_create_shops_table.php b/src/resources/database/migrations/2020_01_29_230905_create_shops_table.php index d7414bc4..33e3bd2e 100644 --- a/src/resources/database/migrations/2020_01_29_230905_create_shops_table.php +++ b/src/resources/database/migrations/2020_01_29_230905_create_shops_table.php @@ -14,13 +14,13 @@ class CreateShopsTable extends Migration */ public function up(): void { - Schema::table('users', function (Blueprint $table) { + Schema::table(Util::getShopifyConfig('table_names.shops'), function (Blueprint $table) { $table->boolean('shopify_grandfathered')->default(false); $table->string('shopify_namespace')->nullable(true)->default(null); $table->boolean('shopify_freemium')->default(false); $table->integer('plan_id')->unsigned()->nullable(); - if (! Schema::hasColumn('users', 'deleted_at')) { + if (! Schema::hasColumn(Util::getShopifyConfig('table_names.shops'), 'deleted_at')) { $table->softDeletes(); } @@ -35,8 +35,8 @@ public function up(): void */ public function down(): void { - Schema::table('users', function (Blueprint $table) { - $table->dropForeign('users_plan_id_foreign'); + Schema::table(Util::getShopifyConfig('table_names.shops'), function (Blueprint $table) { + $table->dropForeign(Util::getShopifyConfig('table_names.shops') . '_plan_id_foreign'); $table->dropColumn([ 'shopify_grandfathered', 'shopify_namespace', diff --git a/src/resources/database/migrations/2020_01_29_231006_create_charges_table.php b/src/resources/database/migrations/2020_01_29_231006_create_charges_table.php index 5f63d9d5..c386a5ae 100644 --- a/src/resources/database/migrations/2020_01_29_231006_create_charges_table.php +++ b/src/resources/database/migrations/2020_01_29_231006_create_charges_table.php @@ -5,6 +5,7 @@ use Illuminate\Foundation\Application; use Illuminate\Support\Facades\Schema; use Osiset\ShopifyApp\Util; +use Illuminate\Support\Str; class CreateChargesTable extends Migration { @@ -79,13 +80,13 @@ public function up() $table->softDeletes(); if ($this->getLaravelVersion() < 5.8) { - $table->integer('user_id')->unsigned(); + $table->integer(Str::singular(Util::getShopifyConfig('table_names.shops')) . '_id')->unsigned(); } else { - $table->bigInteger('user_id')->unsigned(); + $table->bigInteger(Str::singular(Util::getShopifyConfig('table_names.shops')) . '_id')->unsigned(); } // Linking - $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + $table->foreign(Str::singular(Util::getShopifyConfig('table_names.shops')) . '_id')->references('id')->on(Util::getShopifyConfig('table_names.shops'))->onDelete('cascade'); $table->foreign('plan_id')->references('id')->on(Util::getShopifyConfig('table_names.plans', 'plans')); }); } diff --git a/src/resources/database/migrations/2021_04_21_103633_add_password_updated_at_to_users_table.php b/src/resources/database/migrations/2021_04_21_103633_add_password_updated_at_to_users_table.php index ab3ff59b..1b14746b 100644 --- a/src/resources/database/migrations/2021_04_21_103633_add_password_updated_at_to_users_table.php +++ b/src/resources/database/migrations/2021_04_21_103633_add_password_updated_at_to_users_table.php @@ -3,6 +3,7 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; +use Osiset\ShopifyApp\Util; class AddPasswordUpdatedAtToUsersTable extends Migration { @@ -13,7 +14,7 @@ class AddPasswordUpdatedAtToUsersTable extends Migration */ public function up() { - Schema::table('users', function (Blueprint $table) { + Schema::table(Util::getShopifyConfig('table_names.shops'), function (Blueprint $table) { $table->date('password_updated_at')->nullable(); }); } @@ -25,7 +26,7 @@ public function up() */ public function down() { - Schema::table('users', function (Blueprint $table) { + Schema::table(Util::getShopifyConfig('table_names.shops'), function (Blueprint $table) { $table->dropColumn('password_updated_at'); }); } From e00b49eb6d4e48013301bbddc93536c6d4eabf31 Mon Sep 17 00:00:00 2001 From: Nahid Bin Azhar Date: Wed, 7 Sep 2022 11:46:50 +0600 Subject: [PATCH 02/31] fixed charges model saving when using custom shop table --- src/Storage/Commands/Charge.php | 8 ++++++-- src/Storage/Models/Charge.php | 16 ++++++++++++++-- src/Storage/Queries/Charge.php | 6 +++++- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/Storage/Commands/Charge.php b/src/Storage/Commands/Charge.php index 05ca3c20..672020dc 100644 --- a/src/Storage/Commands/Charge.php +++ b/src/Storage/Commands/Charge.php @@ -3,6 +3,7 @@ namespace Osiset\ShopifyApp\Storage\Commands; use Illuminate\Support\Carbon; +use Illuminate\Support\Str; use Osiset\ShopifyApp\Contracts\Commands\Charge as ChargeCommand; use Osiset\ShopifyApp\Contracts\Queries\Charge as ChargeQuery; use Osiset\ShopifyApp\Objects\Enums\ChargeStatus; @@ -50,10 +51,12 @@ public function make(ChargeTransfer $chargeObj): ChargeId return $obj instanceof Carbon; }; + $userTableId = Str::singular(Util::getShopifyConfig('table_names.shops', 'users')) . '_id'; + $chargeClass = Util::getShopifyConfig('models.charge', ChargeModel::class); $charge = new $chargeClass(); $charge->plan_id = $chargeObj->planId->toNative(); - $charge->user_id = $chargeObj->shopId->toNative(); + $charge->$userTableId = $chargeObj->shopId->toNative(); $charge->charge_id = $chargeObj->chargeReference->toNative(); $charge->type = $chargeObj->chargeType->toNative(); $charge->status = $chargeObj->chargeStatus->toNative(); @@ -89,10 +92,11 @@ public function delete(ChargeReference $chargeRef, ShopId $shopId): bool */ public function makeUsage(UsageChargeTransfer $chargeObj): ChargeId { + $userTableId = Str::singular(Util::getShopifyConfig('table_names.shops', 'users')) . '_id'; // Create the charge $chargeClass = Util::getShopifyConfig('models.charge', ChargeModel::class); $charge = new $chargeClass(); - $charge->user_id = $chargeObj->shopId->toNative(); + $charge->$userTableId = $chargeObj->shopId->toNative(); $charge->charge_id = $chargeObj->chargeReference->toNative(); $charge->type = $chargeObj->chargeType->toNative(); $charge->status = $chargeObj->chargeStatus->toNative(); diff --git a/src/Storage/Models/Charge.php b/src/Storage/Models/Charge.php index f9e10828..fddcdd81 100644 --- a/src/Storage/Models/Charge.php +++ b/src/Storage/Models/Charge.php @@ -5,6 +5,7 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\SoftDeletes; +use Illuminate\Support\Str; use Osiset\ShopifyApp\Objects\Enums\ChargeStatus; use Osiset\ShopifyApp\Objects\Enums\ChargeType; use Osiset\ShopifyApp\Objects\Values\ChargeId; @@ -18,6 +19,8 @@ class Charge extends Model { use SoftDeletes; + protected $userTableId = 'user_id'; + /** * The attributes that are mass assignable. * @@ -25,7 +28,6 @@ class Charge extends Model */ protected $fillable = [ 'type', - 'user_id', 'charge_id', 'plan_id', 'status', @@ -49,6 +51,16 @@ class Charge extends Model */ protected $dates = ['deleted_at']; + public function __construct(array $attributes = []) + { + $this->userTableId = Str::singular(Util::getShopifyConfig('table_names.shops', 'users')) . '_id'; + $this->fillable[] = $this->userTableId; + + parent::__construct($attributes); + + + } + /** * Get table name. * @@ -88,7 +100,7 @@ public function shop(): BelongsTo { return $this->belongsTo( Util::getShopifyConfig('user_model'), - 'user_id', + $this->userTableId, 'id' ); } diff --git a/src/Storage/Queries/Charge.php b/src/Storage/Queries/Charge.php index 41a6235f..f1013764 100644 --- a/src/Storage/Queries/Charge.php +++ b/src/Storage/Queries/Charge.php @@ -2,6 +2,7 @@ namespace Osiset\ShopifyApp\Storage\Queries; +use Illuminate\Support\Str; use Osiset\ShopifyApp\Contracts\Queries\Charge as IChargeQuery; use Osiset\ShopifyApp\Objects\Values\ChargeId; use Osiset\ShopifyApp\Objects\Values\ChargeReference; @@ -21,6 +22,8 @@ class Charge implements IChargeQuery */ protected $chargeModel; + protected $userTableId; + /** * Init for charge command. */ @@ -28,6 +31,7 @@ public function __construct() { $chargeClass = Util::getShopifyConfig('models.charge', ChargeModel::class); $this->chargeModel = new $chargeClass(); + $this->userTableId = Str::singular(Util::getShopifyConfig('table_names.shops', 'users')) . '_id'; } @@ -61,7 +65,7 @@ public function getByReferenceAndShopId(ChargeReference $chargeRef, ShopId $shop { return $this->chargeModel->query() ->where('charge_id', $chargeRef->toNative()) - ->where('user_id', $shopId->toNative()) + ->where($this->userTableId, $shopId->toNative()) ->get() ->first(); } From 7ab1e9a60c9e2dcf59d4b5c6cad2392a6eb5d0bc Mon Sep 17 00:00:00 2001 From: Nahid Bin Azhar Date: Thu, 8 Sep 2022 23:00:57 +0600 Subject: [PATCH 03/31] refactor code for reusing shops table name and its foreign id --- src/Storage/Commands/Charge.php | 9 ++++----- src/Storage/Models/Charge.php | 8 ++------ src/Storage/Queries/Charge.php | 6 +----- src/Util.php | 19 +++++++++++++++++++ .../2020_01_29_230905_create_shops_table.php | 8 ++++---- ...2020_01_29_231006_create_charges_table.php | 7 +++---- ...add_password_updated_at_to_users_table.php | 4 ++-- 7 files changed, 35 insertions(+), 26 deletions(-) diff --git a/src/Storage/Commands/Charge.php b/src/Storage/Commands/Charge.php index 672020dc..ee669903 100644 --- a/src/Storage/Commands/Charge.php +++ b/src/Storage/Commands/Charge.php @@ -3,7 +3,6 @@ namespace Osiset\ShopifyApp\Storage\Commands; use Illuminate\Support\Carbon; -use Illuminate\Support\Str; use Osiset\ShopifyApp\Contracts\Commands\Charge as ChargeCommand; use Osiset\ShopifyApp\Contracts\Queries\Charge as ChargeQuery; use Osiset\ShopifyApp\Objects\Enums\ChargeStatus; @@ -51,12 +50,12 @@ public function make(ChargeTransfer $chargeObj): ChargeId return $obj instanceof Carbon; }; - $userTableId = Str::singular(Util::getShopifyConfig('table_names.shops', 'users')) . '_id'; + $shopTableId = Util::getShopsTable(true); $chargeClass = Util::getShopifyConfig('models.charge', ChargeModel::class); $charge = new $chargeClass(); $charge->plan_id = $chargeObj->planId->toNative(); - $charge->$userTableId = $chargeObj->shopId->toNative(); + $charge->$shopTableId = $chargeObj->shopId->toNative(); $charge->charge_id = $chargeObj->chargeReference->toNative(); $charge->type = $chargeObj->chargeType->toNative(); $charge->status = $chargeObj->chargeStatus->toNative(); @@ -92,11 +91,11 @@ public function delete(ChargeReference $chargeRef, ShopId $shopId): bool */ public function makeUsage(UsageChargeTransfer $chargeObj): ChargeId { - $userTableId = Str::singular(Util::getShopifyConfig('table_names.shops', 'users')) . '_id'; + $shopTableId = Util::getShopsTable(true); // Create the charge $chargeClass = Util::getShopifyConfig('models.charge', ChargeModel::class); $charge = new $chargeClass(); - $charge->$userTableId = $chargeObj->shopId->toNative(); + $charge->$shopTableId = $chargeObj->shopId->toNative(); $charge->charge_id = $chargeObj->chargeReference->toNative(); $charge->type = $chargeObj->chargeType->toNative(); $charge->status = $chargeObj->chargeStatus->toNative(); diff --git a/src/Storage/Models/Charge.php b/src/Storage/Models/Charge.php index fddcdd81..440a1777 100644 --- a/src/Storage/Models/Charge.php +++ b/src/Storage/Models/Charge.php @@ -5,7 +5,6 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\SoftDeletes; -use Illuminate\Support\Str; use Osiset\ShopifyApp\Objects\Enums\ChargeStatus; use Osiset\ShopifyApp\Objects\Enums\ChargeType; use Osiset\ShopifyApp\Objects\Values\ChargeId; @@ -19,8 +18,6 @@ class Charge extends Model { use SoftDeletes; - protected $userTableId = 'user_id'; - /** * The attributes that are mass assignable. * @@ -53,8 +50,7 @@ class Charge extends Model public function __construct(array $attributes = []) { - $this->userTableId = Str::singular(Util::getShopifyConfig('table_names.shops', 'users')) . '_id'; - $this->fillable[] = $this->userTableId; + $this->fillable[] = Util::getShopsTable(true); parent::__construct($attributes); @@ -100,7 +96,7 @@ public function shop(): BelongsTo { return $this->belongsTo( Util::getShopifyConfig('user_model'), - $this->userTableId, + Util::getShopsTable(true), 'id' ); } diff --git a/src/Storage/Queries/Charge.php b/src/Storage/Queries/Charge.php index f1013764..75613f46 100644 --- a/src/Storage/Queries/Charge.php +++ b/src/Storage/Queries/Charge.php @@ -2,7 +2,6 @@ namespace Osiset\ShopifyApp\Storage\Queries; -use Illuminate\Support\Str; use Osiset\ShopifyApp\Contracts\Queries\Charge as IChargeQuery; use Osiset\ShopifyApp\Objects\Values\ChargeId; use Osiset\ShopifyApp\Objects\Values\ChargeReference; @@ -22,8 +21,6 @@ class Charge implements IChargeQuery */ protected $chargeModel; - protected $userTableId; - /** * Init for charge command. */ @@ -31,7 +28,6 @@ public function __construct() { $chargeClass = Util::getShopifyConfig('models.charge', ChargeModel::class); $this->chargeModel = new $chargeClass(); - $this->userTableId = Str::singular(Util::getShopifyConfig('table_names.shops', 'users')) . '_id'; } @@ -65,7 +61,7 @@ public function getByReferenceAndShopId(ChargeReference $chargeRef, ShopId $shop { return $this->chargeModel->query() ->where('charge_id', $chargeRef->toNative()) - ->where($this->userTableId, $shopId->toNative()) + ->where(Util::getShopsTable(true), $shopId->toNative()) ->get() ->first(); } diff --git a/src/Util.php b/src/Util.php index ee88ebe3..240669a7 100644 --- a/src/Util.php +++ b/src/Util.php @@ -209,4 +209,23 @@ public static function getGraphQLWebhookTopic(string $topic): string ->upper() ->replaceMatches('/[^A-Z_]/', '_'); } + + + /** + * Get the table name / table foreign_id for shop + * + * @param bool $foreignId + * + * @return string + */ + public static function getShopsTable(bool $foreignId = false): string + { + $shopTable = Util::getShopifyConfig('table_names.shops') ?? 'users'; + + if ($foreignId) { + return Str::singular($shopTable) . '_id' ; + } + + return $shopTable; + } } diff --git a/src/resources/database/migrations/2020_01_29_230905_create_shops_table.php b/src/resources/database/migrations/2020_01_29_230905_create_shops_table.php index 33e3bd2e..cd7cc26b 100644 --- a/src/resources/database/migrations/2020_01_29_230905_create_shops_table.php +++ b/src/resources/database/migrations/2020_01_29_230905_create_shops_table.php @@ -14,13 +14,13 @@ class CreateShopsTable extends Migration */ public function up(): void { - Schema::table(Util::getShopifyConfig('table_names.shops'), function (Blueprint $table) { + Schema::table(Util::getShopsTable(), function (Blueprint $table) { $table->boolean('shopify_grandfathered')->default(false); $table->string('shopify_namespace')->nullable(true)->default(null); $table->boolean('shopify_freemium')->default(false); $table->integer('plan_id')->unsigned()->nullable(); - if (! Schema::hasColumn(Util::getShopifyConfig('table_names.shops'), 'deleted_at')) { + if (! Schema::hasColumn(Util::getShopsTable(), 'deleted_at')) { $table->softDeletes(); } @@ -35,8 +35,8 @@ public function up(): void */ public function down(): void { - Schema::table(Util::getShopifyConfig('table_names.shops'), function (Blueprint $table) { - $table->dropForeign(Util::getShopifyConfig('table_names.shops') . '_plan_id_foreign'); + Schema::table(Util::getShopsTable(), function (Blueprint $table) { + $table->dropForeign(Util::getShopsTable() . '_plan_id_foreign'); $table->dropColumn([ 'shopify_grandfathered', 'shopify_namespace', diff --git a/src/resources/database/migrations/2020_01_29_231006_create_charges_table.php b/src/resources/database/migrations/2020_01_29_231006_create_charges_table.php index c386a5ae..9f1b9128 100644 --- a/src/resources/database/migrations/2020_01_29_231006_create_charges_table.php +++ b/src/resources/database/migrations/2020_01_29_231006_create_charges_table.php @@ -5,7 +5,6 @@ use Illuminate\Foundation\Application; use Illuminate\Support\Facades\Schema; use Osiset\ShopifyApp\Util; -use Illuminate\Support\Str; class CreateChargesTable extends Migration { @@ -80,13 +79,13 @@ public function up() $table->softDeletes(); if ($this->getLaravelVersion() < 5.8) { - $table->integer(Str::singular(Util::getShopifyConfig('table_names.shops')) . '_id')->unsigned(); + $table->integer(Util::getShopsTable(true))->unsigned(); } else { - $table->bigInteger(Str::singular(Util::getShopifyConfig('table_names.shops')) . '_id')->unsigned(); + $table->bigInteger(Util::getShopsTable(true))->unsigned(); } // Linking - $table->foreign(Str::singular(Util::getShopifyConfig('table_names.shops')) . '_id')->references('id')->on(Util::getShopifyConfig('table_names.shops'))->onDelete('cascade'); + $table->foreign(Util::getShopsTable(true))->references('id')->on(Util::getShopsTable())->onDelete('cascade'); $table->foreign('plan_id')->references('id')->on(Util::getShopifyConfig('table_names.plans', 'plans')); }); } diff --git a/src/resources/database/migrations/2021_04_21_103633_add_password_updated_at_to_users_table.php b/src/resources/database/migrations/2021_04_21_103633_add_password_updated_at_to_users_table.php index 1b14746b..21751b3c 100644 --- a/src/resources/database/migrations/2021_04_21_103633_add_password_updated_at_to_users_table.php +++ b/src/resources/database/migrations/2021_04_21_103633_add_password_updated_at_to_users_table.php @@ -14,7 +14,7 @@ class AddPasswordUpdatedAtToUsersTable extends Migration */ public function up() { - Schema::table(Util::getShopifyConfig('table_names.shops'), function (Blueprint $table) { + Schema::table(Util::getShopsTable(), function (Blueprint $table) { $table->date('password_updated_at')->nullable(); }); } @@ -26,7 +26,7 @@ public function up() */ public function down() { - Schema::table(Util::getShopifyConfig('table_names.shops'), function (Blueprint $table) { + Schema::table(Util::getShopsTable(), function (Blueprint $table) { $table->dropColumn('password_updated_at'); }); } From 54907b60c9e7b4efcd86575c733f48a86812c460 Mon Sep 17 00:00:00 2001 From: Nahid Bin Azhar Date: Fri, 9 Sep 2022 00:49:49 +0600 Subject: [PATCH 04/31] refactor getShopsTable() and added new method for getting shops table foreign key --- src/Storage/Commands/Charge.php | 4 ++-- src/Storage/Models/Charge.php | 4 ++-- src/Storage/Queries/Charge.php | 2 +- src/Util.php | 22 ++++++++++--------- ...2020_01_29_231006_create_charges_table.php | 6 ++--- 5 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/Storage/Commands/Charge.php b/src/Storage/Commands/Charge.php index ee669903..18c23a3f 100644 --- a/src/Storage/Commands/Charge.php +++ b/src/Storage/Commands/Charge.php @@ -50,7 +50,7 @@ public function make(ChargeTransfer $chargeObj): ChargeId return $obj instanceof Carbon; }; - $shopTableId = Util::getShopsTable(true); + $shopTableId = Util::getShopsTableForeignKey(); $chargeClass = Util::getShopifyConfig('models.charge', ChargeModel::class); $charge = new $chargeClass(); @@ -91,7 +91,7 @@ public function delete(ChargeReference $chargeRef, ShopId $shopId): bool */ public function makeUsage(UsageChargeTransfer $chargeObj): ChargeId { - $shopTableId = Util::getShopsTable(true); + $shopTableId = Util::getShopsTableForeignKey(); // Create the charge $chargeClass = Util::getShopifyConfig('models.charge', ChargeModel::class); $charge = new $chargeClass(); diff --git a/src/Storage/Models/Charge.php b/src/Storage/Models/Charge.php index 440a1777..d48c2e72 100644 --- a/src/Storage/Models/Charge.php +++ b/src/Storage/Models/Charge.php @@ -50,7 +50,7 @@ class Charge extends Model public function __construct(array $attributes = []) { - $this->fillable[] = Util::getShopsTable(true); + $this->fillable[] = Util::getShopsTableForeignKey(); parent::__construct($attributes); @@ -96,7 +96,7 @@ public function shop(): BelongsTo { return $this->belongsTo( Util::getShopifyConfig('user_model'), - Util::getShopsTable(true), + Util::getShopsTableForeignKey(), 'id' ); } diff --git a/src/Storage/Queries/Charge.php b/src/Storage/Queries/Charge.php index 75613f46..75ba4912 100644 --- a/src/Storage/Queries/Charge.php +++ b/src/Storage/Queries/Charge.php @@ -61,7 +61,7 @@ public function getByReferenceAndShopId(ChargeReference $chargeRef, ShopId $shop { return $this->chargeModel->query() ->where('charge_id', $chargeRef->toNative()) - ->where(Util::getShopsTable(true), $shopId->toNative()) + ->where(Util::getShopsTableForeignKey(), $shopId->toNative()) ->get() ->first(); } diff --git a/src/Util.php b/src/Util.php index 240669a7..5a966328 100644 --- a/src/Util.php +++ b/src/Util.php @@ -212,20 +212,22 @@ public static function getGraphQLWebhookTopic(string $topic): string /** - * Get the table name / table foreign_id for shop - * - * @param bool $foreignId + * Get the table name for shop * * @return string */ - public static function getShopsTable(bool $foreignId = false): string + public static function getShopsTable(): string { - $shopTable = Util::getShopifyConfig('table_names.shops') ?? 'users'; - - if ($foreignId) { - return Str::singular($shopTable) . '_id' ; - } + return Util::getShopifyConfig('table_names.shops') ?? 'users'; + } - return $shopTable; + /** + * Get the table foreign key for shop + * + * @return string + */ + public static function getShopsTableForeignKey(): string + { + return Str::singular(self::getShopsTable()) . '_id'; } } diff --git a/src/resources/database/migrations/2020_01_29_231006_create_charges_table.php b/src/resources/database/migrations/2020_01_29_231006_create_charges_table.php index 9f1b9128..74da5107 100644 --- a/src/resources/database/migrations/2020_01_29_231006_create_charges_table.php +++ b/src/resources/database/migrations/2020_01_29_231006_create_charges_table.php @@ -79,13 +79,13 @@ public function up() $table->softDeletes(); if ($this->getLaravelVersion() < 5.8) { - $table->integer(Util::getShopsTable(true))->unsigned(); + $table->integer(Util::getShopsTableForeignKey())->unsigned(); } else { - $table->bigInteger(Util::getShopsTable(true))->unsigned(); + $table->bigInteger(Util::getShopsTableForeignKey())->unsigned(); } // Linking - $table->foreign(Util::getShopsTable(true))->references('id')->on(Util::getShopsTable())->onDelete('cascade'); + $table->foreign(Util::getShopsTableForeignKey())->references('id')->on(Util::getShopsTable())->onDelete('cascade'); $table->foreign('plan_id')->references('id')->on(Util::getShopifyConfig('table_names.plans', 'plans')); }); } From c099191c3155274bff07e3bac9751e0529fa1a80 Mon Sep 17 00:00:00 2001 From: Nahid Bin Azhar Date: Fri, 9 Sep 2022 18:52:06 +0600 Subject: [PATCH 05/31] Remove unwanted new line feom Charge model contractor --- src/Storage/Models/Charge.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Storage/Models/Charge.php b/src/Storage/Models/Charge.php index d48c2e72..4908608c 100644 --- a/src/Storage/Models/Charge.php +++ b/src/Storage/Models/Charge.php @@ -53,8 +53,6 @@ public function __construct(array $attributes = []) $this->fillable[] = Util::getShopsTableForeignKey(); parent::__construct($attributes); - - } /** From 41c87341caa20ef3d08ffaa8a2850928dd5ca31a Mon Sep 17 00:00:00 2001 From: Nahid Bin Azhar Date: Fri, 9 Sep 2022 20:17:51 +0600 Subject: [PATCH 06/31] auto fixes code style from php-cs-fixer --- src/Util.php | 4 ++-- .../migrations/2020_01_29_230905_create_shops_table.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Util.php b/src/Util.php index 5a966328..b468e0f4 100644 --- a/src/Util.php +++ b/src/Util.php @@ -218,7 +218,7 @@ public static function getGraphQLWebhookTopic(string $topic): string */ public static function getShopsTable(): string { - return Util::getShopifyConfig('table_names.shops') ?? 'users'; + return self::getShopifyConfig('table_names.shops') ?? 'users'; } /** @@ -228,6 +228,6 @@ public static function getShopsTable(): string */ public static function getShopsTableForeignKey(): string { - return Str::singular(self::getShopsTable()) . '_id'; + return Str::singular(self::getShopsTable()).'_id'; } } diff --git a/src/resources/database/migrations/2020_01_29_230905_create_shops_table.php b/src/resources/database/migrations/2020_01_29_230905_create_shops_table.php index cd7cc26b..c87f0b16 100644 --- a/src/resources/database/migrations/2020_01_29_230905_create_shops_table.php +++ b/src/resources/database/migrations/2020_01_29_230905_create_shops_table.php @@ -36,7 +36,7 @@ public function up(): void public function down(): void { Schema::table(Util::getShopsTable(), function (Blueprint $table) { - $table->dropForeign(Util::getShopsTable() . '_plan_id_foreign'); + $table->dropForeign(Util::getShopsTable().'_plan_id_foreign'); $table->dropColumn([ 'shopify_grandfathered', 'shopify_namespace', From 5ddf28b53031c1ac48979f7489bf18e19e1a810d Mon Sep 17 00:00:00 2001 From: Nahid Bin Azhar Date: Fri, 9 Sep 2022 21:42:37 +0600 Subject: [PATCH 07/31] added name, email and password in create shops table migration if its missing current shops table --- .../2020_01_29_230905_create_shops_table.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/resources/database/migrations/2020_01_29_230905_create_shops_table.php b/src/resources/database/migrations/2020_01_29_230905_create_shops_table.php index c87f0b16..05631fdc 100644 --- a/src/resources/database/migrations/2020_01_29_230905_create_shops_table.php +++ b/src/resources/database/migrations/2020_01_29_230905_create_shops_table.php @@ -24,6 +24,18 @@ public function up(): void $table->softDeletes(); } + if (! Schema::hasColumn(Util::getShopsTable(), 'name')) { + $table->string('name')->nullable(); + } + + if (! Schema::hasColumn(Util::getShopsTable(), 'email')) { + $table->string('email')->nullable(); + } + + if (! Schema::hasColumn(Util::getShopsTable(), 'password')) { + $table->string('password', 100)->nullable(); + } + $table->foreign('plan_id')->references('id')->on(Util::getShopifyConfig('table_names.plans', 'plans')); }); } @@ -38,6 +50,9 @@ public function down(): void Schema::table(Util::getShopsTable(), function (Blueprint $table) { $table->dropForeign(Util::getShopsTable().'_plan_id_foreign'); $table->dropColumn([ + 'name', + 'email', + 'password', 'shopify_grandfathered', 'shopify_namespace', 'shopify_freemium', From 79ff4bbca2bb64edd54be3811444d5046b827e5f Mon Sep 17 00:00:00 2001 From: Nahid Bin Azhar Date: Tue, 27 Sep 2022 12:01:18 +0600 Subject: [PATCH 08/31] fixed get data from request issue in StoreUsageCharge --- src/Http/Requests/StoreUsageCharge.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Http/Requests/StoreUsageCharge.php b/src/Http/Requests/StoreUsageCharge.php index 45d10c59..b4b1f25b 100644 --- a/src/Http/Requests/StoreUsageCharge.php +++ b/src/Http/Requests/StoreUsageCharge.php @@ -35,12 +35,12 @@ public function withValidator(Validator $validator): void $validator->after(function (Validator $validator) { // Get the input values needed $data = [ - 'price' => $this->request->get('price'), - 'description' => $this->request->get('description'), - 'signature' => $this->request->get('signature'), + 'price' => $this->get('price'), + 'description' => $this->get('description'), + 'signature' => $this->get('signature'), ]; - if ($this->request->has('redirect')) { - $data['redirect'] = $this->request->get('redirect'); + if ($this->has('redirect')) { + $data['redirect'] = $this->get('redirect'); } $signature = Hmac::fromNative($data['signature']); From 663fe8598add4a66e5c13206e59e1770649f1816 Mon Sep 17 00:00:00 2001 From: Nahid Bin Azhar Date: Mon, 28 Nov 2022 20:00:47 +0600 Subject: [PATCH 09/31] feat(event, listener): added various events, update Util --- src/Actions/ActivatePlan.php | 3 ++ src/Actions/AuthenticateShop.php | 12 ++++- src/Messaging/Events/AppInstalledEvent.php | 36 +++++++++++++ src/Messaging/Events/AppUninstalledEvent.php | 35 ++++++++++++ src/Messaging/Events/PlanActivatedEvent.php | 54 +++++++++++++++++++ .../Events/ShopAuthenticatedEvent.php | 36 +++++++++++++ src/Messaging/Events/ShopDeletedEvent.php | 35 ++++++++++++ src/Messaging/Jobs/AppUninstalledJob.php | 3 ++ src/ShopifyAppProvider.php | 17 ++++++ src/Traits/AuthController.php | 3 ++ src/Traits/ShopModel.php | 5 ++ src/Util.php | 7 +++ src/resources/config/shopify-app.php | 41 ++++++++++++++ 13 files changed, 286 insertions(+), 1 deletion(-) create mode 100644 src/Messaging/Events/AppInstalledEvent.php create mode 100644 src/Messaging/Events/AppUninstalledEvent.php create mode 100644 src/Messaging/Events/PlanActivatedEvent.php create mode 100644 src/Messaging/Events/ShopAuthenticatedEvent.php create mode 100644 src/Messaging/Events/ShopDeletedEvent.php diff --git a/src/Actions/ActivatePlan.php b/src/Actions/ActivatePlan.php index a91e9e02..ec000810 100644 --- a/src/Actions/ActivatePlan.php +++ b/src/Actions/ActivatePlan.php @@ -8,6 +8,7 @@ use Osiset\ShopifyApp\Contracts\Objects\Values\PlanId; use Osiset\ShopifyApp\Contracts\Queries\Plan as IPlanQuery; use Osiset\ShopifyApp\Contracts\Queries\Shop as IShopQuery; +use Osiset\ShopifyApp\Messaging\Events\PlanActivatedEvent; use Osiset\ShopifyApp\Objects\Enums\ChargeStatus; use Osiset\ShopifyApp\Objects\Enums\ChargeType; use Osiset\ShopifyApp\Objects\Enums\PlanType; @@ -142,6 +143,8 @@ public function __invoke(ShopId $shopId, PlanId $planId, ChargeReference $charge $charge = $this->chargeCommand->make($transfer); $this->shopCommand->setToPlan($shopId, $planId); + event(new PlanActivatedEvent($shop, $plan, $charge)); + return $charge; } } diff --git a/src/Actions/AuthenticateShop.php b/src/Actions/AuthenticateShop.php index 248bcfde..2850f39c 100644 --- a/src/Actions/AuthenticateShop.php +++ b/src/Actions/AuthenticateShop.php @@ -4,7 +4,9 @@ use Illuminate\Http\Request; use Osiset\ShopifyApp\Contracts\ApiHelper as IApiHelper; +use Osiset\ShopifyApp\Messaging\Events\AppInstalledEvent; use Osiset\ShopifyApp\Objects\Values\ShopDomain; +use Osiset\ShopifyApp\Util; /** * Authenticates a shop and fires post authentication actions. @@ -103,7 +105,15 @@ public function __invoke(Request $request): array // Fire the post processing jobs call_user_func($this->dispatchScriptsAction, $result['shop_id'], false); call_user_func($this->dispatchWebhooksAction, $result['shop_id'], false); - call_user_func($this->afterAuthorizeAction, $result['shop_id']); + + if (Util::hasAppLegacySupport('after_authenticate_job')) { + call_user_func($this->afterAuthorizeAction, $result['shop_id']); + } + + if (!Util::hasAppLegacySupport('after_authenticate_job')) { + event(new AppInstalledEvent($result['shop_id'])); + } + return [$result, true]; } diff --git a/src/Messaging/Events/AppInstalledEvent.php b/src/Messaging/Events/AppInstalledEvent.php new file mode 100644 index 00000000..e0c9e05a --- /dev/null +++ b/src/Messaging/Events/AppInstalledEvent.php @@ -0,0 +1,36 @@ +shopId = $shop_id; + } +} diff --git a/src/Messaging/Events/AppUninstalledEvent.php b/src/Messaging/Events/AppUninstalledEvent.php new file mode 100644 index 00000000..042549ee --- /dev/null +++ b/src/Messaging/Events/AppUninstalledEvent.php @@ -0,0 +1,35 @@ +shop = $shop; + } +} diff --git a/src/Messaging/Events/PlanActivatedEvent.php b/src/Messaging/Events/PlanActivatedEvent.php new file mode 100644 index 00000000..214fe74b --- /dev/null +++ b/src/Messaging/Events/PlanActivatedEvent.php @@ -0,0 +1,54 @@ +shop = $shop; + $this->plan = $plan; + $this->chargeId = $chargeId; + } + +} diff --git a/src/Messaging/Events/ShopAuthenticatedEvent.php b/src/Messaging/Events/ShopAuthenticatedEvent.php new file mode 100644 index 00000000..e1ced9aa --- /dev/null +++ b/src/Messaging/Events/ShopAuthenticatedEvent.php @@ -0,0 +1,36 @@ +shopId = $shop_id; + } +} diff --git a/src/Messaging/Events/ShopDeletedEvent.php b/src/Messaging/Events/ShopDeletedEvent.php new file mode 100644 index 00000000..1d81cb54 --- /dev/null +++ b/src/Messaging/Events/ShopDeletedEvent.php @@ -0,0 +1,35 @@ +shop = $shop; + } +} diff --git a/src/Messaging/Jobs/AppUninstalledJob.php b/src/Messaging/Jobs/AppUninstalledJob.php index c973e69c..047758a6 100644 --- a/src/Messaging/Jobs/AppUninstalledJob.php +++ b/src/Messaging/Jobs/AppUninstalledJob.php @@ -10,6 +10,7 @@ use Osiset\ShopifyApp\Actions\CancelCurrentPlan; use Osiset\ShopifyApp\Contracts\Commands\Shop as IShopCommand; use Osiset\ShopifyApp\Contracts\Queries\Shop as IShopQuery; +use Osiset\ShopifyApp\Messaging\Events\AppUninstalledEvent; use Osiset\ShopifyApp\Objects\Values\ShopDomain; use Osiset\ShopifyApp\Util; use stdClass; @@ -89,6 +90,8 @@ public function handle( // Soft delete the shop. $shopCommand->softDelete($shopId); + event(new AppUninstalledEvent($shop)); + return true; } } diff --git a/src/ShopifyAppProvider.php b/src/ShopifyAppProvider.php index fdce960d..58d1c56e 100644 --- a/src/ShopifyAppProvider.php +++ b/src/ShopifyAppProvider.php @@ -5,6 +5,7 @@ use Illuminate\Routing\Redirector; use Illuminate\Routing\UrlGenerator; use Illuminate\Support\Facades\Blade; +use Illuminate\Support\Facades\Event; use Illuminate\Support\ServiceProvider; use Osiset\ShopifyApp\Actions\ActivatePlan as ActivatePlanAction; use Osiset\ShopifyApp\Actions\ActivateUsageCharge as ActivateUsageChargeAction; @@ -83,6 +84,10 @@ public function register() WebhookJobMakeCommand::class, ]); + $this->booting(function () { + $this->registerEvents(); + }); + // Services (start) $this->app->bind(IApiHelper::class, function () { return new ApiHelper(); @@ -332,4 +337,16 @@ private function bootDirectives(): void { Blade::directive('sessionToken', new SessionToken()); } + + private function registerEvents(): void + { + $events = Util::getShopifyConfig('listen'); + + foreach ($events as $event => $listeners) { + foreach (array_unique($listeners, SORT_REGULAR) as $listener) { + Event::listen($event, $listener); + } + } + + } } diff --git a/src/Traits/AuthController.php b/src/Traits/AuthController.php index df537b34..f1af6bd7 100644 --- a/src/Traits/AuthController.php +++ b/src/Traits/AuthController.php @@ -11,6 +11,7 @@ use Osiset\ShopifyApp\Exceptions\MissingAuthUrlException; use Osiset\ShopifyApp\Exceptions\MissingShopDomainException; use Osiset\ShopifyApp\Exceptions\SignatureVerificationException; +use Osiset\ShopifyApp\Messaging\Events\ShopAuthenticatedEvent; use Osiset\ShopifyApp\Objects\Values\ShopDomain; use Osiset\ShopifyApp\Util; @@ -57,6 +58,8 @@ public function authenticate(Request $request, AuthenticateShop $authShop) $shopDomain = $shopDomain->toNative(); $shopOrigin = $shopDomain ?? $request->user()->name; + event(new ShopAuthenticatedEvent($result['shop_id'])); + return View::make( 'shopify-app::auth.fullpage_redirect', [ diff --git a/src/Traits/ShopModel.php b/src/Traits/ShopModel.php index 6c9d26c6..a2972369 100644 --- a/src/Traits/ShopModel.php +++ b/src/Traits/ShopModel.php @@ -11,6 +11,7 @@ use Osiset\ShopifyApp\Contracts\Objects\Values\AccessToken as AccessTokenValue; use Osiset\ShopifyApp\Contracts\Objects\Values\ShopDomain as ShopDomainValue; use Osiset\ShopifyApp\Contracts\Objects\Values\ShopId as ShopIdValue; +use Osiset\ShopifyApp\Messaging\Events\ShopDeletedEvent; use Osiset\ShopifyApp\Objects\Values\AccessToken; use Osiset\ShopifyApp\Objects\Values\SessionContext; use Osiset\ShopifyApp\Objects\Values\ShopDomain; @@ -51,6 +52,10 @@ trait ShopModel protected static function bootShopModel(): void { static::addGlobalScope(new Namespacing()); + + static::deleted(function ($shop) { + event(new ShopDeletedEvent($shop)); + }); } /** diff --git a/src/Util.php b/src/Util.php index 67a5a3e1..fe61b7e8 100644 --- a/src/Util.php +++ b/src/Util.php @@ -246,4 +246,11 @@ public static function useNativeAppBridge(): bool return !$frontendEngine->isSame($reactEngine); } + + public static function hasAppLegacySupport(string $feature): bool + { + $legacySupports = self::getShopifyConfig('app_legacy_supports') ?? []; + + return (bool) Arr::get($legacySupports, $feature, true); + } } diff --git a/src/resources/config/shopify-app.php b/src/resources/config/shopify-app.php index d2dec331..04b04fdc 100644 --- a/src/resources/config/shopify-app.php +++ b/src/resources/config/shopify-app.php @@ -326,6 +326,42 @@ 'billing_redirect' => env('SHOPIFY_BILLING_REDIRECT', '/billing/process'), + + /* + |-------------------------------------------------------------------------- + | Enable legacy support for features + |-------------------------------------------------------------------------- + | + */ + 'app_legacy_supports' => [ + 'after_authenticate_job' => true, + ], + + /* + |-------------------------------------------------------------------------- + | Register listeners to the events + |-------------------------------------------------------------------------- + | + */ + + 'listen' => [ + \Osiset\ShopifyApp\Messaging\Events\AppInstalledEvent::class => [ + // \App\Listeners\MyListener::class, + ], + \Osiset\ShopifyApp\Messaging\Events\ShopAuthenticatedEvent::class => [ + // \App\Listeners\MyListener::class, + ], + \Osiset\ShopifyApp\Messaging\Events\ShopDeletedEvent::class => [ + // \App\Listeners\MyListener::class, + ], + \Osiset\ShopifyApp\Messaging\Events\AppUninstalledEvent::class => [ + // \App\Listeners\MyListener::class, + ], + \Osiset\ShopifyApp\Messaging\Events\PlanActivatedEvent::class => [ + // \App\Listeners\MyListener::class, + ], + ], + /* |-------------------------------------------------------------------------- | Shopify Webhooks @@ -382,8 +418,13 @@ | This, like webhooks and scripttag jobs, will fire every time a shop | authenticates, not just once. | + | */ + /** + * @deprecated This will be removed in the next major version. + * @see + */ 'after_authenticate_job' => [ /* [ From fb007eb010847668e9175b0afb473bc3f9f4914a Mon Sep 17 00:00:00 2001 From: Nahid Bin Azhar Date: Mon, 28 Nov 2022 21:55:15 +0600 Subject: [PATCH 10/31] fixed by php-cs-fixer --- src/Messaging/Events/AppInstalledEvent.php | 1 - src/Messaging/Events/PlanActivatedEvent.php | 1 - .../Events/ShopAuthenticatedEvent.php | 1 - src/ShopifyAppProvider.php | 1 - src/resources/config/shopify-app.php | 18 +++++++++--------- 5 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/Messaging/Events/AppInstalledEvent.php b/src/Messaging/Events/AppInstalledEvent.php index e0c9e05a..b8029bb1 100644 --- a/src/Messaging/Events/AppInstalledEvent.php +++ b/src/Messaging/Events/AppInstalledEvent.php @@ -4,7 +4,6 @@ use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Queue\SerializesModels; -use Osiset\ShopifyApp\Contracts\ShopModel as IShopModel; use Osiset\ShopifyApp\Objects\Values\ShopId; /** diff --git a/src/Messaging/Events/PlanActivatedEvent.php b/src/Messaging/Events/PlanActivatedEvent.php index 214fe74b..15e387a6 100644 --- a/src/Messaging/Events/PlanActivatedEvent.php +++ b/src/Messaging/Events/PlanActivatedEvent.php @@ -50,5 +50,4 @@ public function __construct(IShopModel $shop, Model $plan, ChargeId $chargeId) $this->plan = $plan; $this->chargeId = $chargeId; } - } diff --git a/src/Messaging/Events/ShopAuthenticatedEvent.php b/src/Messaging/Events/ShopAuthenticatedEvent.php index e1ced9aa..a7a4e209 100644 --- a/src/Messaging/Events/ShopAuthenticatedEvent.php +++ b/src/Messaging/Events/ShopAuthenticatedEvent.php @@ -4,7 +4,6 @@ use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Queue\SerializesModels; -use Osiset\ShopifyApp\Contracts\ShopModel as IShopModel; use Osiset\ShopifyApp\Objects\Values\ShopId; /** diff --git a/src/ShopifyAppProvider.php b/src/ShopifyAppProvider.php index 58d1c56e..1402c24d 100644 --- a/src/ShopifyAppProvider.php +++ b/src/ShopifyAppProvider.php @@ -347,6 +347,5 @@ private function registerEvents(): void Event::listen($event, $listener); } } - } } diff --git a/src/resources/config/shopify-app.php b/src/resources/config/shopify-app.php index 04b04fdc..61cdc78f 100644 --- a/src/resources/config/shopify-app.php +++ b/src/resources/config/shopify-app.php @@ -56,13 +56,13 @@ */ 'route_names' => [ - 'home' => env('SHOPIFY_ROUTE_NAME_HOME', 'home'), - 'authenticate' => env('SHOPIFY_ROUTE_NAME_AUTHENTICATE', 'authenticate'), - 'authenticate.token' => env('SHOPIFY_ROUTE_NAME_AUTHENTICATE_TOKEN', 'authenticate.token'), - 'billing' => env('SHOPIFY_ROUTE_NAME_BILLING', 'billing'), - 'billing.process' => env('SHOPIFY_ROUTE_NAME_BILLING_PROCESS', 'billing.process'), + 'home' => env('SHOPIFY_ROUTE_NAME_HOME', 'home'), + 'authenticate' => env('SHOPIFY_ROUTE_NAME_AUTHENTICATE', 'authenticate'), + 'authenticate.token' => env('SHOPIFY_ROUTE_NAME_AUTHENTICATE_TOKEN', 'authenticate.token'), + 'billing' => env('SHOPIFY_ROUTE_NAME_BILLING', 'billing'), + 'billing.process' => env('SHOPIFY_ROUTE_NAME_BILLING_PROCESS', 'billing.process'), 'billing.usage_charge' => env('SHOPIFY_ROUTE_NAME_BILLING_USAGE_CHARGE', 'billing.usage_charge'), - 'webhook' => env('SHOPIFY_ROUTE_NAME_WEBHOOK', 'webhook'), + 'webhook' => env('SHOPIFY_ROUTE_NAME_WEBHOOK', 'webhook'), ], /* @@ -421,7 +421,7 @@ | */ - /** + /* * @deprecated This will be removed in the next major version. * @see */ @@ -445,8 +445,8 @@ */ 'job_queues' => [ - 'webhooks' => env('WEBHOOKS_JOB_QUEUE', null), - 'scripttags' => env('SCRIPTTAGS_JOB_QUEUE', null), + 'webhooks' => env('WEBHOOKS_JOB_QUEUE', null), + 'scripttags' => env('SCRIPTTAGS_JOB_QUEUE', null), 'after_authenticate' => env('AFTER_AUTHENTICATE_JOB_QUEUE', null), ], From 94cc3f4479d0dd9eceed65e6b8fcfbe30a08b643 Mon Sep 17 00:00:00 2001 From: Nahid Bin Azhar Date: Mon, 28 Nov 2022 22:18:42 +0600 Subject: [PATCH 11/31] fixed: support for php 7.2 --- src/Messaging/Events/AppInstalledEvent.php | 2 +- src/Messaging/Events/AppUninstalledEvent.php | 2 +- src/Messaging/Events/PlanActivatedEvent.php | 6 +++--- src/Messaging/Events/ShopAuthenticatedEvent.php | 2 +- src/Messaging/Events/ShopDeletedEvent.php | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Messaging/Events/AppInstalledEvent.php b/src/Messaging/Events/AppInstalledEvent.php index b8029bb1..cef4e658 100644 --- a/src/Messaging/Events/AppInstalledEvent.php +++ b/src/Messaging/Events/AppInstalledEvent.php @@ -19,7 +19,7 @@ class AppInstalledEvent * * @var ShopId */ - public ShopId $shopId; + public $shopId; /** * Create a new event instance. diff --git a/src/Messaging/Events/AppUninstalledEvent.php b/src/Messaging/Events/AppUninstalledEvent.php index 042549ee..cb02e57b 100644 --- a/src/Messaging/Events/AppUninstalledEvent.php +++ b/src/Messaging/Events/AppUninstalledEvent.php @@ -19,7 +19,7 @@ class AppUninstalledEvent * * @var IShopModel */ - public IShopModel $shop; + public $shop; /** * Create a new event instance. diff --git a/src/Messaging/Events/PlanActivatedEvent.php b/src/Messaging/Events/PlanActivatedEvent.php index 15e387a6..8e3b8180 100644 --- a/src/Messaging/Events/PlanActivatedEvent.php +++ b/src/Messaging/Events/PlanActivatedEvent.php @@ -21,21 +21,21 @@ class PlanActivatedEvent * * @var IShopModel */ - public IShopModel $shop; + public $shop; /** * Plan's instance. * * @var Model */ - public Model $plan; + public $plan; /** * Charge ID * * @var ChargeId */ - public ChargeId $chargeId; + public $chargeId; /** * Create a new event instance. diff --git a/src/Messaging/Events/ShopAuthenticatedEvent.php b/src/Messaging/Events/ShopAuthenticatedEvent.php index a7a4e209..1a1c7b3e 100644 --- a/src/Messaging/Events/ShopAuthenticatedEvent.php +++ b/src/Messaging/Events/ShopAuthenticatedEvent.php @@ -19,7 +19,7 @@ class ShopAuthenticatedEvent * * @var ShopId */ - public ShopId $shopId; + public $shopId; /** * Create a new event instance. diff --git a/src/Messaging/Events/ShopDeletedEvent.php b/src/Messaging/Events/ShopDeletedEvent.php index 1d81cb54..f901dd1f 100644 --- a/src/Messaging/Events/ShopDeletedEvent.php +++ b/src/Messaging/Events/ShopDeletedEvent.php @@ -19,7 +19,7 @@ class ShopDeletedEvent * * @var IShopModel */ - public IShopModel $shop; + public $shop; /** * Create a new event instance. From 7b8048a926d786d68332eb6b0129a7cd5e0eb3ca Mon Sep 17 00:00:00 2001 From: Nahid Bin Azhar Date: Tue, 29 Nov 2022 12:39:00 +0600 Subject: [PATCH 12/31] fixed(ServiceProvider): added laravel version compatibility in service provider --- src/ShopifyAppProvider.php | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/ShopifyAppProvider.php b/src/ShopifyAppProvider.php index 1402c24d..0775ff77 100644 --- a/src/ShopifyAppProvider.php +++ b/src/ShopifyAppProvider.php @@ -68,6 +68,10 @@ public function boot() $this->bootMiddlewares(); $this->bootMacros(); $this->bootDirectives(); + + if (version_compare($this->app->version(), '8.0.0', '<')) { + $this->registerEvents(); + } } /** @@ -84,9 +88,12 @@ public function register() WebhookJobMakeCommand::class, ]); - $this->booting(function () { - $this->registerEvents(); - }); + if (version_compare($this->app->version(), '8.0.0', '>=')) { + $this->booting(function () { + $this->registerEvents(); + }); + } + // Services (start) $this->app->bind(IApiHelper::class, function () { From d72ea66c9e087e6a6ce87ecdaeaa409d15090a31 Mon Sep 17 00:00:00 2001 From: Nahid Bin Azhar Date: Tue, 29 Nov 2022 12:52:06 +0600 Subject: [PATCH 13/31] fixed(Request Validation): update StoreUsageChare request --- src/Http/Requests/StoreUsageCharge.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Http/Requests/StoreUsageCharge.php b/src/Http/Requests/StoreUsageCharge.php index b4b1f25b..45d10c59 100644 --- a/src/Http/Requests/StoreUsageCharge.php +++ b/src/Http/Requests/StoreUsageCharge.php @@ -35,12 +35,12 @@ public function withValidator(Validator $validator): void $validator->after(function (Validator $validator) { // Get the input values needed $data = [ - 'price' => $this->get('price'), - 'description' => $this->get('description'), - 'signature' => $this->get('signature'), + 'price' => $this->request->get('price'), + 'description' => $this->request->get('description'), + 'signature' => $this->request->get('signature'), ]; - if ($this->has('redirect')) { - $data['redirect'] = $this->get('redirect'); + if ($this->request->has('redirect')) { + $data['redirect'] = $this->request->get('redirect'); } $signature = Hmac::fromNative($data['signature']); From 6b6168230af3eb1fbeb16eb6e32b7858a110428f Mon Sep 17 00:00:00 2001 From: Luke Walsh <32519106+Kyon147@users.noreply.github.com> Date: Wed, 30 Nov 2022 11:01:02 +0000 Subject: [PATCH 14/31] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 30a43a26..7dddecb6 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@ **Please read [this announcement](https://github.com/osiset/laravel-shopify/discussions/1276).** +@kyon147 is going to maintain a version which you can find here https://github.com/Kyon147/laravel-shopify + ---- A full-featured Laravel package for aiding in Shopify App development, similar to `shopify_app` for Rails. Works for Laravel 7 and up. From de8a415acd6bdd5f72572e1fa96c916b584ce8c9 Mon Sep 17 00:00:00 2001 From: Nahid Bin Azhar Date: Tue, 13 Dec 2022 10:49:21 +0600 Subject: [PATCH 15/31] fix: cs-fix changes --- src/Util.php | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Util.php b/src/Util.php index fe61b7e8..b2f03765 100644 --- a/src/Util.php +++ b/src/Util.php @@ -17,7 +17,7 @@ class Util /** * HMAC creation helper. * - * @param array $opts The options for building the HMAC. + * @param array $opts The options for building the HMAC. * @param string $secret The app secret key. * * @return Hmac @@ -36,7 +36,7 @@ public static function createHmac(array $opts, string $secret): Hmac ksort($data); $queryCompiled = []; foreach ($data as $key => $value) { - $queryCompiled[] = "{$key}=".(is_array($value) ? implode(',', $value) : $value); + $queryCompiled[] = "{$key}=" . (is_array($value) ? implode(',', $value) : $value); } $data = implode( $buildQueryWithJoin ? '&' : '', @@ -61,7 +61,7 @@ public static function createHmac(array $opts, string $secret): Hmac * See: https://github.com/rack/rack/blob/f9ad97fd69a6b3616d0a99e6bedcfb9de2f81f6c/lib/rack/query_parser.rb#L36 * * @param string $queryString The query string. - * @param string|null $delimiter The delimiter. + * @param string|null $delimiter The delimiter. * * @return mixed */ @@ -72,12 +72,12 @@ public static function parseQueryString(string $queryString, string $delimiter = $params = []; $split = preg_split( - $delimiter ? $commonSeparator[$delimiter] || '/['.$delimiter.']\s*/' : $defaultSeparator, + $delimiter ? $commonSeparator[$delimiter] || '/[' . $delimiter . ']\s*/' : $defaultSeparator, $queryString ?? '' ); foreach ($split as $part) { - if (! $part) { + if (!$part) { continue; } @@ -135,7 +135,7 @@ public static function base64UrlDecode($data) /** * Checks if the route should be registered or not. * - * @param string $routeToCheck The route name to check. + * @param string $routeToCheck The route name to check. * @param bool|array $routesToExclude The routes which are to be excluded. * * @return bool @@ -158,8 +158,8 @@ public static function registerPackageRoute(string $routeToCheck, $routesToExclu * Used as a helper function so it is accessible in Blade. * The second param of `shop` is important for `config_api_callback`. * - * @param string $key The key to lookup. - * @param mixed $shop The shop domain (string, ShopDomain, etc). + * @param string $key The key to lookup. + * @param mixed $shop The shop domain (string, ShopDomain, etc). * * @return mixed */ @@ -207,8 +207,8 @@ public static function getShopifyConfig(string $key, $shop = null) public static function getGraphQLWebhookTopic(string $topic): string { return Str::of($topic) - ->upper() - ->replaceMatches('/[^A-Z_]/', '_'); + ->upper() + ->replaceMatches('/[^A-Z_]/', '_'); } @@ -229,7 +229,7 @@ public static function getShopsTable(): string */ public static function getShopsTableForeignKey(): string { - return Str::singular(self::getShopsTable()).'_id'; + return Str::singular(self::getShopsTable()) . '_id'; } /** @@ -251,6 +251,6 @@ public static function hasAppLegacySupport(string $feature): bool { $legacySupports = self::getShopifyConfig('app_legacy_supports') ?? []; - return (bool) Arr::get($legacySupports, $feature, true); + return (bool)Arr::get($legacySupports, $feature, true); } } From 1f9a9d0f8dbcad69a3e2f8263d246632ccfa52a9 Mon Sep 17 00:00:00 2001 From: Nahid Bin Azhar Date: Tue, 13 Dec 2022 23:09:44 +0600 Subject: [PATCH 16/31] fixed iframe ancestors issue --- src/Http/Middleware/IframeProtection.php | 10 +++++++++- src/resources/config/shopify-app.php | 2 ++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Http/Middleware/IframeProtection.php b/src/Http/Middleware/IframeProtection.php index fb297530..7e07968a 100644 --- a/src/Http/Middleware/IframeProtection.php +++ b/src/Http/Middleware/IframeProtection.php @@ -7,6 +7,7 @@ use Illuminate\Support\Facades\Cache; use Osiset\ShopifyApp\Contracts\Queries\Shop as IShopQuery; use Osiset\ShopifyApp\Objects\Values\ShopDomain; +use Osiset\ShopifyApp\Util; /** * Responsibility for protection against clickjaking @@ -44,6 +45,7 @@ public function __construct( public function handle(Request $request, Closure $next) { $response = $next($request); + $ancestors = Util::getShopifyConfig('iframe_ancestors'); $shop = Cache::remember( 'frame-ancestors_'.$request->get('shop'), @@ -57,9 +59,15 @@ function () use ($request) { ? $shop->name : '*.myshopify.com'; + $iframeAncestors = "frame-ancestors admin.shopify.com $domain"; + + if (!blank($ancestors)) { + $iframeAncestors .= ' ' . $ancestors; + } + $response->headers->set( 'Content-Security-Policy', - "frame-ancestors https://$domain https://admin.shopify.com" + $iframeAncestors ); return $response; diff --git a/src/resources/config/shopify-app.php b/src/resources/config/shopify-app.php index 61cdc78f..62c06308 100644 --- a/src/resources/config/shopify-app.php +++ b/src/resources/config/shopify-app.php @@ -531,4 +531,6 @@ | */ 'frontend_engine' => env('SHOPIFY_FRONTEND_ENGINE', 'BLADE'), + + 'iframe_ancestors' => '', ]; From c38c3add1c2dfcab8eb7ff5406f496e308772415 Mon Sep 17 00:00:00 2001 From: Nahid Bin Azhar Date: Tue, 13 Dec 2022 23:56:41 +0600 Subject: [PATCH 17/31] fixed iframe ancestors issue --- src/Http/Middleware/IframeProtection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/Middleware/IframeProtection.php b/src/Http/Middleware/IframeProtection.php index 7e07968a..d14078b6 100644 --- a/src/Http/Middleware/IframeProtection.php +++ b/src/Http/Middleware/IframeProtection.php @@ -59,7 +59,7 @@ function () use ($request) { ? $shop->name : '*.myshopify.com'; - $iframeAncestors = "frame-ancestors admin.shopify.com $domain"; + $iframeAncestors = "frame-ancestors https://admin.shopify.com https://$domain"; if (!blank($ancestors)) { $iframeAncestors .= ' ' . $ancestors; From ead87dee24a28f1454c74d9b1764259e9dbb0b20 Mon Sep 17 00:00:00 2001 From: Nahid Bin Azhar Date: Thu, 15 Dec 2022 14:28:30 +0600 Subject: [PATCH 18/31] update verify shop middleware --- src/Http/Middleware/VerifyShopify.php | 35 ++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/Http/Middleware/VerifyShopify.php b/src/Http/Middleware/VerifyShopify.php index c312ffd5..ac00935c 100644 --- a/src/Http/Middleware/VerifyShopify.php +++ b/src/Http/Middleware/VerifyShopify.php @@ -102,9 +102,11 @@ public function handle(Request $request, Closure $next) } if (!Util::useNativeAppBridge()) { - $storeResult = !$this->isApiRequest($request) && $this->checkPreviousInstallation($request); + $shop = $this->getShopIfAlreadyInstalled($request); + $storeResult = !$this->isApiRequest($request) && $shop; if ($storeResult) { + $this->loginFromShop($shop); return $next($request); } } @@ -511,4 +513,35 @@ protected function checkPreviousInstallation(Request $request): bool return $shop && $shop->password && ! $shop->trashed(); } + + /** + * Get shop model if there is a store record in the database. + * + * @param Request $request The request object. + * + * @return ?ShopModel + */ + protected function getShopIfAlreadyInstalled(Request $request): ?ShopModel + { + $shop = $this->shopQuery->getByDomain(ShopDomain::fromRequest($request), [], true); + + return $shop && $shop->password && ! $shop->trashed() ? $shop : null; + } + + /** + * Login and validate store + * + * @param ShopModel $shop + * @return void + */ + protected function loginFromShop(ShopModel $shop): void + { + // Override auth guard + if (($guard = Util::getShopifyConfig('shop_auth_guard'))) { + $this->auth->setDefaultDriver($guard); + } + + // All is well, login the shop + $this->auth->login($shop); + } } From 368619892bcd5aa2e636210399df4fa341d03de7 Mon Sep 17 00:00:00 2001 From: Nahid Bin Azhar Date: Thu, 16 Feb 2023 23:55:43 +0600 Subject: [PATCH 19/31] feat(shopdomain): validate correct shop domain when authenticate --- src/Actions/InstallShop.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/Actions/InstallShop.php b/src/Actions/InstallShop.php index ea89ad77..948f3d7f 100644 --- a/src/Actions/InstallShop.php +++ b/src/Actions/InstallShop.php @@ -55,6 +55,14 @@ public function __construct( */ public function __invoke(ShopDomain $shopDomain, ?string $code): array { + + if (!$this->isValidShop($shopDomain)) { + return [ + 'completed' => false, + 'url' => null, + 'shop_id' => null, + ]; + } // Get the shop $shop = $this->shopQuery->getByDomain($shopDomain, [], true); if ($shop === null) { @@ -102,4 +110,13 @@ public function __invoke(ShopDomain $shopDomain, ?string $code): array ]; } } + + public function isValidShop(ShopDomain $shopDomain): bool + { + $regex = '/^[a-zA-Z0-9][a-zA-Z0-9\-]*.myshopify.com/'; + $isMatched = preg_match($regex, $shopDomain->toNative(), $matches, PREG_OFFSET_CAPTURE); + + return $isMatched === 1; + } + } From b21ae45b4dc70f8da5b9ba19bb64da1fa4c2ee27 Mon Sep 17 00:00:00 2001 From: Nahid Bin Azhar Date: Fri, 17 Feb 2023 11:45:48 +0600 Subject: [PATCH 20/31] change after authenticate job fire logic --- src/Actions/AuthenticateShop.php | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/Actions/AuthenticateShop.php b/src/Actions/AuthenticateShop.php index 2850f39c..5178348a 100644 --- a/src/Actions/AuthenticateShop.php +++ b/src/Actions/AuthenticateShop.php @@ -105,14 +105,9 @@ public function __invoke(Request $request): array // Fire the post processing jobs call_user_func($this->dispatchScriptsAction, $result['shop_id'], false); call_user_func($this->dispatchWebhooksAction, $result['shop_id'], false); + call_user_func($this->afterAuthorizeAction, $result['shop_id']); - if (Util::hasAppLegacySupport('after_authenticate_job')) { - call_user_func($this->afterAuthorizeAction, $result['shop_id']); - } - - if (!Util::hasAppLegacySupport('after_authenticate_job')) { - event(new AppInstalledEvent($result['shop_id'])); - } + event(new AppInstalledEvent($result['shop_id'])); return [$result, true]; From ceffdd3b5fb60f8a3cd7642b8ab54f6e0a509579 Mon Sep 17 00:00:00 2001 From: Nahid Bin Azhar Date: Mon, 27 Mar 2023 14:32:55 +0600 Subject: [PATCH 21/31] Update composer.json --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index f5feb1d3..9f7cc241 100644 --- a/composer.json +++ b/composer.json @@ -24,11 +24,11 @@ } ], "require": { - "php": ">=7.2", + "php": ">=7.4", "ext-json": "*", "funeralzone/valueobjects": "^0.5", "jenssegers/agent": "^2.6", - "laravel/framework": "^7.0 || ^8.0 || ^9.0", + "laravel/framework": "^9.0 || ^10.0", "osiset/basic-shopify-api": "^9.0 || <=10.0.5" }, "require-dev": { From f5090af3cbcf5dcfbe2c0959a12c945844245c3a Mon Sep 17 00:00:00 2001 From: Nahid Bin Azhar Date: Sun, 27 Aug 2023 16:34:11 +0600 Subject: [PATCH 22/31] update laravel shopify package for laravel 10 --- composer.json | 4 ++-- src/Actions/AfterAuthorize.php | 2 +- src/Actions/DispatchScripts.php | 2 +- src/Actions/DispatchWebhooks.php | 2 +- tests/Messaging/Jobs/AppUninstalledTest.php | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.json b/composer.json index 9f7cc241..e6e09b46 100644 --- a/composer.json +++ b/composer.json @@ -24,11 +24,11 @@ } ], "require": { - "php": ">=7.4", + "php": "^8.1", "ext-json": "*", "funeralzone/valueobjects": "^0.5", "jenssegers/agent": "^2.6", - "laravel/framework": "^9.0 || ^10.0", + "laravel/framework": "^10.0", "osiset/basic-shopify-api": "^9.0 || <=10.0.5" }, "require-dev": { diff --git a/src/Actions/AfterAuthorize.php b/src/Actions/AfterAuthorize.php index b682a091..4170c5ab 100644 --- a/src/Actions/AfterAuthorize.php +++ b/src/Actions/AfterAuthorize.php @@ -54,7 +54,7 @@ public function __invoke(ShopIdValue $shopId): bool $job = Arr::get($config, 'job'); if (Arr::get($config, 'inline', false)) { // Run this job immediately - $job::dispatchNow($shop); + $job::dispatchSync($shop); } else { // Run later $job::dispatch($shop) diff --git a/src/Actions/DispatchScripts.php b/src/Actions/DispatchScripts.php index 7afa0e0c..43b7ce40 100644 --- a/src/Actions/DispatchScripts.php +++ b/src/Actions/DispatchScripts.php @@ -61,7 +61,7 @@ public function __invoke(ShopIdValue $shopId, bool $inline = false): bool // Run the installer job if ($inline) { - ($this->jobClass)::dispatchNow( + ($this->jobClass)::dispatchSync( $shop->getId(), $scripttags ); diff --git a/src/Actions/DispatchWebhooks.php b/src/Actions/DispatchWebhooks.php index 1bea1130..ea00ca15 100644 --- a/src/Actions/DispatchWebhooks.php +++ b/src/Actions/DispatchWebhooks.php @@ -61,7 +61,7 @@ public function __invoke(ShopIdValue $shopId, bool $inline = false): bool // Run the installer job if ($inline) { - ($this->jobClass)::dispatchNow( + ($this->jobClass)::dispatchSync( $shop->getId(), $webhooks ); diff --git a/tests/Messaging/Jobs/AppUninstalledTest.php b/tests/Messaging/Jobs/AppUninstalledTest.php index e933636d..f4882360 100644 --- a/tests/Messaging/Jobs/AppUninstalledTest.php +++ b/tests/Messaging/Jobs/AppUninstalledTest.php @@ -33,7 +33,7 @@ public function testJobSoftDeletesShopAndCharges(): void $this->assertNotEmpty($shop->password); // Run the job - AppUninstalledJob::dispatchNow( + AppUninstalledJob::dispatchSync( $shop->getDomain()->toNative(), json_decode(file_get_contents(__DIR__.'/../../fixtures/app_uninstalled.json')) ); From f7360643d8f165db6a871fcb2b11a7841fb852fe Mon Sep 17 00:00:00 2001 From: Nahid Bin Azhar Date: Mon, 11 Sep 2023 14:38:34 +0600 Subject: [PATCH 23/31] php-cs-fix and update readme --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index c27f94cf..9bd4e096 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,6 @@ This is a maintained version of the wonderful but now deprecated original [Laravel Shopify App](https://github.com/gnikyt/laravel-shopify/). To keep things clean, this has been detached from the original. -@kyon147 is going to maintain a version which you can find here https://github.com/Kyon147/laravel-shopify - ---- A full-featured Laravel package for aiding in Shopify App development, similar to `shopify_app` for Rails. Works for Laravel 8 and up. From 6410733cac3c52fb4b6421182a130458c8715e73 Mon Sep 17 00:00:00 2001 From: Nahid Bin Azhar Date: Mon, 11 Sep 2023 14:49:13 +0600 Subject: [PATCH 24/31] code fix with php-cs-fix --- src/Actions/InstallShop.php | 2 -- src/Http/Middleware/IframeProtection.php | 4 ++-- src/Http/Middleware/VerifyShopify.php | 4 +++- src/Util.php | 8 ++++---- src/resources/config/shopify-app.php | 12 ++++++------ 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/Actions/InstallShop.php b/src/Actions/InstallShop.php index da56d41a..9f0a22b3 100644 --- a/src/Actions/InstallShop.php +++ b/src/Actions/InstallShop.php @@ -66,7 +66,6 @@ public function __construct( */ public function __invoke(ShopDomain $shopDomain, ?string $code): array { - if (!$this->isValidShop($shopDomain)) { return [ 'completed' => false, @@ -135,5 +134,4 @@ public function isValidShop(ShopDomain $shopDomain): bool return $isMatched === 1; } - } diff --git a/src/Http/Middleware/IframeProtection.php b/src/Http/Middleware/IframeProtection.php index d14078b6..e03bacd3 100644 --- a/src/Http/Middleware/IframeProtection.php +++ b/src/Http/Middleware/IframeProtection.php @@ -59,10 +59,10 @@ function () use ($request) { ? $shop->name : '*.myshopify.com'; - $iframeAncestors = "frame-ancestors https://admin.shopify.com https://$domain"; + $iframeAncestors = "frame-ancestors https://$domain https://admin.shopify.com"; if (!blank($ancestors)) { - $iframeAncestors .= ' ' . $ancestors; + $iframeAncestors .= ' '.$ancestors; } $response->headers->set( diff --git a/src/Http/Middleware/VerifyShopify.php b/src/Http/Middleware/VerifyShopify.php index 3650cb32..b7d43e60 100644 --- a/src/Http/Middleware/VerifyShopify.php +++ b/src/Http/Middleware/VerifyShopify.php @@ -83,7 +83,7 @@ public function __construct( * @param Request $request The request object. * @param Closure $next The next action. * - * @throws SignatureVerificationException If HMAC verification fails. + * @throws SignatureVerificationException|HttpException If HMAC verification fails. * * @return mixed */ @@ -107,6 +107,7 @@ public function handle(Request $request, Closure $next) if ($storeResult) { $this->loginFromShop($shop); + return $next($request); } } @@ -533,6 +534,7 @@ protected function getShopIfAlreadyInstalled(Request $request): ?ShopModel * Login and validate store * * @param ShopModel $shop + * * @return void */ protected function loginFromShop(ShopModel $shop): void diff --git a/src/Util.php b/src/Util.php index b2f03765..293bf7cb 100644 --- a/src/Util.php +++ b/src/Util.php @@ -36,7 +36,7 @@ public static function createHmac(array $opts, string $secret): Hmac ksort($data); $queryCompiled = []; foreach ($data as $key => $value) { - $queryCompiled[] = "{$key}=" . (is_array($value) ? implode(',', $value) : $value); + $queryCompiled[] = "{$key}=".(is_array($value) ? implode(',', $value) : $value); } $data = implode( $buildQueryWithJoin ? '&' : '', @@ -72,7 +72,7 @@ public static function parseQueryString(string $queryString, string $delimiter = $params = []; $split = preg_split( - $delimiter ? $commonSeparator[$delimiter] || '/[' . $delimiter . ']\s*/' : $defaultSeparator, + $delimiter ? $commonSeparator[$delimiter] || '/['.$delimiter.']\s*/' : $defaultSeparator, $queryString ?? '' ); @@ -229,7 +229,7 @@ public static function getShopsTable(): string */ public static function getShopsTableForeignKey(): string { - return Str::singular(self::getShopsTable()) . '_id'; + return Str::singular(self::getShopsTable()).'_id'; } /** @@ -251,6 +251,6 @@ public static function hasAppLegacySupport(string $feature): bool { $legacySupports = self::getShopifyConfig('app_legacy_supports') ?? []; - return (bool)Arr::get($legacySupports, $feature, true); + return (bool) Arr::get($legacySupports, $feature, true); } } diff --git a/src/resources/config/shopify-app.php b/src/resources/config/shopify-app.php index 0f1ab28b..cda82baf 100644 --- a/src/resources/config/shopify-app.php +++ b/src/resources/config/shopify-app.php @@ -539,26 +539,26 @@ */ 'theme_support' => [ - /** + /* * Specify the name of the template the app will integrate with */ 'templates' => ['product', 'collection', 'index'], - /** + /* * Interval for caching the request: minutes, seconds, hours, days, etc. */ 'cache_interval' => 'hours', - /** + /* * Cache duration */ 'cache_duration' => '12', - /** + /* * At which levels of theme support the use of "theme app extension" is not available * and script tags will be installed. * Available levels: FULL, PARTIAL, UNSUPPORTED. */ 'unacceptable_levels' => [ - Osiset\ShopifyApp\Objects\Enums\ThemeSupportLevel::UNSUPPORTED - ] + Osiset\ShopifyApp\Objects\Enums\ThemeSupportLevel::UNSUPPORTED, + ], ], /* From 64d097c8ad8ff6326e0a95e48326e7001ea58195 Mon Sep 17 00:00:00 2001 From: Nahid Bin Azhar Date: Wed, 13 Sep 2023 14:22:23 +0600 Subject: [PATCH 25/31] added tests --- .gitignore | 3 ++- .idea/laravel-shopify.iml | 8 ++++++ .idea/php.xml | 29 ++++++++++++++++++++- tests/Actions/ActivatePlanTest.php | 6 +++++ tests/Actions/AuthenticateShopTest.php | 4 +++ tests/Messaging/Jobs/AppUninstalledTest.php | 4 +++ tests/Traits/AuthControllerTest.php | 8 ++++++ tests/UtilTest.php | 11 ++++++++ 8 files changed, 71 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index caf3d41f..5d183a2d 100644 --- a/.gitignore +++ b/.gitignore @@ -38,7 +38,8 @@ composer.lock # since they will be recreated, and may cause churn. Uncomment if using # auto-import. # .idea/modules.xml -# .idea/*.iml + .idea/*.iml + .idea/*.xml # .idea/modules # CMake diff --git a/.idea/laravel-shopify.iml b/.idea/laravel-shopify.iml index f39d0417..b0819070 100644 --- a/.idea/laravel-shopify.iml +++ b/.idea/laravel-shopify.iml @@ -137,6 +137,14 @@ + + + + + + + + diff --git a/.idea/php.xml b/.idea/php.xml index f8b21236..0bf0b935 100644 --- a/.idea/php.xml +++ b/.idea/php.xml @@ -1,5 +1,17 @@ + + + + + + @@ -139,9 +151,17 @@ + + + + + + + + - + @@ -149,9 +169,16 @@ + + + + \ No newline at end of file diff --git a/tests/Actions/ActivatePlanTest.php b/tests/Actions/ActivatePlanTest.php index be6fb66f..97bb7c54 100644 --- a/tests/Actions/ActivatePlanTest.php +++ b/tests/Actions/ActivatePlanTest.php @@ -2,7 +2,9 @@ namespace Osiset\ShopifyApp\Test\Actions; +use Illuminate\Support\Facades\Event; use Osiset\ShopifyApp\Actions\ActivatePlan; +use Osiset\ShopifyApp\Messaging\Events\PlanActivatedEvent; use Osiset\ShopifyApp\Objects\Values\ChargeId; use Osiset\ShopifyApp\Objects\Values\ChargeReference; use Osiset\ShopifyApp\Storage\Models\Charge; @@ -27,6 +29,7 @@ public function setUp(): void public function testRunRecurring(): void { + Event::fake(); // Create a plan $plan = factory(Util::getShopifyConfig('models.plan', Plan::class))->states('type_recurring')->create(); @@ -56,10 +59,12 @@ public function testRunRecurring(): void ); $this->assertInstanceOf(ChargeId::class, $result); + Event::assertDispatched(PlanActivatedEvent::class); } public function testRunOnetime(): void { + Event::fake(); // Create a plan $plan = factory(Util::getShopifyConfig('models.plan', Plan::class))->states('type_onetime')->create(); @@ -89,6 +94,7 @@ public function testRunOnetime(): void ); $this->assertInstanceOf(ChargeId::class, $result); + Event::assertDispatched(PlanActivatedEvent::class); } //TODO we need to test for both myshopify and admin hosts diff --git a/tests/Actions/AuthenticateShopTest.php b/tests/Actions/AuthenticateShopTest.php index 49fcb4de..f5699be6 100644 --- a/tests/Actions/AuthenticateShopTest.php +++ b/tests/Actions/AuthenticateShopTest.php @@ -2,8 +2,10 @@ namespace Osiset\ShopifyApp\Test\Actions; +use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Request; use Osiset\ShopifyApp\Actions\AuthenticateShop; +use Osiset\ShopifyApp\Messaging\Events\AppInstalledEvent; use Osiset\ShopifyApp\Test\Stubs\Api as ApiStub; use Osiset\ShopifyApp\Test\TestCase; @@ -88,6 +90,7 @@ public function testShouldGoToAuthRedirectForInvalidHmac(): void public function testRuns(): void { + Event::fake(); // Build request $currentRequest = Request::instance(); $newRequest = $currentRequest->duplicate( @@ -121,5 +124,6 @@ public function testRuns(): void [, $status] = call_user_func($this->action, $newRequest); $this->assertTrue($status); + Event::assertDispatched(AppInstalledEvent::class); } } diff --git a/tests/Messaging/Jobs/AppUninstalledTest.php b/tests/Messaging/Jobs/AppUninstalledTest.php index f4882360..e4250563 100644 --- a/tests/Messaging/Jobs/AppUninstalledTest.php +++ b/tests/Messaging/Jobs/AppUninstalledTest.php @@ -2,6 +2,8 @@ namespace Osiset\ShopifyApp\Test\Messaging\Jobs; +use Illuminate\Support\Facades\Event; +use Osiset\ShopifyApp\Messaging\Events\AppUninstalledEvent; use Osiset\ShopifyApp\Messaging\Jobs\AppUninstalledJob; use Osiset\ShopifyApp\Objects\Enums\ChargeStatus; use Osiset\ShopifyApp\Storage\Models\Charge; @@ -32,6 +34,7 @@ public function testJobSoftDeletesShopAndCharges(): void $this->assertNotNull($shop->plan); $this->assertNotEmpty($shop->password); + Event::fake(); // Run the job AppUninstalledJob::dispatchSync( $shop->getDomain()->toNative(), @@ -46,5 +49,6 @@ public function testJobSoftDeletesShopAndCharges(): void $this->assertFalse($shop->hasCharges()); $this->assertNull($shop->plan); $this->assertEmpty($shop->password); + Event::assertDispatched(AppUninstalledEvent::class); } } diff --git a/tests/Traits/AuthControllerTest.php b/tests/Traits/AuthControllerTest.php index 9988ffe3..84e054e5 100644 --- a/tests/Traits/AuthControllerTest.php +++ b/tests/Traits/AuthControllerTest.php @@ -2,10 +2,15 @@ namespace Osiset\ShopifyApp\Test\Traits; +use Illuminate\Support\Facades\Request; use Illuminate\Http\Response; +use Illuminate\Support\Facades\Event; +use Osiset\ShopifyApp\Actions\AuthenticateShop; use Osiset\ShopifyApp\Exceptions\MissingShopDomainException; +use Osiset\ShopifyApp\Messaging\Events\ShopAuthenticatedEvent; use Osiset\ShopifyApp\Test\Stubs\Api as ApiStub; use Osiset\ShopifyApp\Test\TestCase; +use Osiset\ShopifyApp\Traits\AuthController; use Osiset\ShopifyApp\Util; class AuthControllerTest extends TestCase @@ -20,6 +25,7 @@ public function setUp(): void public function testAuthRedirectsToShopifyWhenNoCode(): void { + Event::fake(); // Run the request $response = $this->call('post', '/authenticate', ['shop' => 'example.myshopify.com']); @@ -29,6 +35,8 @@ public function testAuthRedirectsToShopifyWhenNoCode(): void 'authUrl', 'https://example.myshopify.com/admin/oauth/authorize?client_id='.Util::getShopifyConfig('api_key').'&scope=read_products%2Cwrite_products%2Cread_themes&redirect_uri=https%3A%2F%2Flocalhost%2Fauthenticate' ); + + Event::assertDispatched(ShopAuthenticatedEvent::class); } public function testAuthAcceptsShopWithCode(): void diff --git a/tests/UtilTest.php b/tests/UtilTest.php index 13cff9aa..e88ee5b8 100644 --- a/tests/UtilTest.php +++ b/tests/UtilTest.php @@ -123,4 +123,15 @@ public function testUseNativeAppBridgeIsFalse(): void $this->assertFalse($result); } + + public function testHasAppLegacySupport(): void + { + $supportedFeatures = $this->app['config']->get('shopify-app.app_legacy_supports', []); + foreach ($supportedFeatures as $feature => $val) { + $this->assertSame( + $val, + Util::hasAppLegacySupport($feature) + ); + } + } } From 8a665ea70543d23db869a907a05015e6174d62a6 Mon Sep 17 00:00:00 2001 From: Nahid Bin Azhar Date: Wed, 13 Sep 2023 14:23:57 +0600 Subject: [PATCH 26/31] rollback gitigonre --- .gitignore | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 5d183a2d..caf3d41f 100644 --- a/.gitignore +++ b/.gitignore @@ -38,8 +38,7 @@ composer.lock # since they will be recreated, and may cause churn. Uncomment if using # auto-import. # .idea/modules.xml - .idea/*.iml - .idea/*.xml +# .idea/*.iml # .idea/modules # CMake From 3b2507b56aac8e6ed35afaf2c70d715de6f377e6 Mon Sep 17 00:00:00 2001 From: Nahid Bin Azhar Date: Wed, 13 Sep 2023 14:26:12 +0600 Subject: [PATCH 27/31] fixed by php-cs-fixer --- tests/Traits/AuthControllerTest.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/Traits/AuthControllerTest.php b/tests/Traits/AuthControllerTest.php index 84e054e5..a435c7c9 100644 --- a/tests/Traits/AuthControllerTest.php +++ b/tests/Traits/AuthControllerTest.php @@ -2,15 +2,13 @@ namespace Osiset\ShopifyApp\Test\Traits; -use Illuminate\Support\Facades\Request; use Illuminate\Http\Response; use Illuminate\Support\Facades\Event; -use Osiset\ShopifyApp\Actions\AuthenticateShop; +use Illuminate\Support\Facades\Request; use Osiset\ShopifyApp\Exceptions\MissingShopDomainException; use Osiset\ShopifyApp\Messaging\Events\ShopAuthenticatedEvent; use Osiset\ShopifyApp\Test\Stubs\Api as ApiStub; use Osiset\ShopifyApp\Test\TestCase; -use Osiset\ShopifyApp\Traits\AuthController; use Osiset\ShopifyApp\Util; class AuthControllerTest extends TestCase From c71e073d93b8c90d3602bc3587964bd268b313a4 Mon Sep 17 00:00:00 2001 From: Nahid Bin Azhar Date: Wed, 13 Sep 2023 14:28:26 +0600 Subject: [PATCH 28/31] update phpunit.xml.dist for excluding Events --- phpunit.xml.dist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index ddbac182..9fc87217 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -23,7 +23,7 @@ src/Exceptions/ src/Objects/Enums/ src/resources/ - src/Messaging/Events/AppLoggedIn.php + src/Messaging/Events/ src/ShopifyAppProvider.php From 37ef397d9611c4b1fcafc052331b5889125c975c Mon Sep 17 00:00:00 2001 From: Nahid Bin Azhar Date: Wed, 13 Sep 2023 15:27:31 +0600 Subject: [PATCH 29/31] added tests for coverage --- src/Actions/InstallShop.php | 15 --------- .../Http/Middleware/IframeProtectionTest.php | 24 ++++++++++++++ tests/Http/Middleware/VerifyShopifyTest.php | 32 +++++++++++++++++++ 3 files changed, 56 insertions(+), 15 deletions(-) diff --git a/src/Actions/InstallShop.php b/src/Actions/InstallShop.php index 9f0a22b3..11360d02 100644 --- a/src/Actions/InstallShop.php +++ b/src/Actions/InstallShop.php @@ -66,13 +66,6 @@ public function __construct( */ public function __invoke(ShopDomain $shopDomain, ?string $code): array { - if (!$this->isValidShop($shopDomain)) { - return [ - 'completed' => false, - 'url' => null, - 'shop_id' => null, - ]; - } // Get the shop $shop = $this->shopQuery->getByDomain($shopDomain, [], true); @@ -126,12 +119,4 @@ public function __invoke(ShopDomain $shopDomain, ?string $code): array ]; } } - - public function isValidShop(ShopDomain $shopDomain): bool - { - $regex = '/^[a-zA-Z0-9][a-zA-Z0-9\-]*.myshopify.com/'; - $isMatched = preg_match($regex, $shopDomain->toNative(), $matches, PREG_OFFSET_CAPTURE); - - return $isMatched === 1; - } } diff --git a/tests/Http/Middleware/IframeProtectionTest.php b/tests/Http/Middleware/IframeProtectionTest.php index a02448db..14bba2d8 100644 --- a/tests/Http/Middleware/IframeProtectionTest.php +++ b/tests/Http/Middleware/IframeProtectionTest.php @@ -63,4 +63,28 @@ public function testIframeProtectionWithUnauthorizedShop(): void $this->assertNotEmpty($currentHeader); $this->assertEquals($expectedHeader, $currentHeader); } + + public function testIframeProtectionWithExistingAncestorsInConfig(): void + { + $shop = factory($this->model)->create(); + $this->auth->login($shop); + $this->app['config']->set('shopify-app.iframe_ancestors', 'https://example.com'); + + $domain = auth()->user()->name; + $expectedHeader = "frame-ancestors https://$domain https://admin.shopify.com https://example.com"; + + $request = new Request(); + $shopQueryStub = $this->createStub(ShopQuery::class); + $shopQueryStub->method('getByDomain')->willReturn($shop); + $next = function () { + return new Response('Test Response'); + }; + + $middleware = new IframeProtection($shopQueryStub); + $response = $middleware->handle($request, $next); + $currentHeader = $response->headers->get('content-security-policy'); + + $this->assertNotEmpty($currentHeader); + $this->assertEquals($expectedHeader, $currentHeader); + } } diff --git a/tests/Http/Middleware/VerifyShopifyTest.php b/tests/Http/Middleware/VerifyShopifyTest.php index 047584c7..79869ed2 100644 --- a/tests/Http/Middleware/VerifyShopifyTest.php +++ b/tests/Http/Middleware/VerifyShopifyTest.php @@ -317,4 +317,36 @@ public function testTokenProcessingAndMissMatchingShops(): void $this->expectException(HttpException::class); $this->runMiddleware(VerifyShopify::class, $newRequest); } + + public function testNotNativeAppbridgeWithTokenProcessingAndLoginShop(): void + { + // Create a shop that matches the token from buildToken + factory($this->model)->create(['name' => 'shop-name.myshopify.com']); + $this->app['config']->set('shopify-app.frontend_engine', 'REACT'); + + // Setup the request + $currentRequest = Request::instance(); + $newRequest = $currentRequest->duplicate( + // Query Params + [ + 'shop' => 'shop-name.myshopify.com', + ], + // Request Params + null, + // Attributes + null, + // Cookies + null, + // Files + null, + // Server vars + [ + 'HTTP_Authorization' => "Bearer {$this->buildToken()}", + ] + ); + + // Run the middleware + $result = $this->runMiddleware(VerifyShopify::class, $newRequest); + $this->assertTrue($result[0]); + } } From 3d90f3e70d7a51d52f5fa3da559a77be021f18c1 Mon Sep 17 00:00:00 2001 From: Nahid Bin Azhar Date: Sun, 1 Oct 2023 15:35:13 +0600 Subject: [PATCH 30/31] Refactor code with PSR-12 standard --- src/Actions/AuthenticateShop.php | 1 - src/Messaging/Events/ShopAuthenticatedEvent.php | 4 ++-- tests/UtilTest.php | 1 + 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Actions/AuthenticateShop.php b/src/Actions/AuthenticateShop.php index b325f9f8..c2d8767c 100644 --- a/src/Actions/AuthenticateShop.php +++ b/src/Actions/AuthenticateShop.php @@ -112,7 +112,6 @@ public function __invoke(Request $request): array event(new AppInstalledEvent($result['shop_id'])); - return [$result, true]; } } diff --git a/src/Messaging/Events/ShopAuthenticatedEvent.php b/src/Messaging/Events/ShopAuthenticatedEvent.php index 1a1c7b3e..3cd16b18 100644 --- a/src/Messaging/Events/ShopAuthenticatedEvent.php +++ b/src/Messaging/Events/ShopAuthenticatedEvent.php @@ -28,8 +28,8 @@ class ShopAuthenticatedEvent * * @return void */ - public function __construct(ShopId $shop_id) + public function __construct(ShopId $shopId) { - $this->shopId = $shop_id; + $this->shopId = $shopId; } } diff --git a/tests/UtilTest.php b/tests/UtilTest.php index e88ee5b8..358ff3d6 100644 --- a/tests/UtilTest.php +++ b/tests/UtilTest.php @@ -127,6 +127,7 @@ public function testUseNativeAppBridgeIsFalse(): void public function testHasAppLegacySupport(): void { $supportedFeatures = $this->app['config']->get('shopify-app.app_legacy_supports', []); + foreach ($supportedFeatures as $feature => $val) { $this->assertSame( $val, From 7e1e5c8f0fb893bde1c593ef38fcebd3ee80130e Mon Sep 17 00:00:00 2001 From: Nahid Bin Azhar Date: Sun, 1 Oct 2023 15:42:50 +0600 Subject: [PATCH 31/31] Fix linting issue --- tests/UtilTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/UtilTest.php b/tests/UtilTest.php index 358ff3d6..e88ee5b8 100644 --- a/tests/UtilTest.php +++ b/tests/UtilTest.php @@ -127,7 +127,6 @@ public function testUseNativeAppBridgeIsFalse(): void public function testHasAppLegacySupport(): void { $supportedFeatures = $this->app['config']->get('shopify-app.app_legacy_supports', []); - foreach ($supportedFeatures as $feature => $val) { $this->assertSame( $val,