From 352299b776a5c9b8071c0f6391d2487fc587d58e Mon Sep 17 00:00:00 2001 From: Tyler King Date: Tue, 23 Jan 2018 15:17:47 -0330 Subject: [PATCH 01/17] Begin of branch and middleware code --- src/ShopifyApp/Middleware/Billable.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/ShopifyApp/Middleware/Billable.php diff --git a/src/ShopifyApp/Middleware/Billable.php b/src/ShopifyApp/Middleware/Billable.php new file mode 100644 index 00000000..0ee9834d --- /dev/null +++ b/src/ShopifyApp/Middleware/Billable.php @@ -0,0 +1,20 @@ + Date: Tue, 23 Jan 2018 16:04:26 -0330 Subject: [PATCH 02/17] Basic model changes for billing, tests for the new model changes, and migration --- src/ShopifyApp/Models/Shop.php | 25 +++++++++++-- ...1_23_153809_add_billing_to_shops_table.php | 35 +++++++++++++++++++ tests/Models/ShopModelTest.php | 20 ++++++++++- tests/TestCase.php | 10 +++++- 4 files changed, 86 insertions(+), 4 deletions(-) create mode 100644 src/ShopifyApp/resources/database/migrations/2018_01_23_153809_add_billing_to_shops_table.php diff --git a/src/ShopifyApp/Models/Shop.php b/src/ShopifyApp/Models/Shop.php index a301386c..d71632fa 100644 --- a/src/ShopifyApp/Models/Shop.php +++ b/src/ShopifyApp/Models/Shop.php @@ -12,11 +12,12 @@ class Shop extends Model */ protected $fillable = [ 'shopify_domain', - 'shopify_token' + 'shopify_token', + 'grandfathered' ]; /** - * The API instance + * The API instance. * * @var object */ @@ -40,4 +41,24 @@ public function api() // Return existing instance return $this->api; } + + /** + * Checks if a shop has a charge ID. + * + * @return boolean + */ + public function isPaid() + { + return !is_null($this->charge_id); + } + + /** + * Checks is shop is grandfathered in. + * + * @return boolean + */ + public function isGrandfathered() + { + return ((boolean) $this->grandfathered) === true; + } } diff --git a/src/ShopifyApp/resources/database/migrations/2018_01_23_153809_add_billing_to_shops_table.php b/src/ShopifyApp/resources/database/migrations/2018_01_23_153809_add_billing_to_shops_table.php new file mode 100644 index 00000000..2f44df08 --- /dev/null +++ b/src/ShopifyApp/resources/database/migrations/2018_01_23_153809_add_billing_to_shops_table.php @@ -0,0 +1,35 @@ +bigInteger('charge_id')->nullable(true)->default(null); + $table->boolean('grandfathered')->default(false); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('shops', function (Blueprint $table) { + // Laravel doesn't seem to support multiple dropColumn commands + // See: (https://github.com/laravel/framework/issues/2979#issuecomment-227468621) + $table->dropColumn(['charge_id', 'grandfathered']); + }); + } +} diff --git a/tests/Models/ShopModelTest.php b/tests/Models/ShopModelTest.php index 7b8a5dd6..bf096c12 100644 --- a/tests/Models/ShopModelTest.php +++ b/tests/Models/ShopModelTest.php @@ -37,8 +37,26 @@ public function testShopShouldSaveAndAllowForMassAssignment() $shop = Shop::create( ['shopify_domain' => 'abc.myshopify.com', 'shopify_token' => '1234'], - ['shopify_domain' => 'cba.myshopify.com', 'shopify_token' => '1234'] + ['shopify_domain' => 'cba.myshopify.com', 'shopify_token' => '1234', 'grandfathered' => true] ); $this->assertEquals(true, true); } + + public function testShopShouldReturnGrandfatheredState() + { + $shop = Shop::where('shopify_domain', 'grandfathered.myshopify.com')->first(); + $shop_2 = Shop::where('shopify_domain', 'example.myshopify.com')->first(); + + $this->assertEquals(true, $shop->isGrandfathered()); + $this->assertEquals(false, $shop_2->isGrandfathered()); + } + + public function testShopShouldConfirmPaidState() + { + $shop = Shop::where('shopify_domain', 'grandfathered.myshopify.com')->first(); + $shop_2 = Shop::where('shopify_domain', 'example.myshopify.com')->first(); + + $this->assertEquals(false, $shop->isPaid()); + $this->assertEquals(true, $shop_2->isPaid()); + } } diff --git a/tests/TestCase.php b/tests/TestCase.php index e9455ba1..ad2fb2d8 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -50,7 +50,8 @@ protected function getEnvironmentSetUp($app) ]); } - protected function setupDatabase($app) { + protected function setupDatabase($app) + { // Path to our migrations to load $this->loadMigrationsFrom(realpath(__DIR__.'/../src/ShopifyApp/resources/database/migrations')); } @@ -61,6 +62,13 @@ protected function seedDatabase() $shop = new Shop; $shop->shopify_domain = 'example.myshopify.com'; $shop->shopify_token = '1234'; + $shop->charge_id = 678298290; + $shop->save(); + + $shop = new Shop; + $shop->shopify_domain = 'grandfathered.myshopify.com'; + $shop->shopify_token = '1234'; + $shop->grandfathered = true; $shop->save(); } } \ No newline at end of file From 3e4da6a62984fc94a5fb8ce219a7702b9bd78876 Mon Sep 17 00:00:00 2001 From: Tyler King Date: Tue, 23 Jan 2018 16:39:58 -0330 Subject: [PATCH 03/17] PHPCS fixes, config entries for billing, and start of middleware --- src/ShopifyApp/Middleware/Billable.php | 11 ++- src/ShopifyApp/Models/Shop.php | 2 +- src/ShopifyApp/Traits/AuthControllerTrait.php | 2 +- .../resources/config/shopify-app.php | 80 +++++++++++++++++++ tests/Controllers/WebhookControllerTest.php | 12 ++- tests/Facades/ShopAppFacadeTest.php | 2 +- tests/Middleware/AuthProxyMiddlewareTest.php | 6 +- tests/Middleware/AuthShopMiddlewareTest.php | 8 +- .../Middleware/AuthWebhookMiddlewareTest.php | 20 +++-- tests/ShopifyAppTest.php | 2 +- tests/Stubs/Kernel.php | 2 +- tests/TestCase.php | 2 +- 12 files changed, 126 insertions(+), 23 deletions(-) diff --git a/src/ShopifyApp/Middleware/Billable.php b/src/ShopifyApp/Middleware/Billable.php index 0ee9834d..98d5f858 100644 --- a/src/ShopifyApp/Middleware/Billable.php +++ b/src/ShopifyApp/Middleware/Billable.php @@ -15,6 +15,13 @@ class Billable */ public function handle(Request $request, Closure $next) { - // ... + if (config('shopify_app.billing_enabled') === true) + { + $shop = ShopifyApp::shop(); + // ... + } + + // Move on, everything's fine + return $next($request); } -} \ No newline at end of file +} diff --git a/src/ShopifyApp/Models/Shop.php b/src/ShopifyApp/Models/Shop.php index d71632fa..aea3ba93 100644 --- a/src/ShopifyApp/Models/Shop.php +++ b/src/ShopifyApp/Models/Shop.php @@ -54,7 +54,7 @@ public function isPaid() /** * Checks is shop is grandfathered in. - * + * * @return boolean */ public function isGrandfathered() diff --git a/src/ShopifyApp/Traits/AuthControllerTrait.php b/src/ShopifyApp/Traits/AuthControllerTrait.php index d0529cb1..42c97d52 100644 --- a/src/ShopifyApp/Traits/AuthControllerTrait.php +++ b/src/ShopifyApp/Traits/AuthControllerTrait.php @@ -134,7 +134,7 @@ protected function installScripttags() /** * Runs a job after authentication if provided - * + * * @return bool */ protected function afterAuthenticateJob() diff --git a/src/ShopifyApp/resources/config/shopify-app.php b/src/ShopifyApp/resources/config/shopify-app.php index c595fded..d30ebe55 100644 --- a/src/ShopifyApp/resources/config/shopify-app.php +++ b/src/ShopifyApp/resources/config/shopify-app.php @@ -93,6 +93,86 @@ 'myshopify_domain' => 'myshopify.com', + /* + |-------------------------------------------------------------------------- + | Enable Billing + |-------------------------------------------------------------------------- + | + | Enable billing component to the package. + | + */ + + 'billing_enabled' => (boolean) env('SHOPIFY_BILLING_ENABLED', false), + + /* + |-------------------------------------------------------------------------- + | Billing Type + |-------------------------------------------------------------------------- + | + | Single charge or recurring charge. + | Simply use "single" for single, and "recurring" for recurring. + | + */ + + 'billing_type' => env('SHOPIFY_BILLING_TYPE', 'recurring'), + + /* + |-------------------------------------------------------------------------- + | Billing Plan Name + |-------------------------------------------------------------------------- + | + | The name of the plan which shows on the billing. + | + */ + + 'billing_plan' => env('SHOPIFY_BILLING_PLAN_NAME', 'Base Plan'), + + /* + |-------------------------------------------------------------------------- + | Billing Price + |-------------------------------------------------------------------------- + | + | The single or recurring price to charge the customer. + | + */ + + 'billing_price' => (float) env('SHOPIFY_BILLING_PRICE', 0.00), + + /* + |-------------------------------------------------------------------------- + | Billing Trial + |-------------------------------------------------------------------------- + | + | Trails days for the app. Set to 0 for no trial period. + | + */ + + 'billing_trial_days' => (int) env('SHOPIFY_BILLING_TRIAL_DAYS', 7), + + /* + |-------------------------------------------------------------------------- + | Billing Test + |-------------------------------------------------------------------------- + | + | Enable or disable test mode for billing. + | This is useful for development purposes, see Shopify's documentation. + | + */ + + 'billing_test' => (boolean) env('SHOPIFY_BILLING_TEST', false), + + /* + |-------------------------------------------------------------------------- + | Billing Redirect + |-------------------------------------------------------------------------- + | + | Required redirection URL for billing when + | a customer accepts or declines the charge presented. + | + */ + + 'billing_redirect' => env('SHOPIFY_BILLING_REDIRECT', 'billing'), + /* |-------------------------------------------------------------------------- | Shopify Webhooks diff --git a/tests/Controllers/WebhookControllerTest.php b/tests/Controllers/WebhookControllerTest.php index d474f7f1..25c1dc64 100644 --- a/tests/Controllers/WebhookControllerTest.php +++ b/tests/Controllers/WebhookControllerTest.php @@ -26,7 +26,9 @@ public function testShouldReturn201ResponseOnSuccess() $response = $this->call( 'post', '/webhook/orders-create', - [], [], [], + [], + [], + [], $this->headers, file_get_contents(__DIR__.'/../fixtures/webhook.json') ); @@ -41,7 +43,9 @@ public function testShouldReturnErrorResponseOnFailure() $response = $this->call( 'post', '/webhook/products-create', - [], [], [], + [], + [], + [], $this->headers, file_get_contents(__DIR__.'/../fixtures/webhook.json') ); @@ -73,7 +77,9 @@ public function testWebhookShouldRecieveData() $response = $this->call( 'post', '/webhook/orders-create', - [], [], [], + [], + [], + [], $this->headers, file_get_contents(__DIR__.'/../fixtures/webhook.json') ); diff --git a/tests/Facades/ShopAppFacadeTest.php b/tests/Facades/ShopAppFacadeTest.php index cba4dd8e..02ac0cc7 100644 --- a/tests/Facades/ShopAppFacadeTest.php +++ b/tests/Facades/ShopAppFacadeTest.php @@ -13,4 +13,4 @@ public function testBasic() $this->assertEquals('shopifyapp', $method->invoke(null)); } -} \ No newline at end of file +} diff --git a/tests/Middleware/AuthProxyMiddlewareTest.php b/tests/Middleware/AuthProxyMiddlewareTest.php index 5bdbcd3c..84ddb655 100644 --- a/tests/Middleware/AuthProxyMiddlewareTest.php +++ b/tests/Middleware/AuthProxyMiddlewareTest.php @@ -34,7 +34,7 @@ public function testDenysForMissingShop() Input::merge($query); $called = false; - (new AuthProxy)->handle(request(), function($request) use(&$called) { + (new AuthProxy)->handle(request(), function ($request) use (&$called) { // Should never be called $called = true; }); @@ -47,7 +47,7 @@ public function testRuns() Input::merge($this->queryParams); $called = false; - (new AuthProxy)->handle(request(), function($request) use(&$called) { + (new AuthProxy)->handle(request(), function ($request) use (&$called) { // Should be called $called = true; }); @@ -66,7 +66,7 @@ public function testDoesNotRunForInvalidSignature() Input::merge($query); $called = false; - (new AuthProxy)->handle(request(), function($request) use(&$called) { + (new AuthProxy)->handle(request(), function ($request) use (&$called) { // Should never be called $called = true; }); diff --git a/tests/Middleware/AuthShopMiddlewareTest.php b/tests/Middleware/AuthShopMiddlewareTest.php index 2798a251..8ee034be 100644 --- a/tests/Middleware/AuthShopMiddlewareTest.php +++ b/tests/Middleware/AuthShopMiddlewareTest.php @@ -9,7 +9,7 @@ class AuthShopMiddlewareTest extends TestCase public function testShopHasNoAccessShouldAbort() { $called = false; - $result = (new AuthShop)->handle(request(), function($request) use(&$called) { + $result = (new AuthShop)->handle(request(), function ($request) use (&$called) { // Should never be called $called = true; }); @@ -24,7 +24,7 @@ public function testShopHasWithAccessShouldPassMiddleware() session(['shopify_domain' => 'example.myshopify.com']); $called = false; - (new AuthShop)->handle(request(), function($request) use(&$called) { + (new AuthShop)->handle(request(), function ($request) use (&$called) { // Should be called $called = true; }); @@ -39,7 +39,7 @@ public function testShopsWhichDoNotMatchShouldKillSessionAndDirectToReAuthentica Input::merge(['shop' => 'example-different-shop.myshopify.com']); $called = false; - (new AuthShop)->handle(request(), function($request) use(&$called) { + (new AuthShop)->handle(request(), function ($request) use (&$called) { // Should never be called $called = true; }); @@ -47,4 +47,4 @@ public function testShopsWhichDoNotMatchShouldKillSessionAndDirectToReAuthentica $this->assertFalse($called); $this->assertEquals('example-different-shop.myshopify.com', session('shop')); } -} \ No newline at end of file +} diff --git a/tests/Middleware/AuthWebhookMiddlewareTest.php b/tests/Middleware/AuthWebhookMiddlewareTest.php index b5325c76..9cf4ea38 100644 --- a/tests/Middleware/AuthWebhookMiddlewareTest.php +++ b/tests/Middleware/AuthWebhookMiddlewareTest.php @@ -15,7 +15,8 @@ class AuthWebhookMiddlewareTest extends TestCase public function testDenysForMissingShopHeader() { request()->header('x-shopify-hmac-sha256', '1234'); - (new AuthWebhook)->handle(request(), function($request) { }); + (new AuthWebhook)->handle(request(), function ($request) { + }); } /** @@ -25,7 +26,8 @@ public function testDenysForMissingShopHeader() public function testDenysForMissingHmacHeader() { request()->header('x-shopify-shop-domain', 'example.myshopify.com'); - (new AuthWebhook)->handle(request(), function($request) { }); + (new AuthWebhook)->handle(request(), function ($request) { + }); } public function testRuns() @@ -35,11 +37,15 @@ public function testRuns() $response = $this->call( 'post', '/webhook/orders-create', - [], [], [], + [], + + [], + + [], [ 'HTTP_CONTENT_TYPE' => 'application/json', 'HTTP_X_SHOPIFY_SHOP_DOMAIN' => 'example.myshopify.com', - 'HTTP_X_SHOPIFY_HMAC_SHA256' => 'hDJhTqHOY7d5WRlbDl4ehGm/t4kOQKtR+5w6wm+LBQw=', // Matches fixture data and API secret + 'HTTP_X_SHOPIFY_HMAC_SHA256' => 'hDJhTqHOY7d5WRlbDl4ehGm/t4kOQKtR+5w6wm+LBQw=', // Matches fixture data and API secret ], file_get_contents(__DIR__.'/../fixtures/webhook.json') ); @@ -53,7 +59,11 @@ public function testInvalidHmacWontRun() $response = $this->call( 'post', '/webhook/orders-create', - [], [], [], + [], + + [], + + [], [ 'HTTP_CONTENT_TYPE' => 'application/json', 'HTTP_X_SHOPIFY_SHOP_DOMAIN' => 'example.myshopify.com', diff --git a/tests/ShopifyAppTest.php b/tests/ShopifyAppTest.php index cc9bcc05..b9690b3e 100644 --- a/tests/ShopifyAppTest.php +++ b/tests/ShopifyAppTest.php @@ -87,4 +87,4 @@ public function testShouldAllowForModelOverride() $this->assertEquals('OhMyBrew\ShopifyApp\Test\Stubs\ShopModelStub', get_class($shop)); $this->assertEquals('hello', $shop->hello()); } -} \ No newline at end of file +} diff --git a/tests/Stubs/Kernel.php b/tests/Stubs/Kernel.php index 88cd824a..4b1f2b19 100644 --- a/tests/Stubs/Kernel.php +++ b/tests/Stubs/Kernel.php @@ -22,4 +22,4 @@ class Kernel extends \Orchestra\Testbench\Http\Kernel 'auth.webhook' => \OhMyBrew\ShopifyApp\Middleware\AuthWebhook::class, 'auth.proxy' => \OhMyBrew\ShopifyApp\Middleware\AuthProxy::class ]; -} \ No newline at end of file +} diff --git a/tests/TestCase.php b/tests/TestCase.php index ad2fb2d8..c46b22a0 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -71,4 +71,4 @@ protected function seedDatabase() $shop->grandfathered = true; $shop->save(); } -} \ No newline at end of file +} From f65277682a8aff122c7c7df22c665c0d72b1a4d6 Mon Sep 17 00:00:00 2001 From: Tyler King Date: Tue, 23 Jan 2018 18:32:31 -0330 Subject: [PATCH 04/17] Basic middleware tests --- src/ShopifyApp/Middleware/Billable.php | 9 ++- src/ShopifyApp/resources/routes.php | 15 ++++ .../Middleware/AuthWebhookMiddlewareTest.php | 5 +- tests/Middleware/BillableMiddlewareTest.php | 69 +++++++++++++++++++ tests/TestCase.php | 9 ++- 5 files changed, 99 insertions(+), 8 deletions(-) create mode 100644 tests/Middleware/BillableMiddlewareTest.php diff --git a/src/ShopifyApp/Middleware/Billable.php b/src/ShopifyApp/Middleware/Billable.php index 98d5f858..3094a654 100644 --- a/src/ShopifyApp/Middleware/Billable.php +++ b/src/ShopifyApp/Middleware/Billable.php @@ -2,6 +2,7 @@ use Closure; use Illuminate\Http\Request; +use OhMyBrew\ShopifyApp\Facades\ShopifyApp; class Billable { @@ -15,10 +16,12 @@ class Billable */ public function handle(Request $request, Closure $next) { - if (config('shopify_app.billing_enabled') === true) - { + if (config('shopify-app.billing_enabled') === true) { $shop = ShopifyApp::shop(); - // ... + if (!$shop->isPaid() && !$shop->isGrandfathered()) { + // No charge in database and they're not grandfathered in, redirect to billing + return redirect()->route('billing'); + } } // Move on, everything's fine diff --git a/src/ShopifyApp/resources/routes.php b/src/ShopifyApp/resources/routes.php index 9b473416..0c4ee3fb 100644 --- a/src/ShopifyApp/resources/routes.php +++ b/src/ShopifyApp/resources/routes.php @@ -57,6 +57,21 @@ ) ->name('authenticate'); + /* + |-------------------------------------------------------------------------- + | Billing Method + |-------------------------------------------------------------------------- + | + | Billing handler. + | + */ + + Route::get( + '/billing', + 'OhMyBrew\ShopifyApp\Controllers\BillingController@index' + ) + ->name('billing'); + /* |-------------------------------------------------------------------------- | Webhook Handler diff --git a/tests/Middleware/AuthWebhookMiddlewareTest.php b/tests/Middleware/AuthWebhookMiddlewareTest.php index 9cf4ea38..21a5aa08 100644 --- a/tests/Middleware/AuthWebhookMiddlewareTest.php +++ b/tests/Middleware/AuthWebhookMiddlewareTest.php @@ -16,6 +16,7 @@ public function testDenysForMissingShopHeader() { request()->header('x-shopify-hmac-sha256', '1234'); (new AuthWebhook)->handle(request(), function ($request) { + // ... }); } @@ -38,9 +39,7 @@ public function testRuns() 'post', '/webhook/orders-create', [], - [], - [], [ 'HTTP_CONTENT_TYPE' => 'application/json', @@ -60,9 +59,7 @@ public function testInvalidHmacWontRun() 'post', '/webhook/orders-create', [], - [], - [], [ 'HTTP_CONTENT_TYPE' => 'application/json', diff --git a/tests/Middleware/BillableMiddlewareTest.php b/tests/Middleware/BillableMiddlewareTest.php new file mode 100644 index 00000000..f0e0998e --- /dev/null +++ b/tests/Middleware/BillableMiddlewareTest.php @@ -0,0 +1,69 @@ + true]); + session(['shopify_domain' => 'new-shop.myshopify.com']); + + $called = false; + $result = (new Billable)->handle(request(), function ($request) use (&$called) { + // Should never be called + $called = true; + }); + + $this->assertFalse($called); + $this->assertEquals(true, strpos($result, 'Redirecting to http://localhost/billing') !== false); + } + + public function testEnabledBillingWithPaidShop() + { + // Enable billing and set a shop + config(['shopify-app.billing_enabled' => true]); + session(['shopify_domain' => 'example.myshopify.com']); + + $called = false; + $result = (new Billable)->handle(request(), function ($request) use (&$called) { + // Should be called + $called = true; + }); + + $this->assertTrue($called); + } + + public function testEnabledBillingWithGrandfatheredShop() + { + // Enable billing and set a shop + config(['shopify-app.billing_enabled' => true]); + session(['shopify_domain' => 'grandfathered.myshopify.com']); + + $called = false; + $result = (new Billable)->handle(request(), function ($request) use (&$called) { + // Should be called + $called = true; + }); + + $this->assertTrue($called); + } + + public function testDisabledBillingShouldPassOn() + { + // Ensure billing is disabled and set a shop + config(['shopify-app.billing_enabled' => false]); + session(['shopify_domain' => 'example.myshopify.com']); + + $called = false; + $result = (new Billable)->handle(request(), function ($request) use (&$called) { + // Should be called + $called = true; + }); + + $this->assertTrue($called); + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php index c46b22a0..f2e1ce55 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -58,17 +58,24 @@ protected function setupDatabase($app) protected function seedDatabase() { - // Base shop we use in most tests + // Paid shop, not grandfathered $shop = new Shop; $shop->shopify_domain = 'example.myshopify.com'; $shop->shopify_token = '1234'; $shop->charge_id = 678298290; $shop->save(); + // Non-paid shop, grandfathered $shop = new Shop; $shop->shopify_domain = 'grandfathered.myshopify.com'; $shop->shopify_token = '1234'; $shop->grandfathered = true; $shop->save(); + + // New shop... non-paid, not grandfathered + $shop = new Shop; + $shop->shopify_domain = 'new-shop.myshopify.com'; + $shop->shopify_token = '1234'; + $shop->save(); } } From 95747ba6ea4f1eec06556747798d630709efdbb9 Mon Sep 17 00:00:00 2001 From: Tyler King Date: Tue, 23 Jan 2018 18:35:43 -0330 Subject: [PATCH 05/17] Adittional route setup --- src/ShopifyApp/resources/routes.php | 2 +- tests/Stubs/Kernel.php | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/ShopifyApp/resources/routes.php b/src/ShopifyApp/resources/routes.php index 0c4ee3fb..7b6713ca 100644 --- a/src/ShopifyApp/resources/routes.php +++ b/src/ShopifyApp/resources/routes.php @@ -24,7 +24,7 @@ '/', 'OhMyBrew\ShopifyApp\Controllers\HomeController@index' ) - ->middleware('auth.shop') + ->middleware(['auth.shop', 'billable']) ->name('home'); /* diff --git a/tests/Stubs/Kernel.php b/tests/Stubs/Kernel.php index 4b1f2b19..85eff407 100644 --- a/tests/Stubs/Kernel.php +++ b/tests/Stubs/Kernel.php @@ -18,8 +18,9 @@ class Kernel extends \Orchestra\Testbench\Http\Kernel 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, // Added for testing - 'auth.shop' => \OhMyBrew\ShopifyApp\Middleware\AuthShop::class, - 'auth.webhook' => \OhMyBrew\ShopifyApp\Middleware\AuthWebhook::class, - 'auth.proxy' => \OhMyBrew\ShopifyApp\Middleware\AuthProxy::class + 'auth.shop' => \OhMyBrew\ShopifyApp\Middleware\AuthShop::class, + 'auth.webhook' => \OhMyBrew\ShopifyApp\Middleware\AuthWebhook::class, + 'auth.proxy' => \OhMyBrew\ShopifyApp\Middleware\AuthProxy::class, + 'billable' => \OhMyBrew\ShopifyApp\Middleware\Billable::class ]; } From e71369a472a3610ca13af41763abee5d616fbade Mon Sep 17 00:00:00 2001 From: Tyler King Date: Tue, 23 Jan 2018 23:38:20 -0330 Subject: [PATCH 06/17] Billing controller complete. No testing for the process action yet --- .../Controllers/BillingController.php | 9 +++ .../Traits/BillingControllerTrait.php | 74 +++++++++++++++++++ .../resources/config/shopify-app.php | 2 +- src/ShopifyApp/resources/routes.php | 19 ++++- .../views/auth/fullpage_redirect.blade.php | 2 +- .../views/billing/fullpage_redirect.blade.php | 13 ++++ tests/Controllers/BillingControllerTest.php | 26 +++++++ ...t_admin_recurring_application_charges.json | 24 ++++++ 8 files changed, 165 insertions(+), 4 deletions(-) create mode 100644 src/ShopifyApp/Controllers/BillingController.php create mode 100644 src/ShopifyApp/Traits/BillingControllerTrait.php create mode 100644 src/ShopifyApp/resources/views/billing/fullpage_redirect.blade.php create mode 100644 tests/Controllers/BillingControllerTest.php create mode 100644 tests/fixtures/post_admin_recurring_application_charges.json diff --git a/src/ShopifyApp/Controllers/BillingController.php b/src/ShopifyApp/Controllers/BillingController.php new file mode 100644 index 00000000..e50c3182 --- /dev/null +++ b/src/ShopifyApp/Controllers/BillingController.php @@ -0,0 +1,9 @@ +api()->request( + 'POST', + "/admin/{$charge_type}s.json", + [ + "{$charge_type}" => [ + 'name' => config('shopify-app.billing_plan'), + 'price' => config('shopify-app.billing_price'), + 'test' => config('shopify-app.billing_test'), + 'trial_days' => config('shopify-app.billing_trial_days'), + 'return_url' => url(config('shopify-app.billing_redirect')) + ] + ] + )->body->{$charge_type}; + + // Do a fullpage redirect + return view('shopify-app::billing.fullpage_redirect', [ + 'url' => $charge->confirmation_url + ]); + } + + /** + * Processes the response from the customer. + * + * @return void + */ + public function process() + { + // Setup the shop and API + $shop = ShopifyApp::shop(); + $api = $shop->api(); + + // Get the charge ID passed back and determine the charge type + $charge_id = request('charge_id'); + $charge_type = config('shopify-app.billing_type') === 'single' ? 'application_charge' : 'recurring_application_charge'; + + // Get the charge + $charge = $api->request( + 'GET', + "/admin/{$charge_type}s/{$charge_id}.json" + )->body->{$charge_type}; + + if ($charge->status == 'accepted') { + // Customer accepted, activate the charge + $api->request('POST', "/admin/{$charge_type}s/{$charge_id}/activate.json"); + + // Save the charge ID to the shop + $shop->charge_id = $charge_id; + $shop->save(); + + // Go to homepage of app + return redirect()->route('home'); + } else { + // Customer declined the charge, abort + return abort(404, 'It seems you have declined the billing charge for this application.'); + } + } +} diff --git a/src/ShopifyApp/resources/config/shopify-app.php b/src/ShopifyApp/resources/config/shopify-app.php index d30ebe55..ebc3a233 100644 --- a/src/ShopifyApp/resources/config/shopify-app.php +++ b/src/ShopifyApp/resources/config/shopify-app.php @@ -171,7 +171,7 @@ | */ - 'billing_redirect' => env('SHOPIFY_BILLING_REDIRECT', 'billing'), + 'billing_redirect' => env('SHOPIFY_BILLING_REDIRECT', 'billing.process'), /* |-------------------------------------------------------------------------- diff --git a/src/ShopifyApp/resources/routes.php b/src/ShopifyApp/resources/routes.php index 7b6713ca..61aec0c5 100644 --- a/src/ShopifyApp/resources/routes.php +++ b/src/ShopifyApp/resources/routes.php @@ -59,10 +59,10 @@ /* |-------------------------------------------------------------------------- - | Billing Method + | Billing Handler |-------------------------------------------------------------------------- | - | Billing handler. + | Billing handler. Sends to billing screen for Shopify. | */ @@ -72,6 +72,21 @@ ) ->name('billing'); + /* + |-------------------------------------------------------------------------- + | Billing Processor + |-------------------------------------------------------------------------- + | + | Processes the customer's response to the billing screen. + | + */ + + Route::get( + '/billing/process', + 'OhMyBrew\ShopifyApp\Controllers\BillingController@process' + ) + ->name('billing.process'); + /* |-------------------------------------------------------------------------- | Webhook Handler diff --git a/src/ShopifyApp/resources/views/auth/fullpage_redirect.blade.php b/src/ShopifyApp/resources/views/auth/fullpage_redirect.blade.php index 663b8dcd..ea2e8610 100644 --- a/src/ShopifyApp/resources/views/auth/fullpage_redirect.blade.php +++ b/src/ShopifyApp/resources/views/auth/fullpage_redirect.blade.php @@ -4,7 +4,7 @@ - Redirecting… + Redirecting... + + + + \ No newline at end of file diff --git a/tests/Controllers/BillingControllerTest.php b/tests/Controllers/BillingControllerTest.php new file mode 100644 index 00000000..77c3f475 --- /dev/null +++ b/tests/Controllers/BillingControllerTest.php @@ -0,0 +1,26 @@ + new ApiStub]); + } + + public function testSendsShopToBillingScreen() + { + session(['shopify_domain' => 'example.myshopify.com']); + + $response = $this->get('/billing'); + $response->assertViewHas( + 'url', + 'https://example.myshopify.com/admin/charges/1029266947/confirm_recurring_application_charge?signature=BAhpBANeWT0%3D--64de8739eb1e63a8f848382bb757b20343eb414f' + ); + } +} diff --git a/tests/fixtures/post_admin_recurring_application_charges.json b/tests/fixtures/post_admin_recurring_application_charges.json new file mode 100644 index 00000000..fe48f8db --- /dev/null +++ b/tests/fixtures/post_admin_recurring_application_charges.json @@ -0,0 +1,24 @@ +{ + "recurring_application_charge": { + "id": 1029266947, + "name": "Basic Plan", + "api_client_id": 755357713, + "price": "10.00", + "status": "pending", + "return_url": "http:\/\/super-duper.shopifyapps.com\/", + "billing_on": null, + "created_at": "2017-08-17T15:29:47-04:00", + "updated_at": "2017-08-17T15:29:47-04:00", + "test": null, + "activated_on": null, + "trial_ends_on": null, + "cancelled_on": null, + "trial_days": 0, + "capped_amount": "100.00", + "balance_used": 0.0, + "balance_remaining": 100.0, + "risk_level": 0.0, + "decorated_return_url": "http:\/\/super-duper.shopifyapps.com\/?charge_id=1029266947", + "confirmation_url": "https:\/\/example.myshopify.com\/admin\/charges\/1029266947\/confirm_recurring_application_charge?signature=BAhpBANeWT0%3D--64de8739eb1e63a8f848382bb757b20343eb414f" + } +} \ No newline at end of file From 5382bb935286cf8a8a75b91d864b942df2887ff7 Mon Sep 17 00:00:00 2001 From: Tyler King Date: Wed, 24 Jan 2018 01:03:49 -0330 Subject: [PATCH 07/17] Adjustment to status code for decline of charges --- src/ShopifyApp/Traits/BillingControllerTrait.php | 2 +- tests/Controllers/BillingControllerTest.php | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/ShopifyApp/Traits/BillingControllerTrait.php b/src/ShopifyApp/Traits/BillingControllerTrait.php index f83622a8..b07032e3 100644 --- a/src/ShopifyApp/Traits/BillingControllerTrait.php +++ b/src/ShopifyApp/Traits/BillingControllerTrait.php @@ -68,7 +68,7 @@ public function process() return redirect()->route('home'); } else { // Customer declined the charge, abort - return abort(404, 'It seems you have declined the billing charge for this application.'); + return abort(403, 'It seems you have declined the billing charge for this application.'); } } } diff --git a/tests/Controllers/BillingControllerTest.php b/tests/Controllers/BillingControllerTest.php index 77c3f475..5f619d60 100644 --- a/tests/Controllers/BillingControllerTest.php +++ b/tests/Controllers/BillingControllerTest.php @@ -11,16 +11,22 @@ public function setUp() // Stub in our API class config(['shopify-app.api_class' => new ApiStub]); + + // Base shop for all tests here + session(['shopify_domain' => 'example.myshopify.com']); } public function testSendsShopToBillingScreen() { - session(['shopify_domain' => 'example.myshopify.com']); - $response = $this->get('/billing'); $response->assertViewHas( 'url', 'https://example.myshopify.com/admin/charges/1029266947/confirm_recurring_application_charge?signature=BAhpBANeWT0%3D--64de8739eb1e63a8f848382bb757b20343eb414f' ); } + + public function testShopAcceptsBilling() + { + // ... + } } From 5dec2348a86f9c7b6a6485cbe041e3b93e63b24c Mon Sep 17 00:00:00 2001 From: Tyler King Date: Wed, 24 Jan 2018 12:22:10 -0330 Subject: [PATCH 08/17] BillingPlan class to keep logic clean for BillingTrait, and allow for modification of plans --- src/ShopifyApp/Libraries/BillingPlan.php | 153 ++++++++++++++++++ .../Traits/WebhookControllerTrait.php | 2 - 2 files changed, 153 insertions(+), 2 deletions(-) create mode 100644 src/ShopifyApp/Libraries/BillingPlan.php diff --git a/src/ShopifyApp/Libraries/BillingPlan.php b/src/ShopifyApp/Libraries/BillingPlan.php new file mode 100644 index 00000000..d199bc4a --- /dev/null +++ b/src/ShopifyApp/Libraries/BillingPlan.php @@ -0,0 +1,153 @@ +shop = $shop; + $this->charge_type = $charge_type === 'single' ? 'application_charge' : 'recurring_application_charge'; + + return $this; + } + + /** + * Sets the plan. + * + * @param array $plan The plan details. + * $plan = [ + * 'name' => (string) Plan name. + * 'price' => (float) Plan price. Required. + * 'test' => (boolean) Test mode or not. + * 'trial_days' => (int) Plan trial period in days. + * 'return_url' => (string) URL to handle response for acceptance or decline or billing. Required. + * ] + * @return $this + */ + public function setDetails(array $details) + { + $this->details = $details; + + return $this; + } + + /** + * Sets the charge ID. + * + * @param int $charge_id The charge ID to use + * @return $this + */ + public function setChargeId(int $charge_id) + { + $this->charge_id = $charge_id; + + return $this; + } + + /** + * Gets the charge information for a previously inited charge. + * + * @return object + */ + public function getCharge() + { + // Run API to grab details + return $this->shop->api()->request( + 'GET', + "/admin/{$this->charge_type}s/{$this->charge_id}.json" + )->body->{$this->charge_type}; + } + + /** + * Activates a plan to the shop. + * + * Example usage: + * (new BillingPlan([shop], 'recurring'))->setChargeId(request('charge_id'))->activate(); + * + * @return object + */ + public function activate() + { + // Check if we have a charge ID to use + if (!$this->charge_id) { + throw new Exception('Can not activate plan without a charge ID.'); + } + + // Activate and return the API response + return $this->shop->api()->request( + 'POST', + "/admin/{$this->charge_type}s/{$this->charge_id}/activate.json" + ); + } + + /** + * Gets the confirmation URL to redirect the customer to. + * This URL sends them to Shopify's billing page. + * + * Example usage: + * (new BillingPlan([shop], 'recurring'))->setDetails($plan)->getConfirmationUrl(); + * + * @return string + */ + public function getConfirmationUrl() + { + // Check if we have plan details + if (!is_array($this->details)) { + throw new Exception('Plan details are missing for confirmation URL request.'); + } + + // Begin the charge request + $charge = $this->shop->api()->request( + 'POST', + "/admin/{$this->charge_type}s.json", + [ + "{$this->charge_type}" => [ + 'name' => $this->plan['name'], + 'price' => $this->plan['price'], + 'test' => $this->plan['test'], + 'trial_days' => $this->plan['trial_days'], + 'return_url' => $this->plan['return_url'], + ] + ] + )->body->{$this->charge_type}; + + return $charge->confirmation_url; + } +} diff --git a/src/ShopifyApp/Traits/WebhookControllerTrait.php b/src/ShopifyApp/Traits/WebhookControllerTrait.php index 53d04a69..f5587111 100644 --- a/src/ShopifyApp/Traits/WebhookControllerTrait.php +++ b/src/ShopifyApp/Traits/WebhookControllerTrait.php @@ -1,7 +1,5 @@ Date: Wed, 24 Jan 2018 12:54:11 -0330 Subject: [PATCH 09/17] BillingPlan tested --- src/ShopifyApp/Libraries/BillingPlan.php | 21 +++-- tests/Libraries/BillingPlanTest.php | 79 +++++++++++++++++++ ...urring_application_charges_1029266947.json | 20 +++++ ...plication_charges_1029266947_activate.json | 19 +++++ 4 files changed, 131 insertions(+), 8 deletions(-) create mode 100644 tests/Libraries/BillingPlanTest.php create mode 100644 tests/fixtures/get_admin_recurring_application_charges_1029266947.json create mode 100644 tests/fixtures/post_admin_recurring_application_charges_1029266947_activate.json diff --git a/src/ShopifyApp/Libraries/BillingPlan.php b/src/ShopifyApp/Libraries/BillingPlan.php index d199bc4a..b7dc9893 100644 --- a/src/ShopifyApp/Libraries/BillingPlan.php +++ b/src/ShopifyApp/Libraries/BillingPlan.php @@ -1,7 +1,7 @@ shop = $shop; $this->charge_type = $charge_type === 'single' ? 'application_charge' : 'recurring_application_charge'; @@ -88,6 +88,11 @@ public function setChargeId(int $charge_id) */ public function getCharge() { + // Check if we have a charge ID to use + if (!$this->charge_id) { + throw new Exception('Can not get charge information without charge ID.'); + } + // Run API to grab details return $this->shop->api()->request( 'GET', @@ -114,7 +119,7 @@ public function activate() return $this->shop->api()->request( 'POST', "/admin/{$this->charge_type}s/{$this->charge_id}/activate.json" - ); + )->body->{$this->charge_type}; } /** @@ -139,11 +144,11 @@ public function getConfirmationUrl() "/admin/{$this->charge_type}s.json", [ "{$this->charge_type}" => [ - 'name' => $this->plan['name'], - 'price' => $this->plan['price'], - 'test' => $this->plan['test'], - 'trial_days' => $this->plan['trial_days'], - 'return_url' => $this->plan['return_url'], + 'test' => isset($this->details['test']) ? $this->details['test'] : false, + 'trial_days' => isset($this->details['trial_days']) ? $this->details['trial_days'] : 0, + 'name' => $this->details['name'], + 'price' => $this->details['price'], + 'return_url' => $this->details['return_url'], ] ] )->body->{$this->charge_type}; diff --git a/tests/Libraries/BillingPlanTest.php b/tests/Libraries/BillingPlanTest.php new file mode 100644 index 00000000..8bbc2aeb --- /dev/null +++ b/tests/Libraries/BillingPlanTest.php @@ -0,0 +1,79 @@ + new ApiStub]); + + // Base shop and plan + $this->shop = Shop::find(1); + $this->plan = [ + 'name' => 'Basic Plan', + 'price' => 3.00, + 'trial_days' => 0, + 'return_url' => 'http://example.com/' + ]; + } + + public function testShouldReturnConfirmationUrl() + { + $url = (new BillingPlan($this->shop))->setDetails($this->plan)->getConfirmationUrl(); + + $this->assertEquals( + 'https://example.myshopify.com/admin/charges/1029266947/confirm_recurring_application_charge?signature=BAhpBANeWT0%3D--64de8739eb1e63a8f848382bb757b20343eb414f', + $url + ); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage Plan details are missing for confirmation URL request. + */ + public function testShouldNotReturnConfirmationUrlAndThrowException() + { + (new BillingPlan($this->shop))->getConfirmationUrl(); + } + + public function testShouldActivatePlan() + { + $response = (new BillingPlan($this->shop))->setChargeId(1029266947)->activate(); + + $this->assertEquals(true, is_object($response)); + $this->assertEquals('accepted', $response->status); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage Can not activate plan without a charge ID. + */ + public function testShouldNotActivatePlanAndThrowException() + { + (new BillingPlan($this->shop))->activate(); + } + + public function testShouldGetChargeDetails() + { + $response = (new BillingPlan($this->shop))->setChargeId(1029266947)->getCharge(); + + $this->assertEquals(true, is_object($response)); + $this->assertEquals('accepted', $response->status); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage Can not get charge information without charge ID. + */ + public function testShouldNotGetChargeDetailsAndThrowException() + { + (new BillingPlan($this->shop))->getCharge(); + } +} diff --git a/tests/fixtures/get_admin_recurring_application_charges_1029266947.json b/tests/fixtures/get_admin_recurring_application_charges_1029266947.json new file mode 100644 index 00000000..5df04344 --- /dev/null +++ b/tests/fixtures/get_admin_recurring_application_charges_1029266947.json @@ -0,0 +1,20 @@ +{ + "recurring_application_charge": { + "id": 1029266947, + "name": "Super Mega Plan", + "api_client_id": 755357713, + "price": "15.00", + "status": "accepted", + "return_url": "http:\/\/yourapp.com", + "billing_on": "2017-08-17", + "created_at": "2017-08-17T15:16:08-04:00", + "updated_at": "2017-08-17T15:16:08-04:00", + "test": null, + "activated_on": null, + "trial_ends_on": null, + "cancelled_on": null, + "trial_days": 0, + "decorated_return_url": "http:\/\/yourapp.com?charge_id=1029266947", + "confirmation_url": "https:\/\/example.myshopify.com\/admin\/charges\/1029266947\/confirm_recurring_application_charge?signature=BAhpBANeWT0%3D--64de8739eb1e63a8f848382bb757b20343eb414f" + } +} \ No newline at end of file diff --git a/tests/fixtures/post_admin_recurring_application_charges_1029266947_activate.json b/tests/fixtures/post_admin_recurring_application_charges_1029266947_activate.json new file mode 100644 index 00000000..bbe4d18d --- /dev/null +++ b/tests/fixtures/post_admin_recurring_application_charges_1029266947_activate.json @@ -0,0 +1,19 @@ +{ + "recurring_application_charge": { + "id": 1029266947, + "name": "Super Mega Plan", + "api_client_id": 755357713, + "price": "15.00", + "status": "accepted", + "return_url": "http:\/\/yourapp.com", + "billing_on": "2017-08-17", + "created_at": "2017-08-17T15:16:08-04:00", + "updated_at": "2017-08-17T15:29:50-04:00", + "test": null, + "activated_on": null, + "trial_ends_on": null, + "cancelled_on": null, + "trial_days": 0, + "decorated_return_url": "http:\/\/yourapp.com?charge_id=1029266947" + } +} \ No newline at end of file From c2da902665ba0cb7d3665433df92589ce11bf23a Mon Sep 17 00:00:00 2001 From: Tyler King Date: Wed, 24 Jan 2018 13:07:24 -0330 Subject: [PATCH 10/17] Billing trait moved to use new plan lib --- .../Traits/BillingControllerTrait.php | 69 +++++++++++-------- 1 file changed, 40 insertions(+), 29 deletions(-) diff --git a/src/ShopifyApp/Traits/BillingControllerTrait.php b/src/ShopifyApp/Traits/BillingControllerTrait.php index b07032e3..4cf50a70 100644 --- a/src/ShopifyApp/Traits/BillingControllerTrait.php +++ b/src/ShopifyApp/Traits/BillingControllerTrait.php @@ -1,6 +1,7 @@ api()->request( - 'POST', - "/admin/{$charge_type}s.json", - [ - "{$charge_type}" => [ - 'name' => config('shopify-app.billing_plan'), - 'price' => config('shopify-app.billing_price'), - 'test' => config('shopify-app.billing_test'), - 'trial_days' => config('shopify-app.billing_trial_days'), - 'return_url' => url(config('shopify-app.billing_redirect')) - ] - ] - )->body->{$charge_type}; + // Get the confirmation URL + $plan = new BillingPlan(ShopifyApp::shop(), $this->chargeType()); + $plan->setDetails($this->planDetails()); // Do a fullpage redirect return view('shopify-app::billing.fullpage_redirect', [ - 'url' => $charge->confirmation_url + 'url' => $plan->getConfirmationUrl() ]); } @@ -42,23 +29,19 @@ public function index() */ public function process() { - // Setup the shop and API + // Setup the shop and get the charge ID passed in $shop = ShopifyApp::shop(); - $api = $shop->api(); - - // Get the charge ID passed back and determine the charge type $charge_id = request('charge_id'); - $charge_type = config('shopify-app.billing_type') === 'single' ? 'application_charge' : 'recurring_application_charge'; - // Get the charge - $charge = $api->request( - 'GET', - "/admin/{$charge_type}s/{$charge_id}.json" - )->body->{$charge_type}; + // Setup the plan and get the charge + $plan = new BillingPlan($shop, $this->chargeType()); + $plan->setChargeId($charge_id); + // Check the customer's answer to the billing + $charge = $plan->getCharge(); if ($charge->status == 'accepted') { // Customer accepted, activate the charge - $api->request('POST', "/admin/{$charge_type}s/{$charge_id}/activate.json"); + $plan->activate(); // Save the charge ID to the shop $shop->charge_id = $charge_id; @@ -71,4 +54,32 @@ public function process() return abort(403, 'It seems you have declined the billing charge for this application.'); } } + + /** + * Base plan to use for billing. + * Setup as a function so its patchable. + * + * @return array + */ + protected function planDetails() + { + return [ + 'name' => config('shopify-app.billing_plan'), + 'price' => config('shopify-app.billing_price'), + 'test' => config('shopify-app.billing_test'), + 'trial_days' => config('shopify-app.billing_trial_days'), + 'return_url' => url(config('shopify-app.billing_redirect')) + ]; + } + + /** + * Base charge type (single or recurring). + * Setup as a function so its patchable. + * + * @return string + */ + protected function chargeType() + { + return config('shopify-app.billing_type'); + } } From 428157c692f909257f08d1ae29238115fa51376f Mon Sep 17 00:00:00 2001 From: Tyler King Date: Wed, 24 Jan 2018 13:52:19 -0330 Subject: [PATCH 11/17] Completed tests for billing --- .../Traits/BillingControllerTrait.php | 2 +- tests/Controllers/BillingControllerTest.php | 53 ++++++++++++++++++- tests/Controllers/WebhookControllerTest.php | 5 +- ...n_recurring_application_charges_10292.json | 20 +++++++ 4 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 tests/fixtures/get_admin_recurring_application_charges_10292.json diff --git a/src/ShopifyApp/Traits/BillingControllerTrait.php b/src/ShopifyApp/Traits/BillingControllerTrait.php index 4cf50a70..500f8dc2 100644 --- a/src/ShopifyApp/Traits/BillingControllerTrait.php +++ b/src/ShopifyApp/Traits/BillingControllerTrait.php @@ -51,7 +51,7 @@ public function process() return redirect()->route('home'); } else { // Customer declined the charge, abort - return abort(403, 'It seems you have declined the billing charge for this application.'); + return abort(403, 'It seems you have declined the billing charge for this application'); } } diff --git a/tests/Controllers/BillingControllerTest.php b/tests/Controllers/BillingControllerTest.php index 5f619d60..a5cc0e54 100644 --- a/tests/Controllers/BillingControllerTest.php +++ b/tests/Controllers/BillingControllerTest.php @@ -1,7 +1,10 @@ first(); + $this->assertEquals(678298290, $shop->charge_id); // Based on seedDatabase() + + $response = $this->call('get', '/billing/process', ['charge_id' => 1029266947]); + $shop = $shop->fresh(); // Reload model + + $response->assertStatus(302); + $this->assertEquals(1029266947, $shop->charge_id); + } + + public function testShopDeclinesBilling() + { + $response = $this->call('get', '/billing/process', ['charge_id' => 10292]); + + $response->assertStatus(403); + $this->assertEquals( + 'It seems you have declined the billing charge for this application', + $response->exception->getMessage() + ); + } + + public function testReturnsBasePlanDetails() + { + $controller = new BillingController; + $method = new ReflectionMethod(BillingController::class, 'planDetails'); + $method->setAccessible(true); + + // Based on default config + $this->assertEquals( + [ + 'name' => config('shopify-app.billing_plan'), + 'price' => config('shopify-app.billing_price'), + 'test' => config('shopify-app.billing_test'), + 'trial_days' => config('shopify-app.billing_trial_days'), + 'return_url' => url(config('shopify-app.billing_redirect')) + + ], + $method->invoke($controller, 'planDetails') + ); + } + + public function testReturnsBaseChargeType() + { + $controller = new BillingController; + $method = new ReflectionMethod(BillingController::class, 'chargeType'); + $method->setAccessible(true); + + // Based on default config + $this->assertEquals(config('shopify-app.billing_type'), $method->invoke($controller, 'chargeType')); } } diff --git a/tests/Controllers/WebhookControllerTest.php b/tests/Controllers/WebhookControllerTest.php index 25c1dc64..1d497a34 100644 --- a/tests/Controllers/WebhookControllerTest.php +++ b/tests/Controllers/WebhookControllerTest.php @@ -3,6 +3,7 @@ use \ReflectionMethod; use Illuminate\Support\Facades\Queue; use OhMyBrew\ShopifyApp\Test\TestCase; +use OhMyBrew\ShopifyApp\Controllers\WebhookController; require_once __DIR__.'/../Stubs/OrdersCreateJobStub.php'; @@ -55,8 +56,8 @@ public function testShouldReturnErrorResponseOnFailure() public function testShouldCaseTypeToClass() { - $controller = new \OhMyBrew\ShopifyApp\Controllers\WebhookController; - $method = new ReflectionMethod(\OhMyBrew\ShopifyApp\Controllers\WebhookController::class, 'getJobClassFromType'); + $controller = new WebhookController; + $method = new ReflectionMethod(WebhookController::class, 'getJobClassFromType'); $method->setAccessible(true); $types = [ diff --git a/tests/fixtures/get_admin_recurring_application_charges_10292.json b/tests/fixtures/get_admin_recurring_application_charges_10292.json new file mode 100644 index 00000000..291361d3 --- /dev/null +++ b/tests/fixtures/get_admin_recurring_application_charges_10292.json @@ -0,0 +1,20 @@ +{ + "recurring_application_charge": { + "id": 10292, + "name": "Super Mega Plan", + "api_client_id": 755357713, + "price": "15.00", + "status": "declined", + "return_url": "http:\/\/yourapp.com", + "billing_on": "2017-08-17", + "created_at": "2017-08-17T15:16:08-04:00", + "updated_at": "2017-08-17T15:16:08-04:00", + "test": null, + "activated_on": null, + "trial_ends_on": null, + "cancelled_on": null, + "trial_days": 0, + "decorated_return_url": "http:\/\/yourapp.com?charge_id=1029266947", + "confirmation_url": "https:\/\/example.myshopify.com\/admin\/charges\/1029266947\/confirm_recurring_application_charge?signature=BAhpBANeWT0%3D--64de8739eb1e63a8f848382bb757b20343eb414f" + } +} \ No newline at end of file From f7c27e7e8172c0e965bdc73f18d0a947a82477cc Mon Sep 17 00:00:00 2001 From: Tyler King Date: Wed, 24 Jan 2018 16:42:22 -0330 Subject: [PATCH 12/17] Use url path for config --- src/ShopifyApp/resources/config/shopify-app.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ShopifyApp/resources/config/shopify-app.php b/src/ShopifyApp/resources/config/shopify-app.php index ebc3a233..fe9dc500 100644 --- a/src/ShopifyApp/resources/config/shopify-app.php +++ b/src/ShopifyApp/resources/config/shopify-app.php @@ -65,7 +65,7 @@ | */ - 'api_redirect' => env('SHOPIFY_API_REDIRECT', 'authenticate'), + 'api_redirect' => env('SHOPIFY_API_REDIRECT', '/authenticate'), /* |-------------------------------------------------------------------------- @@ -171,7 +171,7 @@ | */ - 'billing_redirect' => env('SHOPIFY_BILLING_REDIRECT', 'billing.process'), + 'billing_redirect' => env('SHOPIFY_BILLING_REDIRECT', '/billing/process'), /* |-------------------------------------------------------------------------- From 7d0c0254085c82f412b590ee0421a1cdb8b6ee78 Mon Sep 17 00:00:00 2001 From: Tyler King Date: Thu, 25 Jan 2018 13:58:36 -0330 Subject: [PATCH 13/17] Switch to sha1-based fixtures for clean up --- tests/Stubs/ApiStub.php | 14 ++++++++++---- ... 4744eacfaa8251b2c4fbe30d470b2cf9da0b2c9a.json} | 0 ... 5b71681ba31d0eb3c4030f15bb1eef7ad298bbf4.json} | 0 ... 602d13096715a537f9bb6e2ea2ac0d493a24344c.json} | 0 ... 8ead4dc2ed931a8eb418b0ddf4d6169bd0477cc5.json} | 0 ... 911a6ce7c33ed152806c1731661286090c0ba2e8.json} | 0 ... d2b3670671d2127ebca6e2c133e7492adcc2be3b.json} | 0 ... fb85fcd35c36c0296df824b4343e3b52e686d589.json} | 0 8 files changed, 10 insertions(+), 4 deletions(-) rename tests/fixtures/{get_admin_recurring_application_charges_1029266947.json => 4744eacfaa8251b2c4fbe30d470b2cf9da0b2c9a.json} (100%) rename tests/fixtures/{get_admin_recurring_application_charges_10292.json => 5b71681ba31d0eb3c4030f15bb1eef7ad298bbf4.json} (100%) rename tests/fixtures/{post_admin_recurring_application_charges_1029266947_activate.json => 602d13096715a537f9bb6e2ea2ac0d493a24344c.json} (100%) rename tests/fixtures/{get_admin_webhooks.json => 8ead4dc2ed931a8eb418b0ddf4d6169bd0477cc5.json} (100%) rename tests/fixtures/{post_admin_access_token.json => 911a6ce7c33ed152806c1731661286090c0ba2e8.json} (100%) rename tests/fixtures/{post_admin_recurring_application_charges.json => d2b3670671d2127ebca6e2c133e7492adcc2be3b.json} (100%) rename tests/fixtures/{get_admin_script_tags.json => fb85fcd35c36c0296df824b4343e3b52e686d589.json} (100%) diff --git a/tests/Stubs/ApiStub.php b/tests/Stubs/ApiStub.php index c5f0eb70..a7cafe00 100644 --- a/tests/Stubs/ApiStub.php +++ b/tests/Stubs/ApiStub.php @@ -6,9 +6,7 @@ class ApiStub extends BasicShopifyAPI { public function request(string $method, string $path, array $params = null) { - $path = str_replace('/', '_', parse_url($path, PHP_URL_PATH)); - $filePath = __DIR__.'/../fixtures/'.strtolower($method).$path; - + $filePath = $this->pathToHash($method, $path); $responseJSON = null; if (file_exists($filePath)) { $responseJSON = json_decode(file_get_contents($filePath)); @@ -22,7 +20,15 @@ public function request(string $method, string $path, array $params = null) public function requestAccessToken(string $code) { - $filePath = __DIR__.'/../fixtures/post_admin_access_token.json'; + $filePath = $this->pathToHash('GET', '/admin/access_token.json'); return json_decode(file_get_contents($filePath))->access_token; } + + private function pathToHash($method, $path) + { + $path = str_replace('/', '_', parse_url($path, PHP_URL_PATH)); + $hash = hash('sha1', strtolower($method) . $path); + + return __DIR__."/../fixtures/{$hash}.json"; + } } diff --git a/tests/fixtures/get_admin_recurring_application_charges_1029266947.json b/tests/fixtures/4744eacfaa8251b2c4fbe30d470b2cf9da0b2c9a.json similarity index 100% rename from tests/fixtures/get_admin_recurring_application_charges_1029266947.json rename to tests/fixtures/4744eacfaa8251b2c4fbe30d470b2cf9da0b2c9a.json diff --git a/tests/fixtures/get_admin_recurring_application_charges_10292.json b/tests/fixtures/5b71681ba31d0eb3c4030f15bb1eef7ad298bbf4.json similarity index 100% rename from tests/fixtures/get_admin_recurring_application_charges_10292.json rename to tests/fixtures/5b71681ba31d0eb3c4030f15bb1eef7ad298bbf4.json diff --git a/tests/fixtures/post_admin_recurring_application_charges_1029266947_activate.json b/tests/fixtures/602d13096715a537f9bb6e2ea2ac0d493a24344c.json similarity index 100% rename from tests/fixtures/post_admin_recurring_application_charges_1029266947_activate.json rename to tests/fixtures/602d13096715a537f9bb6e2ea2ac0d493a24344c.json diff --git a/tests/fixtures/get_admin_webhooks.json b/tests/fixtures/8ead4dc2ed931a8eb418b0ddf4d6169bd0477cc5.json similarity index 100% rename from tests/fixtures/get_admin_webhooks.json rename to tests/fixtures/8ead4dc2ed931a8eb418b0ddf4d6169bd0477cc5.json diff --git a/tests/fixtures/post_admin_access_token.json b/tests/fixtures/911a6ce7c33ed152806c1731661286090c0ba2e8.json similarity index 100% rename from tests/fixtures/post_admin_access_token.json rename to tests/fixtures/911a6ce7c33ed152806c1731661286090c0ba2e8.json diff --git a/tests/fixtures/post_admin_recurring_application_charges.json b/tests/fixtures/d2b3670671d2127ebca6e2c133e7492adcc2be3b.json similarity index 100% rename from tests/fixtures/post_admin_recurring_application_charges.json rename to tests/fixtures/d2b3670671d2127ebca6e2c133e7492adcc2be3b.json diff --git a/tests/fixtures/get_admin_script_tags.json b/tests/fixtures/fb85fcd35c36c0296df824b4343e3b52e686d589.json similarity index 100% rename from tests/fixtures/get_admin_script_tags.json rename to tests/fixtures/fb85fcd35c36c0296df824b4343e3b52e686d589.json From 2c78e21edd2861c69546bac5e9181417f329fa4d Mon Sep 17 00:00:00 2001 From: Tyler King Date: Thu, 25 Jan 2018 14:02:48 -0330 Subject: [PATCH 14/17] Code adjustments for variable names --- src/ShopifyApp/Libraries/BillingPlan.php | 34 +++++++++---------- .../Traits/BillingControllerTrait.php | 6 ++-- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/ShopifyApp/Libraries/BillingPlan.php b/src/ShopifyApp/Libraries/BillingPlan.php index b7dc9893..226cbe67 100644 --- a/src/ShopifyApp/Libraries/BillingPlan.php +++ b/src/ShopifyApp/Libraries/BillingPlan.php @@ -24,26 +24,26 @@ class BillingPlan * * @var integer */ - protected $charge_id; + protected $chargeId; /** * The charge type * * @var string */ - protected $charge_type; + protected $chargeType; /** * Constructor for billing plan class * * @param Shop $shop The shop to target for billing. - * @param string $charge_type The type of charge for the plan (single or recurring). + * @param string $chargeType The type of charge for the plan (single or recurring). * @return $this */ - public function __construct(Shop $shop, string $charge_type = 'recurring') + public function __construct(Shop $shop, string $chargeType = 'recurring') { $this->shop = $shop; - $this->charge_type = $charge_type === 'single' ? 'application_charge' : 'recurring_application_charge'; + $this->chargeType = $chargeType === 'single' ? 'application_charge' : 'recurring_application_charge'; return $this; } @@ -71,12 +71,12 @@ public function setDetails(array $details) /** * Sets the charge ID. * - * @param int $charge_id The charge ID to use + * @param int $chargeId The charge ID to use * @return $this */ - public function setChargeId(int $charge_id) + public function setChargeId(int $chargeId) { - $this->charge_id = $charge_id; + $this->chargeId = $chargeId; return $this; } @@ -89,15 +89,15 @@ public function setChargeId(int $charge_id) public function getCharge() { // Check if we have a charge ID to use - if (!$this->charge_id) { + if (!$this->chargeId) { throw new Exception('Can not get charge information without charge ID.'); } // Run API to grab details return $this->shop->api()->request( 'GET', - "/admin/{$this->charge_type}s/{$this->charge_id}.json" - )->body->{$this->charge_type}; + "/admin/{$this->chargeType}s/{$this->chargeId}.json" + )->body->{$this->chargeType}; } /** @@ -111,15 +111,15 @@ public function getCharge() public function activate() { // Check if we have a charge ID to use - if (!$this->charge_id) { + if (!$this->chargeId) { throw new Exception('Can not activate plan without a charge ID.'); } // Activate and return the API response return $this->shop->api()->request( 'POST', - "/admin/{$this->charge_type}s/{$this->charge_id}/activate.json" - )->body->{$this->charge_type}; + "/admin/{$this->chargeType}s/{$this->chargeId}/activate.json" + )->body->{$this->chargeType}; } /** @@ -141,9 +141,9 @@ public function getConfirmationUrl() // Begin the charge request $charge = $this->shop->api()->request( 'POST', - "/admin/{$this->charge_type}s.json", + "/admin/{$this->chargeType}s.json", [ - "{$this->charge_type}" => [ + "{$this->chargeType}" => [ 'test' => isset($this->details['test']) ? $this->details['test'] : false, 'trial_days' => isset($this->details['trial_days']) ? $this->details['trial_days'] : 0, 'name' => $this->details['name'], @@ -151,7 +151,7 @@ public function getConfirmationUrl() 'return_url' => $this->details['return_url'], ] ] - )->body->{$this->charge_type}; + )->body->{$this->chargeType}; return $charge->confirmation_url; } diff --git a/src/ShopifyApp/Traits/BillingControllerTrait.php b/src/ShopifyApp/Traits/BillingControllerTrait.php index 500f8dc2..683a2610 100644 --- a/src/ShopifyApp/Traits/BillingControllerTrait.php +++ b/src/ShopifyApp/Traits/BillingControllerTrait.php @@ -31,11 +31,11 @@ public function process() { // Setup the shop and get the charge ID passed in $shop = ShopifyApp::shop(); - $charge_id = request('charge_id'); + $chargeId = request('charge_id'); // Setup the plan and get the charge $plan = new BillingPlan($shop, $this->chargeType()); - $plan->setChargeId($charge_id); + $plan->setChargeId($chargeId); // Check the customer's answer to the billing $charge = $plan->getCharge(); @@ -44,7 +44,7 @@ public function process() $plan->activate(); // Save the charge ID to the shop - $shop->charge_id = $charge_id; + $shop->charge_id = $chargeId; $shop->save(); // Go to homepage of app From ada45f68774a34122e5ed15bbc0e46df141cbbf6 Mon Sep 17 00:00:00 2001 From: Tyler King Date: Thu, 25 Jan 2018 17:43:46 +0000 Subject: [PATCH 15/17] Apply fixes from StyleCI --- .../Console/WebhookJobMakeCommand.php | 10 ++-- src/ShopifyApp/Controllers/AuthController.php | 4 +- .../Controllers/BillingController.php | 4 +- src/ShopifyApp/Controllers/HomeController.php | 4 +- .../Controllers/WebhookController.php | 4 +- src/ShopifyApp/Facades/ShopifyApp.php | 4 +- src/ShopifyApp/Jobs/ScripttagInstaller.php | 15 +++--- src/ShopifyApp/Jobs/WebhookInstaller.php | 17 ++++--- src/ShopifyApp/Libraries/BillingPlan.php | 39 ++++++++------- src/ShopifyApp/Middleware/AuthProxy.php | 10 ++-- src/ShopifyApp/Middleware/AuthShop.php | 7 ++- src/ShopifyApp/Middleware/AuthWebhook.php | 6 ++- src/ShopifyApp/Middleware/Billable.php | 4 +- src/ShopifyApp/Models/Shop.php | 12 +++-- src/ShopifyApp/ShopifyApp.php | 14 +++--- src/ShopifyApp/ShopifyAppProvider.php | 6 ++- src/ShopifyApp/Traits/AuthControllerTrait.php | 30 ++++++------ .../Traits/BillingControllerTrait.php | 8 +-- src/ShopifyApp/Traits/HomeControllerTrait.php | 6 ++- .../Traits/WebhookControllerTrait.php | 10 ++-- .../resources/config/shopify-app.php | 5 +- .../2017_07_07_171903_create_shops_table.php | 4 +- ...1_23_153809_add_billing_to_shops_table.php | 4 +- src/ShopifyApp/resources/routes.php | 8 +-- tests/Console/WebhookJobMakeCommandTest.php | 24 ++++----- tests/Controllers/AuthControllerTest.php | 49 ++++++++++--------- tests/Controllers/BillingControllerTest.php | 26 +++++----- tests/Controllers/HomeControllerTest.php | 9 ++-- tests/Controllers/WebhookControllerTest.php | 20 ++++---- tests/Facades/ShopAppFacadeTest.php | 8 +-- tests/Jobs/ScripttagInstallerJobTest.php | 37 +++++++------- tests/Jobs/WebhookInstallerJobTest.php | 37 +++++++------- tests/Libraries/BillingPlanTest.php | 12 +++-- tests/Middleware/AuthProxyMiddlewareTest.php | 20 ++++---- tests/Middleware/AuthShopMiddlewareTest.php | 12 +++-- .../Middleware/AuthWebhookMiddlewareTest.php | 20 ++++---- tests/Middleware/BillableMiddlewareTest.php | 13 ++--- tests/Models/ShopModelTest.php | 8 +-- tests/ShopifyAppTest.php | 9 ++-- tests/Stubs/AfterAuthenticateJobStub.php | 8 +-- tests/Stubs/ApiStub.php | 11 +++-- tests/Stubs/Kernel.php | 6 ++- tests/Stubs/OrdersCreateJobStub.php | 8 +-- tests/Stubs/ShopModelStub.php | 4 +- tests/TestCase.php | 20 ++++---- 45 files changed, 336 insertions(+), 260 deletions(-) diff --git a/src/ShopifyApp/Console/WebhookJobMakeCommand.php b/src/ShopifyApp/Console/WebhookJobMakeCommand.php index bbe84e09..3db8aac4 100644 --- a/src/ShopifyApp/Console/WebhookJobMakeCommand.php +++ b/src/ShopifyApp/Console/WebhookJobMakeCommand.php @@ -1,7 +1,9 @@ -=5.5) + * Execute the console command (>=5.5). * * @return void */ @@ -67,7 +69,7 @@ public function handle() } /** - * Converts the job class name into a URL endpoint + * Converts the job class name into a URL endpoint. * * @param string $name The name of the job * diff --git a/src/ShopifyApp/Controllers/AuthController.php b/src/ShopifyApp/Controllers/AuthController.php index d46af98e..a1f601e6 100644 --- a/src/ShopifyApp/Controllers/AuthController.php +++ b/src/ShopifyApp/Controllers/AuthController.php @@ -1,4 +1,6 @@ - (string) Plan name. - * 'price' => (float) Plan price. Required. - * 'test' => (boolean) Test mode or not. - * 'trial_days' => (int) Plan trial period in days. - * 'return_url' => (string) URL to handle response for acceptance or decline or billing. Required. - * ] + * $plan = [ + * 'name' => (string) Plan name. + * 'price' => (float) Plan price. Required. + * 'test' => (boolean) Test mode or not. + * 'trial_days' => (int) Plan trial period in days. + * 'return_url' => (string) URL to handle response for acceptance or decline or billing. Required. + * ] + * * @return $this */ public function setDetails(array $details) @@ -72,6 +76,7 @@ public function setDetails(array $details) * Sets the charge ID. * * @param int $chargeId The charge ID to use + * * @return $this */ public function setChargeId(int $chargeId) @@ -149,7 +154,7 @@ public function getConfirmationUrl() 'name' => $this->details['name'], 'price' => $this->details['price'], 'return_url' => $this->details['return_url'], - ] + ], ] )->body->{$this->chargeType}; diff --git a/src/ShopifyApp/Middleware/AuthProxy.php b/src/ShopifyApp/Middleware/AuthProxy.php index 31376973..fe74aab3 100644 --- a/src/ShopifyApp/Middleware/AuthProxy.php +++ b/src/ShopifyApp/Middleware/AuthProxy.php @@ -1,4 +1,6 @@ - $value) { - $queryCompiled[] = "{$key}=" . (is_array($value) ? join($value, ',') : $value); + $queryCompiled[] = "{$key}=".(is_array($value) ? implode($value, ',') : $value); } - $queryJoined = join($queryCompiled, ''); + $queryJoined = implode($queryCompiled, ''); // Build a local signature $signatureLocal = hash_hmac('sha256', $queryJoined, config('shopify-app.api_secret')); diff --git a/src/ShopifyApp/Middleware/AuthShop.php b/src/ShopifyApp/Middleware/AuthShop.php index 0fa7c5df..ba3a9f05 100644 --- a/src/ShopifyApp/Middleware/AuthShop.php +++ b/src/ShopifyApp/Middleware/AuthShop.php @@ -1,4 +1,6 @@ -shopify_domain) === true) { // Either no shop session or shops do not match session()->forget('shopify_domain'); + return redirect()->route('authenticate')->with('shop', $shopParam); } diff --git a/src/ShopifyApp/Middleware/AuthWebhook.php b/src/ShopifyApp/Middleware/AuthWebhook.php index e0e197fe..8615da34 100644 --- a/src/ShopifyApp/Middleware/AuthWebhook.php +++ b/src/ShopifyApp/Middleware/AuthWebhook.php @@ -1,4 +1,6 @@ -grandfathered) === true; + return ((bool) $this->grandfathered) === true; } } diff --git a/src/ShopifyApp/ShopifyApp.php b/src/ShopifyApp/ShopifyApp.php index 96648b03..cc4d1798 100644 --- a/src/ShopifyApp/ShopifyApp.php +++ b/src/ShopifyApp/ShopifyApp.php @@ -1,4 +1,6 @@ -setApiKey(config('shopify-app.api_key')); $api->setApiSecret(config('shopify-app.api_secret')); @@ -76,7 +78,7 @@ public function api() public function sanitizeShopDomain($domain) { if (empty($domain)) { - return null; + return; } $configEndDomain = config('shopify-app.myshopify_domain'); diff --git a/src/ShopifyApp/ShopifyAppProvider.php b/src/ShopifyApp/ShopifyAppProvider.php index 4b70a755..348fd77a 100644 --- a/src/ShopifyApp/ShopifyAppProvider.php +++ b/src/ShopifyApp/ShopifyAppProvider.php @@ -1,4 +1,6 @@ -publishes([ - __DIR__.'/resources/config/shopify-app.php' => config_path('shopify-app.php') + __DIR__.'/resources/config/shopify-app.php' => config_path('shopify-app.php'), ]); // Database migrations diff --git a/src/ShopifyApp/Traits/AuthControllerTrait.php b/src/ShopifyApp/Traits/AuthControllerTrait.php index 42c97d52..bea2347c 100644 --- a/src/ShopifyApp/Traits/AuthControllerTrait.php +++ b/src/ShopifyApp/Traits/AuthControllerTrait.php @@ -1,13 +1,15 @@ - $authUrl, - 'shopDomain' => $shopDomain + 'authUrl' => $authUrl, + 'shopDomain' => $shopDomain, ]); } /** - * Fires when there is a code on authentication + * Fires when there is a code on authentication. * * @return \Illuminate\Http\Response */ @@ -103,14 +105,14 @@ protected function authenticationWithCode() } /** - * Installs webhooks (if any) + * Installs webhooks (if any). * * @return void */ protected function installWebhooks() { $webhooks = config('shopify-app.webhooks'); - if (sizeof($webhooks) > 0) { + if (count($webhooks) > 0) { dispatch( new WebhookInstaller(ShopifyApp::shop(), $webhooks) ); @@ -118,14 +120,14 @@ protected function installWebhooks() } /** - * Installs scripttags (if any) + * Installs scripttags (if any). * * @return void */ protected function installScripttags() { $scripttags = config('shopify-app.scripttags'); - if (sizeof($scripttags) > 0) { + if (count($scripttags) > 0) { dispatch( new ScripttagInstaller(ShopifyApp::shop(), $scripttags) ); @@ -133,7 +135,7 @@ protected function installScripttags() } /** - * Runs a job after authentication if provided + * Runs a job after authentication if provided. * * @return bool */ diff --git a/src/ShopifyApp/Traits/BillingControllerTrait.php b/src/ShopifyApp/Traits/BillingControllerTrait.php index 683a2610..499ffbb0 100644 --- a/src/ShopifyApp/Traits/BillingControllerTrait.php +++ b/src/ShopifyApp/Traits/BillingControllerTrait.php @@ -1,4 +1,6 @@ - $plan->getConfirmationUrl() + 'url' => $plan->getConfirmationUrl(), ]); } @@ -68,7 +70,7 @@ protected function planDetails() 'price' => config('shopify-app.billing_price'), 'test' => config('shopify-app.billing_test'), 'trial_days' => config('shopify-app.billing_trial_days'), - 'return_url' => url(config('shopify-app.billing_redirect')) + 'return_url' => url(config('shopify-app.billing_redirect')), ]; } diff --git a/src/ShopifyApp/Traits/HomeControllerTrait.php b/src/ShopifyApp/Traits/HomeControllerTrait.php index 1a0cab52..42e1a494 100644 --- a/src/ShopifyApp/Traits/HomeControllerTrait.php +++ b/src/ShopifyApp/Traits/HomeControllerTrait.php @@ -1,9 +1,11 @@ - env('SHOPIFY_API_CLASS', \OhMyBrew\BasicShopifyAPI::class), - /* |-------------------------------------------------------------------------- | Shopify "MyShopify" domain @@ -102,7 +101,7 @@ | */ - 'billing_enabled' => (boolean) env('SHOPIFY_BILLING_ENABLED', false), + 'billing_enabled' => (bool) env('SHOPIFY_BILLING_ENABLED', false), /* |-------------------------------------------------------------------------- @@ -159,7 +158,7 @@ | */ - 'billing_test' => (boolean) env('SHOPIFY_BILLING_TEST', false), + 'billing_test' => (bool) env('SHOPIFY_BILLING_TEST', false), /* |-------------------------------------------------------------------------- diff --git a/src/ShopifyApp/resources/database/migrations/2017_07_07_171903_create_shops_table.php b/src/ShopifyApp/resources/database/migrations/2017_07_07_171903_create_shops_table.php index ad94ae92..7fe2e374 100644 --- a/src/ShopifyApp/resources/database/migrations/2017_07_07_171903_create_shops_table.php +++ b/src/ShopifyApp/resources/database/migrations/2017_07_07_171903_create_shops_table.php @@ -1,8 +1,8 @@ app->make(WebhookJobMakeCommand::class); $testedCommand->setLaravel($this->app); @@ -21,12 +23,12 @@ public function testItShouldRun() $commandTester->execute([ 'command' => $command->getName(), - 'name' => 'OrdersCreateJob', - 'topic' => 'orders/create' + 'name' => 'OrdersCreateJob', + 'topic' => 'orders/create', ]); $output = $commandTester->getDisplay(); - + $this->assertContains("Don't forget to register the webhook in config/shopify-app.php", $output); $this->assertContains("'address' => 'https://your-domain.com/webhook/orders-create'", $output); $this->assertContains("'topic' => 'orders/create',", $output); @@ -34,7 +36,7 @@ public function testItShouldRun() public function testShouldMakeUrlFromName() { - $application = new ConsoleApplication; + $application = new ConsoleApplication(); $testedCommand = $this->app->make(WebhookJobMakeCommand::class); $testedCommand->setLaravel($this->app); $application->add($testedCommand); @@ -47,7 +49,7 @@ public function testShouldMakeUrlFromName() $result = $method->invoke($command, 'OrdersCreateJob'); $result2 = $method->invoke($command, 'OrdersCreate'); $result3 = $method->invoke($command, 'OrdersCreateCustomJob'); - + $this->assertEquals($result, 'orders-create'); $this->assertEquals($result2, 'orders-create'); $this->assertEquals($result3, 'orders-create-custom'); @@ -55,7 +57,7 @@ public function testShouldMakeUrlFromName() public function testShouldReturnStub() { - $application = new ConsoleApplication; + $application = new ConsoleApplication(); $testedCommand = $this->app->make(WebhookJobMakeCommand::class); $testedCommand->setLaravel($this->app); $application->add($testedCommand); @@ -66,7 +68,7 @@ public function testShouldReturnStub() $method->setAccessible(true); $result = $method->invoke($command); - + $this->assertContains('/stubs/webhook-job.stub', $result); } } diff --git a/tests/Controllers/AuthControllerTest.php b/tests/Controllers/AuthControllerTest.php index 1817e785..a2addb7a 100644 --- a/tests/Controllers/AuthControllerTest.php +++ b/tests/Controllers/AuthControllerTest.php @@ -1,14 +1,15 @@ -hmac = 'a7448f7c42c9bc025b077ac8b73e7600b6f8012719d21cbeb88db66e5dbbd163'; $this->hmacParams = [ - 'hmac' => $this->hmac, - 'shop' => 'example.myshopify.com', - 'code' => '1234678', - 'timestamp' => '1337178173' + 'hmac' => $this->hmac, + 'shop' => 'example.myshopify.com', + 'code' => '1234678', + 'timestamp' => '1337178173', ]; // Stub in our API class - config(['shopify-app.api_class' => new ApiStub]); + config(['shopify-app.api_class' => new ApiStub()]); } public function testLoginTest() @@ -40,7 +41,7 @@ public function testLoginTest() public function testAuthRedirectsBackToLoginWhenNoShop() { $response = $this->post('/authenticate'); - + $response->assertStatus(302); $this->assertEquals('http://localhost/login', $response->headers->get('location')); } @@ -97,17 +98,17 @@ public function testAuthenticateDoesNotFiresJobsWhenNoConfigForThem() public function testAuthenticateDoesFiresJobs() { Queue::fake(); - + config(['shopify-app.webhooks' => [ [ - 'topic' => 'orders/create', - 'address' => 'https://localhost/webhooks/orders-create' - ] + 'topic' => 'orders/create', + 'address' => 'https://localhost/webhooks/orders-create', + ], ]]); config(['shopify-app.scripttags' => [ [ - 'src' => 'https://localhost/scripts/file.js' - ] + 'src' => 'https://localhost/scripts/file.js', + ], ]]); $this->call('get', '/authenticate', $this->hmacParams); @@ -122,13 +123,13 @@ public function testAfterAuthenticateFiresInline() $jobClass = \App\Jobs\AfterAuthenticateJob::class; config(['shopify-app.after_authenticate_job' => [ - 'job' => $jobClass, - 'inline' => true + 'job' => $jobClass, + 'inline' => true, ]]); $method = new ReflectionMethod(AuthController::class, 'afterAuthenticateJob'); $method->setAccessible(true); - $result = $method->invoke(new AuthController); + $result = $method->invoke(new AuthController()); $this->assertEquals(true, $result); Queue::assertNotPushed($jobClass); // since inline == true @@ -140,13 +141,13 @@ public function testAfterAuthenticateFiresDispatched() $jobClass = \App\Jobs\AfterAuthenticateJob::class; config(['shopify-app.after_authenticate_job' => [ - 'job' => $jobClass, - 'inline' => false + 'job' => $jobClass, + 'inline' => false, ]]); $method = new ReflectionMethod(AuthController::class, 'afterAuthenticateJob'); $method->setAccessible(true); - $result = $method->invoke(new AuthController); + $result = $method->invoke(new AuthController()); $this->assertEquals(true, $result); Queue::assertPushed($jobClass); // since inline == false @@ -161,7 +162,7 @@ public function testAfterAuthenticateDoesNotFireForNoConfig() $method = new ReflectionMethod(AuthController::class, 'afterAuthenticateJob'); $method->setAccessible(true); - $result = $method->invoke(new AuthController); + $result = $method->invoke(new AuthController()); $this->assertEquals(false, $result); Queue::assertNotPushed($jobClass); diff --git a/tests/Controllers/BillingControllerTest.php b/tests/Controllers/BillingControllerTest.php index a5cc0e54..66de9568 100644 --- a/tests/Controllers/BillingControllerTest.php +++ b/tests/Controllers/BillingControllerTest.php @@ -1,10 +1,12 @@ - new ApiStub]); + config(['shopify-app.api_class' => new ApiStub()]); // Base shop for all tests here session(['shopify_domain' => 'example.myshopify.com']); @@ -53,18 +55,18 @@ public function testShopDeclinesBilling() public function testReturnsBasePlanDetails() { - $controller = new BillingController; + $controller = new BillingController(); $method = new ReflectionMethod(BillingController::class, 'planDetails'); $method->setAccessible(true); // Based on default config $this->assertEquals( [ - 'name' => config('shopify-app.billing_plan'), - 'price' => config('shopify-app.billing_price'), - 'test' => config('shopify-app.billing_test'), + 'name' => config('shopify-app.billing_plan'), + 'price' => config('shopify-app.billing_price'), + 'test' => config('shopify-app.billing_test'), 'trial_days' => config('shopify-app.billing_trial_days'), - 'return_url' => url(config('shopify-app.billing_redirect')) + 'return_url' => url(config('shopify-app.billing_redirect')), ], $method->invoke($controller, 'planDetails') @@ -73,7 +75,7 @@ public function testReturnsBasePlanDetails() public function testReturnsBaseChargeType() { - $controller = new BillingController; + $controller = new BillingController(); $method = new ReflectionMethod(BillingController::class, 'chargeType'); $method->setAccessible(true); diff --git a/tests/Controllers/HomeControllerTest.php b/tests/Controllers/HomeControllerTest.php index 06e5e00f..81bf2873 100644 --- a/tests/Controllers/HomeControllerTest.php +++ b/tests/Controllers/HomeControllerTest.php @@ -1,7 +1,9 @@ - new ApiStub]); + config(['shopify-app.api_class' => new ApiStub()]); } public function testNoShopSessionShouldRedirectToAuthenticate() @@ -26,7 +28,6 @@ public function testWithMismatchedShopsShouldRedirectToAuthenticate() $this->assertEquals(true, strpos($response->content(), 'Redirecting to http://localhost/authenticate') !== false); } - public function testShopWithSessionShouldLoad() { session(['shopify_domain' => 'example.myshopify.com']); diff --git a/tests/Controllers/WebhookControllerTest.php b/tests/Controllers/WebhookControllerTest.php index 1d497a34..049bc03d 100644 --- a/tests/Controllers/WebhookControllerTest.php +++ b/tests/Controllers/WebhookControllerTest.php @@ -1,9 +1,11 @@ -headers = [ - 'HTTP_CONTENT_TYPE' => 'application/json', + 'HTTP_CONTENT_TYPE' => 'application/json', 'HTTP_X_SHOPIFY_SHOP_DOMAIN' => 'example.myshopify.com', 'HTTP_X_SHOPIFY_HMAC_SHA256' => 'hDJhTqHOY7d5WRlbDl4ehGm/t4kOQKtR+5w6wm+LBQw=', // Matches fixture data and API secret ]; @@ -38,7 +40,6 @@ public function testShouldReturn201ResponseOnSuccess() Queue::assertPushed(\App\Jobs\OrdersCreateJob::class); } - public function testShouldReturnErrorResponseOnFailure() { $response = $this->call( @@ -56,14 +57,14 @@ public function testShouldReturnErrorResponseOnFailure() public function testShouldCaseTypeToClass() { - $controller = new WebhookController; + $controller = new WebhookController(); $method = new ReflectionMethod(WebhookController::class, 'getJobClassFromType'); $method->setAccessible(true); $types = [ - 'orders-create' => 'OrdersCreateJob', + 'orders-create' => 'OrdersCreateJob', 'super-duper-order' => 'SuperDuperOrderJob', - 'order' => 'OrderJob' + 'order' => 'OrderJob', ]; foreach ($types as $type => $className) { @@ -89,8 +90,7 @@ public function testWebhookShouldRecieveData() Queue::assertPushed(\App\Jobs\OrdersCreateJob::class, function ($job) { return $job->shopDomain === 'example.myshopify.com' && $job->data instanceof \stdClass - && $job->data->email === 'jon@doe.ca' - ; + && $job->data->email === 'jon@doe.ca'; }); } } diff --git a/tests/Facades/ShopAppFacadeTest.php b/tests/Facades/ShopAppFacadeTest.php index 02ac0cc7..cbb0c0a4 100644 --- a/tests/Facades/ShopAppFacadeTest.php +++ b/tests/Facades/ShopAppFacadeTest.php @@ -1,10 +1,12 @@ -shop = Shop::find(1); $this->scripttags = [ [ - 'src' => 'https://js-aplenty.com/bar.js', - 'event' => 'onload' - ] + 'src' => 'https://js-aplenty.com/bar.js', + 'event' => 'onload', + ], ]; // Replace with our API - config(['shopify-app.api_class' => new ApiStub]); + config(['shopify-app.api_class' => new ApiStub()]); } public function testJobAcceptsLoad() @@ -52,22 +53,22 @@ public function testJobShouldTestScripttagExistanceMethod() $job, [ // Existing scripttags - (object) ['src' => 'https://js-aplenty.com/bar.js'] + (object) ['src' => 'https://js-aplenty.com/bar.js'], ], [ // Defined scripttag in config - 'src' => 'https://js-aplenty.com/bar.js' + 'src' => 'https://js-aplenty.com/bar.js', ] ); $result_2 = $method->invoke( $job, [ // Existing scripttags - (object) ['src' => 'https://js-aplenty.com/bar.js'] + (object) ['src' => 'https://js-aplenty.com/bar.js'], ], [ // Defined scripttag in config - 'src' => 'https://js-aplenty.com/foo.js' + 'src' => 'https://js-aplenty.com/foo.js', ] ); @@ -82,16 +83,16 @@ public function testJobShouldNotRecreateScripttags() // Scripttag JSON comes from fixture JSON which matches $this->scripttags // so this should be 0 - $this->assertEquals(0, sizeof($created)); + $this->assertEquals(0, count($created)); } public function testJobShouldCreateScripttags() { $scripttags = [ [ - 'src' => 'https://js-aplenty.com/fooy-dooy.js', - 'event' => 'onload' - ] + 'src' => 'https://js-aplenty.com/fooy-dooy.js', + 'event' => 'onload', + ], ]; $job = new ScripttagInstaller($this->shop, $scripttags); @@ -99,7 +100,7 @@ public function testJobShouldCreateScripttags() // $scripttags is new scripttags which does not exist in the JSON fixture // for scripttags, so it should create it - $this->assertEquals(1, sizeof($created)); + $this->assertEquals(1, count($created)); $this->assertEquals($scripttags[0], $created[0]); } } diff --git a/tests/Jobs/WebhookInstallerJobTest.php b/tests/Jobs/WebhookInstallerJobTest.php index a82a55e7..5d056607 100644 --- a/tests/Jobs/WebhookInstallerJobTest.php +++ b/tests/Jobs/WebhookInstallerJobTest.php @@ -1,12 +1,13 @@ -shop = Shop::find(1); $this->webhooks = [ [ - 'topic' => 'orders/create', - 'address' => 'https://localhost/webhooks/orders-create' - ] + 'topic' => 'orders/create', + 'address' => 'https://localhost/webhooks/orders-create', + ], ]; // Stub with our API - config(['shopify-app.api_class' => new ApiStub]); + config(['shopify-app.api_class' => new ApiStub()]); } public function testJobAcceptsLoad() @@ -52,22 +53,22 @@ public function testJobShouldTestWebhookExistanceMethod() $job, [ // Existing webhooks - (object) ['address' => 'http://localhost/webhooks/test'] + (object) ['address' => 'http://localhost/webhooks/test'], ], [ // Defined webhooks in config - 'address' => 'http://localhost/webhooks/test' + 'address' => 'http://localhost/webhooks/test', ] ); $result_2 = $method->invoke( $job, [ // Existing webhooks - (object) ['address' => 'http://localhost/webhooks/test'] + (object) ['address' => 'http://localhost/webhooks/test'], ], [ // Defined webhook in config - 'address' => 'http://localhost/webhooks/test-two' + 'address' => 'http://localhost/webhooks/test-two', ] ); @@ -82,16 +83,16 @@ public function testJobShouldNotRecreateWebhooks() // Webhook JSON comes from fixture JSON which matches $this->webhooks // so this should be 0 - $this->assertEquals(0, sizeof($created)); + $this->assertEquals(0, count($created)); } public function testJobShouldCreateWebhooks() { $webhooks = [ [ - 'topic' => 'orders/create', - 'address' => 'https://localhost/webhooks/orders-create-two' - ] + 'topic' => 'orders/create', + 'address' => 'https://localhost/webhooks/orders-create-two', + ], ]; $job = new WebhookInstaller($this->shop, $webhooks); @@ -99,7 +100,7 @@ public function testJobShouldCreateWebhooks() // $webhooks is new webhooks which does not exist in the JSON fixture // for webhooks, so it should create it - $this->assertEquals(1, sizeof($created)); + $this->assertEquals(1, count($created)); $this->assertEquals($webhooks[0], $created[0]); } } diff --git a/tests/Libraries/BillingPlanTest.php b/tests/Libraries/BillingPlanTest.php index 8bbc2aeb..4ffbc8e4 100644 --- a/tests/Libraries/BillingPlanTest.php +++ b/tests/Libraries/BillingPlanTest.php @@ -1,18 +1,20 @@ - new ApiStub]); + config(['shopify-app.api_class' => new ApiStub()]); // Base shop and plan $this->shop = Shop::find(1); @@ -20,7 +22,7 @@ public function setUp() 'name' => 'Basic Plan', 'price' => 3.00, 'trial_days' => 0, - 'return_url' => 'http://example.com/' + 'return_url' => 'http://example.com/', ]; } diff --git a/tests/Middleware/AuthProxyMiddlewareTest.php b/tests/Middleware/AuthProxyMiddlewareTest.php index 84ddb655..73735ce2 100644 --- a/tests/Middleware/AuthProxyMiddlewareTest.php +++ b/tests/Middleware/AuthProxyMiddlewareTest.php @@ -1,8 +1,10 @@ -queryParams = [ - 'extra' => ['1', '2'], - 'shop' => 'shop-name.myshopify.com', + 'extra' => ['1', '2'], + 'shop' => 'shop-name.myshopify.com', 'path_prefix' => '/apps/awesome_reviews', - 'timestamp' => '1317327555', - 'signature' => 'a9718877bea71c2484f91608a7eaea1532bdf71f5c56825065fa4ccabe549ef3' + 'timestamp' => '1317327555', + 'signature' => 'a9718877bea71c2484f91608a7eaea1532bdf71f5c56825065fa4ccabe549ef3', ]; // Set the app secret to match Shopify's docs @@ -34,7 +36,7 @@ public function testDenysForMissingShop() Input::merge($query); $called = false; - (new AuthProxy)->handle(request(), function ($request) use (&$called) { + (new AuthProxy())->handle(request(), function ($request) use (&$called) { // Should never be called $called = true; }); @@ -47,7 +49,7 @@ public function testRuns() Input::merge($this->queryParams); $called = false; - (new AuthProxy)->handle(request(), function ($request) use (&$called) { + (new AuthProxy())->handle(request(), function ($request) use (&$called) { // Should be called $called = true; }); @@ -66,7 +68,7 @@ public function testDoesNotRunForInvalidSignature() Input::merge($query); $called = false; - (new AuthProxy)->handle(request(), function ($request) use (&$called) { + (new AuthProxy())->handle(request(), function ($request) use (&$called) { // Should never be called $called = true; }); diff --git a/tests/Middleware/AuthShopMiddlewareTest.php b/tests/Middleware/AuthShopMiddlewareTest.php index 8ee034be..4ed849da 100644 --- a/tests/Middleware/AuthShopMiddlewareTest.php +++ b/tests/Middleware/AuthShopMiddlewareTest.php @@ -1,7 +1,9 @@ -handle(request(), function ($request) use (&$called) { + $result = (new AuthShop())->handle(request(), function ($request) use (&$called) { // Should never be called $called = true; }); @@ -24,7 +26,7 @@ public function testShopHasWithAccessShouldPassMiddleware() session(['shopify_domain' => 'example.myshopify.com']); $called = false; - (new AuthShop)->handle(request(), function ($request) use (&$called) { + (new AuthShop())->handle(request(), function ($request) use (&$called) { // Should be called $called = true; }); @@ -39,7 +41,7 @@ public function testShopsWhichDoNotMatchShouldKillSessionAndDirectToReAuthentica Input::merge(['shop' => 'example-different-shop.myshopify.com']); $called = false; - (new AuthShop)->handle(request(), function ($request) use (&$called) { + (new AuthShop())->handle(request(), function ($request) use (&$called) { // Should never be called $called = true; }); diff --git a/tests/Middleware/AuthWebhookMiddlewareTest.php b/tests/Middleware/AuthWebhookMiddlewareTest.php index 21a5aa08..e8fab081 100644 --- a/tests/Middleware/AuthWebhookMiddlewareTest.php +++ b/tests/Middleware/AuthWebhookMiddlewareTest.php @@ -1,7 +1,9 @@ -header('x-shopify-hmac-sha256', '1234'); - (new AuthWebhook)->handle(request(), function ($request) { + (new AuthWebhook())->handle(request(), function ($request) { // ... }); } @@ -27,14 +29,14 @@ public function testDenysForMissingShopHeader() public function testDenysForMissingHmacHeader() { request()->header('x-shopify-shop-domain', 'example.myshopify.com'); - (new AuthWebhook)->handle(request(), function ($request) { + (new AuthWebhook())->handle(request(), function ($request) { }); } public function testRuns() { Queue::fake(); - + $response = $this->call( 'post', '/webhook/orders-create', @@ -42,7 +44,7 @@ public function testRuns() [], [], [ - 'HTTP_CONTENT_TYPE' => 'application/json', + 'HTTP_CONTENT_TYPE' => 'application/json', 'HTTP_X_SHOPIFY_SHOP_DOMAIN' => 'example.myshopify.com', 'HTTP_X_SHOPIFY_HMAC_SHA256' => 'hDJhTqHOY7d5WRlbDl4ehGm/t4kOQKtR+5w6wm+LBQw=', // Matches fixture data and API secret ], @@ -54,7 +56,7 @@ public function testRuns() public function testInvalidHmacWontRun() { Queue::fake(); - + $response = $this->call( 'post', '/webhook/orders-create', @@ -62,11 +64,11 @@ public function testInvalidHmacWontRun() [], [], [ - 'HTTP_CONTENT_TYPE' => 'application/json', + 'HTTP_CONTENT_TYPE' => 'application/json', 'HTTP_X_SHOPIFY_SHOP_DOMAIN' => 'example.myshopify.com', 'HTTP_X_SHOPIFY_HMAC_SHA256' => 'hDJhTqHOY7d5WRlbDl4ehGm/t4kOQKtR+5w6wm+LBQw=', // Matches fixture data and API secret ], - file_get_contents(__DIR__.'/../fixtures/webhook.json') . 'invalid' + file_get_contents(__DIR__.'/../fixtures/webhook.json').'invalid' ); $response->assertStatus(401); } diff --git a/tests/Middleware/BillableMiddlewareTest.php b/tests/Middleware/BillableMiddlewareTest.php index f0e0998e..11adfd37 100644 --- a/tests/Middleware/BillableMiddlewareTest.php +++ b/tests/Middleware/BillableMiddlewareTest.php @@ -1,7 +1,8 @@ - 'new-shop.myshopify.com']); $called = false; - $result = (new Billable)->handle(request(), function ($request) use (&$called) { + $result = (new Billable())->handle(request(), function ($request) use (&$called) { // Should never be called $called = true; }); @@ -29,7 +30,7 @@ public function testEnabledBillingWithPaidShop() session(['shopify_domain' => 'example.myshopify.com']); $called = false; - $result = (new Billable)->handle(request(), function ($request) use (&$called) { + $result = (new Billable())->handle(request(), function ($request) use (&$called) { // Should be called $called = true; }); @@ -44,7 +45,7 @@ public function testEnabledBillingWithGrandfatheredShop() session(['shopify_domain' => 'grandfathered.myshopify.com']); $called = false; - $result = (new Billable)->handle(request(), function ($request) use (&$called) { + $result = (new Billable())->handle(request(), function ($request) use (&$called) { // Should be called $called = true; }); @@ -59,7 +60,7 @@ public function testDisabledBillingShouldPassOn() session(['shopify_domain' => 'example.myshopify.com']); $called = false; - $result = (new Billable)->handle(request(), function ($request) use (&$called) { + $result = (new Billable())->handle(request(), function ($request) use (&$called) { // Should be called $called = true; }); diff --git a/tests/Models/ShopModelTest.php b/tests/Models/ShopModelTest.php index bf096c12..4e01a630 100644 --- a/tests/Models/ShopModelTest.php +++ b/tests/Models/ShopModelTest.php @@ -1,4 +1,6 @@ -shopify_token = '1234'; $shop->save(); } public function testShopShouldSaveAndAllowForMassAssignment() { - $shop = new Shop; + $shop = new Shop(); $shop->shopify_domain = 'hello.myshopify.com'; $shop->shopify_token = '1234'; $shop->save(); diff --git a/tests/ShopifyAppTest.php b/tests/ShopifyAppTest.php index b9690b3e..aaaee770 100644 --- a/tests/ShopifyAppTest.php +++ b/tests/ShopifyAppTest.php @@ -1,10 +1,11 @@ - $responseJSON, - 'status' => 200 + 'body' => $responseJSON, + 'status' => 200, ]; } public function requestAccessToken(string $code) { $filePath = $this->pathToHash('GET', '/admin/access_token.json'); + return json_decode(file_get_contents($filePath))->access_token; } private function pathToHash($method, $path) { $path = str_replace('/', '_', parse_url($path, PHP_URL_PATH)); - $hash = hash('sha1', strtolower($method) . $path); + $hash = hash('sha1', strtolower($method).$path); return __DIR__."/../fixtures/{$hash}.json"; } diff --git a/tests/Stubs/Kernel.php b/tests/Stubs/Kernel.php index 85eff407..75be09ff 100644 --- a/tests/Stubs/Kernel.php +++ b/tests/Stubs/Kernel.php @@ -1,4 +1,6 @@ - \OhMyBrew\ShopifyApp\Middleware\AuthShop::class, 'auth.webhook' => \OhMyBrew\ShopifyApp\Middleware\AuthWebhook::class, 'auth.proxy' => \OhMyBrew\ShopifyApp\Middleware\AuthProxy::class, - 'billable' => \OhMyBrew\ShopifyApp\Middleware\Billable::class + 'billable' => \OhMyBrew\ShopifyApp\Middleware\Billable::class, ]; } diff --git a/tests/Stubs/OrdersCreateJobStub.php b/tests/Stubs/OrdersCreateJobStub.php index 464c3207..6a8d9f52 100644 --- a/tests/Stubs/OrdersCreateJobStub.php +++ b/tests/Stubs/OrdersCreateJobStub.php @@ -1,10 +1,12 @@ -set('database.connections.sqlite', [ 'driver' => 'sqlite', 'database' => ':memory:', - 'prefix' => '' + 'prefix' => '', ]); } @@ -59,21 +61,21 @@ protected function setupDatabase($app) protected function seedDatabase() { // Paid shop, not grandfathered - $shop = new Shop; + $shop = new Shop(); $shop->shopify_domain = 'example.myshopify.com'; $shop->shopify_token = '1234'; $shop->charge_id = 678298290; $shop->save(); // Non-paid shop, grandfathered - $shop = new Shop; + $shop = new Shop(); $shop->shopify_domain = 'grandfathered.myshopify.com'; $shop->shopify_token = '1234'; $shop->grandfathered = true; $shop->save(); // New shop... non-paid, not grandfathered - $shop = new Shop; + $shop = new Shop(); $shop->shopify_domain = 'new-shop.myshopify.com'; $shop->shopify_token = '1234'; $shop->save(); From f136cb27ab161baead9af2ace5792c4a01dc80da Mon Sep 17 00:00:00 2001 From: Tyler King Date: Thu, 25 Jan 2018 14:18:13 -0330 Subject: [PATCH 16/17] Readme adjustment --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 41dcfb9b..c5428e2d 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,10 @@ [![Build Status](https://secure.travis-ci.org/ohmybrew/laravel-shopify.png?branch=master)](http://travis-ci.org/ohmybrew/laravel-shopify) [![Coverage Status](https://coveralls.io/repos/github/ohmybrew/laravel-shopify/badge.svg?branch=master)](https://coveralls.io/github/ohmybrew/laravel-shopify?branch=master) +[![StyleCI](https://styleci.io/repos/96462257/shield?branch=master)](https://styleci.io/repos/96462257) [![License](https://poser.pugx.org/ohmybrew/laravel-shopify/license)](https://packagist.org/packages/ohmybrew/laravel-shopify) -A Laravel package for aiding in Shopify App development, similar to `shopify_app` for Rails. Works for Laravel 5.4-5.5. +A full-featured Laravel package for aiding in Shopify App development, similar to `shopify_app` for Rails. Works for Laravel 5.4-5.5. ![Screenshot](https://github.com/ohmybrew/laravel-shopify/raw/master/screenshot.png) @@ -27,6 +28,7 @@ __*__ *Wiki pages* - [x] Provide assistance in developing Shopify apps with Laravel - [x] Integration with Shopify API - [x] Authentication & installation for shops +- [x] Billing integration built-in for single and recurring application charges - [x] Auto install app webhooks and scripttags thorugh background jobs - [x] Provide basic ESDK views - [x] Handles and processes incoming webhooks From 65f2e4aa38d4e916a0dd794ef18b3ddbf6f5405d Mon Sep 17 00:00:00 2001 From: Tyler King Date: Thu, 25 Jan 2018 14:22:23 -0330 Subject: [PATCH 17/17] README update --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index c5428e2d..d1c1a2de 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,8 @@ __*__ *Wiki pages* For full resources on this package, see the [wiki](https://github.com/ohmybrew/laravel-shopify/wiki). +For internal documentation, you may run `phpdoc` (hopefully host this online at somepoint). + ## LICENSE This project is released under the MIT license.