From cc2a1ea9b29610cf1d14f590ed5511e34e504eaa Mon Sep 17 00:00:00 2001
From: Gio <ggelashvili@gmail.com>
Date: Wed, 24 May 2023 16:50:59 -0400
Subject: [PATCH 1/7] Makes Billable middleware useful for SPAs. If we are in
 SPA mode & the request is ajax it will respond with the proper redirect URL

---
 src/Http/Middleware/Billable.php | 39 +++++++++++++++++++++-----------
 1 file changed, 26 insertions(+), 13 deletions(-)

diff --git a/src/Http/Middleware/Billable.php b/src/Http/Middleware/Billable.php
index 9e3a1afa..671f3a86 100644
--- a/src/Http/Middleware/Billable.php
+++ b/src/Http/Middleware/Billable.php
@@ -27,23 +27,36 @@ class Billable
      */
     public function handle(Request $request, Closure $next)
     {
-        if (Util::useNativeAppBridge() === false) {
-            throw new RuntimeException('You cannot use Billable middleware with SPA mode');
+        if (Util::getShopifyConfig('billing_enabled') !== true) {
+            return $next($request);
         }
 
-        if (Util::getShopifyConfig('billing_enabled') === true) {
-            /** @var $shop IShopModel */
-            $shop = auth()->user();
-            if (!$shop->plan && !$shop->isFreemium() && !$shop->isGrandfathered()) {
-                // They're not grandfathered in, and there is no charge or charge was declined... redirect to billing
-                return Redirect::route(
-                    Util::getShopifyConfig('route_names.billing'),
-                    array_merge($request->input(), [
-                        'shop' => $shop->getDomain()->toNative(),
-                        'host' => $request->get('host'),
-                    ])
+        // Proceed if we are on SPA mode & it's a non ajax request
+        if (! Util::useNativeAppBridge() && ! $request->ajax()) {
+            return $next($request);
+        }
+
+        /** @var $shop IShopModel */
+        $shop = auth()->user();
+
+        if (! $shop->plan && ! $shop->isFreemium() && ! $shop->isGrandfathered()) {
+            $args = [
+                Util::getShopifyConfig('route_names.billing'),
+                array_merge($request->input(), [
+                    'shop' => $shop->getDomain()->toNative(),
+                    'host' => $request->get('host'),
+                ]),
+            ];
+
+            if ($request->ajax()) {
+                return response()->json(
+                    ['forceRedirectUrl' => route(...$args)],
+                    403
                 );
             }
+
+            // They're not grandfathered in, and there is no charge or charge was declined... redirect to billing
+            return Redirect::route(...$args);
         }
 
         // Move on, everything's fine

From 041844b7ab3de26428b3e960e636a78e418a2c3d Mon Sep 17 00:00:00 2001
From: Gio <ggelashvili@gmail.com>
Date: Thu, 25 May 2023 14:40:32 -0400
Subject: [PATCH 2/7] Removes unused use statement

---
 src/Http/Middleware/Billable.php | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/Http/Middleware/Billable.php b/src/Http/Middleware/Billable.php
index 671f3a86..08f1d4e3 100644
--- a/src/Http/Middleware/Billable.php
+++ b/src/Http/Middleware/Billable.php
@@ -8,7 +8,6 @@
 use Illuminate\Support\Facades\Redirect;
 use Osiset\ShopifyApp\Contracts\ShopModel as IShopModel;
 use Osiset\ShopifyApp\Util;
-use RuntimeException;
 
 /**
  * Responsible for ensuring the shop is being billed.

From 3e9667ff9bb1ff6a706fad4d3acf74f8972bdb6c Mon Sep 17 00:00:00 2001
From: Gio <ggelashvili@gmail.com>
Date: Mon, 29 May 2023 19:11:11 -0400
Subject: [PATCH 3/7] Improves readability & adds check for when user/shop is
 null

---
 src/Http/Middleware/Billable.php | 43 ++++++++++++++++----------------
 1 file changed, 21 insertions(+), 22 deletions(-)

diff --git a/src/Http/Middleware/Billable.php b/src/Http/Middleware/Billable.php
index 08f1d4e3..90924dd8 100644
--- a/src/Http/Middleware/Billable.php
+++ b/src/Http/Middleware/Billable.php
@@ -18,11 +18,11 @@ class Billable
      * Checks if a shop has paid for access.
      *
      * @param Request $request The request object.
-     * @param Closure $next The next action.
-     *
-     *@throws Exception
+     * @param Closure $next    The next action.
      *
      * @return mixed
+     * @throws Exception
+     *
      */
     public function handle(Request $request, Closure $next)
     {
@@ -38,27 +38,26 @@ public function handle(Request $request, Closure $next)
         /** @var $shop IShopModel */
         $shop = auth()->user();
 
-        if (! $shop->plan && ! $shop->isFreemium() && ! $shop->isGrandfathered()) {
-            $args = [
-                Util::getShopifyConfig('route_names.billing'),
-                array_merge($request->input(), [
-                    'shop' => $shop->getDomain()->toNative(),
-                    'host' => $request->get('host'),
-                ]),
-            ];
-
-            if ($request->ajax()) {
-                return response()->json(
-                    ['forceRedirectUrl' => route(...$args)],
-                    403
-                );
-            }
+        // if shop has plan or is on freemium or is grandfathered then move on with request
+        if (! $shop || $shop->plan || $shop->isFreemium() || $shop->isGrandfathered()) {
+            return $next($request);
+        }
 
-            // They're not grandfathered in, and there is no charge or charge was declined... redirect to billing
-            return Redirect::route(...$args);
+        $args = [
+            Util::getShopifyConfig('route_names.billing'),
+            array_merge($request->input(), [
+                'shop' => $shop->getDomain()->toNative(),
+                'host' => $request->get('host'),
+            ]),
+        ];
+
+        if ($request->ajax()) {
+            return response()->json(
+                ['forceRedirectUrl' => route(...$args)],
+                403
+            );
         }
 
-        // Move on, everything's fine
-        return $next($request);
+        return Redirect::route(...$args);
     }
 }

From c62ea4510e7f48de0dbd25301ea8f2066616cc11 Mon Sep 17 00:00:00 2001
From: Gio <ggelashvili@gmail.com>
Date: Thu, 1 Jun 2023 15:49:19 -0400
Subject: [PATCH 4/7] Fixes code style

---
 src/Http/Middleware/Billable.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/Http/Middleware/Billable.php b/src/Http/Middleware/Billable.php
index 90924dd8..dc2ab4b5 100644
--- a/src/Http/Middleware/Billable.php
+++ b/src/Http/Middleware/Billable.php
@@ -20,9 +20,9 @@ class Billable
      * @param Request $request The request object.
      * @param Closure $next    The next action.
      *
-     * @return mixed
      * @throws Exception
      *
+     * @return mixed
      */
     public function handle(Request $request, Closure $next)
     {

From f650925f7f29c3c0bf3b4a4ccdc17ab5d4f0ceec Mon Sep 17 00:00:00 2001
From: Gio <ggelashvili@gmail.com>
Date: Thu, 22 Jun 2023 12:44:57 -0400
Subject: [PATCH 5/7] Adds test cases

---
 tests/Http/Middleware/BillableTest.php | 46 ++++++++++++++++++++++++++
 1 file changed, 46 insertions(+)

diff --git a/tests/Http/Middleware/BillableTest.php b/tests/Http/Middleware/BillableTest.php
index ff3de70b..6f81c4dd 100644
--- a/tests/Http/Middleware/BillableTest.php
+++ b/tests/Http/Middleware/BillableTest.php
@@ -8,6 +8,7 @@
 use Osiset\ShopifyApp\Storage\Models\Plan;
 use Osiset\ShopifyApp\Test\TestCase;
 use Osiset\ShopifyApp\Util;
+use Illuminate\Http\Request;
 
 class BillableTest extends TestCase
 {
@@ -100,4 +101,49 @@ public function testDisabledBillingShouldPassOn(): void
 
         $this->assertTrue($result[0]);
     }
+
+    public function testNoNativeAppBridgeAndNoAjaxRequest(): void
+    {
+        $plan = factory(Util::getShopifyConfig('models.plan', Plan::class))->states('type_recurring')->create();
+        $shop = factory($this->model)->create(['plan_id' => $plan->getId()->toNative()]);
+
+        factory(Util::getShopifyConfig('models.charge', Charge::class))->states('type_recurring')->create(
+            [
+                'plan_id' => $plan->getId()->toNative(),
+                'user_id' => $shop->getId()->toNative(),
+            ]
+        );
+
+        $this->auth->login($shop);
+        $this->app['config']->set('shopify-app.billing_enabled', true);
+        $this->app['config']->set('shopify-app.frontend_engine', 'REACT');
+
+        $request = Request::create('/test', 'GET');
+
+        $this->assertFalse($request->ajax());
+
+        $result = $this->runMiddleware(BillableMiddleware::class, $request);
+
+        $this->assertTrue($result[0]);
+    }
+
+    public function testAjaxRequest(): void
+    {
+        $shop = factory($this->model)->create();
+
+        $this->auth->login($shop);
+        $this->app['config']->set('shopify-app.billing_enabled', true);
+        $this->app['config']->set('shopify-app.frontend_engine', 'REACT');
+
+        $request = Request::create('/test', 'GET');
+        $request->headers->set('X-Requested-With', 'XMLHttpRequest');
+
+        $this->assertTrue($request->ajax());
+
+        $response = $this->runMiddleware(BillableMiddleware::class, $request);
+
+        $this->assertFalse($response[0]);
+        $this->assertEquals(403, $response[1]->getStatusCode());
+        $this->assertArrayHasKey('forceRedirectUrl', $response[1]->getData(true));
+    }
 }

From 05ae0412eefa8c837af8b67bd03191da482f5b22 Mon Sep 17 00:00:00 2001
From: Gio <ggelashvili@gmail.com>
Date: Thu, 22 Jun 2023 13:28:06 -0400
Subject: [PATCH 6/7] WIP

---
 tests/Http/Middleware/BillableTest.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/Http/Middleware/BillableTest.php b/tests/Http/Middleware/BillableTest.php
index 6f81c4dd..ffb71f9a 100644
--- a/tests/Http/Middleware/BillableTest.php
+++ b/tests/Http/Middleware/BillableTest.php
@@ -3,12 +3,12 @@
 namespace Osiset\ShopifyApp\Test\Http\Middleware;
 
 use Illuminate\Auth\AuthManager;
+use Illuminate\Http\Request;
 use Osiset\ShopifyApp\Http\Middleware\Billable as BillableMiddleware;
 use Osiset\ShopifyApp\Storage\Models\Charge;
 use Osiset\ShopifyApp\Storage\Models\Plan;
 use Osiset\ShopifyApp\Test\TestCase;
 use Osiset\ShopifyApp\Util;
-use Illuminate\Http\Request;
 
 class BillableTest extends TestCase
 {

From c9b80479e1e7e01e57da637573b9d5357d18ecc1 Mon Sep 17 00:00:00 2001
From: Gio <ggelashvili@gmail.com>
Date: Wed, 12 Jul 2023 18:01:38 -0400
Subject: [PATCH 7/7] WIP

---
 src/Http/Middleware/Billable.php       | 2 +-
 tests/Http/Middleware/BillableTest.php | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/Http/Middleware/Billable.php b/src/Http/Middleware/Billable.php
index dc2ab4b5..20479ab5 100644
--- a/src/Http/Middleware/Billable.php
+++ b/src/Http/Middleware/Billable.php
@@ -54,7 +54,7 @@ public function handle(Request $request, Closure $next)
         if ($request->ajax()) {
             return response()->json(
                 ['forceRedirectUrl' => route(...$args)],
-                403
+                402
             );
         }
 
diff --git a/tests/Http/Middleware/BillableTest.php b/tests/Http/Middleware/BillableTest.php
index ffb71f9a..5c1d1978 100644
--- a/tests/Http/Middleware/BillableTest.php
+++ b/tests/Http/Middleware/BillableTest.php
@@ -143,7 +143,7 @@ public function testAjaxRequest(): void
         $response = $this->runMiddleware(BillableMiddleware::class, $request);
 
         $this->assertFalse($response[0]);
-        $this->assertEquals(403, $response[1]->getStatusCode());
+        $this->assertEquals(402, $response[1]->getStatusCode());
         $this->assertArrayHasKey('forceRedirectUrl', $response[1]->getData(true));
     }
 }