diff --git a/src/Traits/BillingController.php b/src/Traits/BillingController.php index 5b07e29d..1e165508 100644 --- a/src/Traits/BillingController.php +++ b/src/Traits/BillingController.php @@ -10,10 +10,13 @@ use Osiset\ShopifyApp\Actions\ActivatePlan; use Osiset\ShopifyApp\Actions\ActivateUsageCharge; use Osiset\ShopifyApp\Actions\GetPlanUrl; +use Osiset\ShopifyApp\Exceptions\ChargeNotRecurringException; +use Osiset\ShopifyApp\Exceptions\MissingShopDomainException; use Osiset\ShopifyApp\Http\Requests\StoreUsageCharge; use Osiset\ShopifyApp\Objects\Transfers\UsageChargeDetails as UsageChargeDetailsTransfer; use Osiset\ShopifyApp\Objects\Values\ChargeReference; use Osiset\ShopifyApp\Objects\Values\NullablePlanId; +use Osiset\ShopifyApp\Objects\Values\NullableShopDomain; use Osiset\ShopifyApp\Objects\Values\PlanId; use Osiset\ShopifyApp\Objects\Values\ShopDomain; use Osiset\ShopifyApp\Storage\Queries\Shop as ShopQuery; @@ -100,11 +103,25 @@ public function process( * * @param StoreUsageCharge $request The verified request. * @param ActivateUsageCharge $activateUsageCharge The action for activating a usage charge. + * @param ShopQuery $shopQuery The shop querier. + * + * @throws MissingShopDomainException|ChargeNotRecurringException * * @return RedirectResponse */ - public function usageCharge(StoreUsageCharge $request, ActivateUsageCharge $activateUsageCharge): RedirectResponse - { + public function usageCharge( + StoreUsageCharge $request, + ActivateUsageCharge $activateUsageCharge, + ShopQuery $shopQuery + ): RedirectResponse { + $shopDomain = NullableShopDomain::fromNative($request->get('shop')); + // Get the shop from the shop param after it has been validated. + if ($shopDomain->isNull()) { + throw new MissingShopDomainException('Shop parameter is missing from request'); + } + $shop = $shopQuery->getByDomain($shopDomain); + + // Valid the request params. $validated = $request->validated(); // Create the transfer object @@ -113,7 +130,7 @@ public function usageCharge(StoreUsageCharge $request, ActivateUsageCharge $acti $ucd->description = $validated['description']; // Activate and save the usage charge - $activateUsageCharge($request->user()->getId(), $ucd); + $activateUsageCharge($shop->getId(), $ucd); // All done, return with success return isset($validated['redirect']) diff --git a/tests/Traits/BillingControllerTest.php b/tests/Traits/BillingControllerTest.php index eef18804..5d541dab 100644 --- a/tests/Traits/BillingControllerTest.php +++ b/tests/Traits/BillingControllerTest.php @@ -3,6 +3,7 @@ namespace Osiset\ShopifyApp\Test\Traits; use Illuminate\Auth\AuthManager; +use Osiset\ShopifyApp\Exceptions\MissingShopDomainException; use Osiset\ShopifyApp\Storage\Models\Charge; use Osiset\ShopifyApp\Storage\Models\Plan; use Osiset\ShopifyApp\Test\Stubs\Api as ApiStub; @@ -112,7 +113,7 @@ public function testUsageChargeSuccess(): void $response = $this->call( 'post', '/billing/usage-charge', - array_merge($data, ['signature' => $signature->toNative()]) + array_merge($data, ['signature' => $signature->toNative(), 'shop' => $shop->name]) ); $response->assertRedirect($data['redirect']); $response->assertSessionHas('success'); @@ -125,13 +126,13 @@ public function testUsageChargeSuccess(): void $response = $this->call( 'post', '/billing/usage-charge', - array_merge($data, ['signature' => $signature->toNative()]) + array_merge($data, ['signature' => $signature->toNative(), 'shop' => $shop->name]) ); $response->assertRedirect('http://localhost'); $response->assertSessionHas('success'); } - public function testReturnToSettingScreenNoPlan() + public function testReturnToSettingScreenNoPlan(): void { // Set up a shop $shop = factory($this->model)->create([ @@ -149,4 +150,82 @@ public function testReturnToSettingScreenNoPlan() //Confirm we get sent back to the homepage of the app $response->assertRedirect('https://example-app.com?shop='.$shop->name); } + + public function testUsageChargeSuccessWithShopParam(): void + { + // Stub the responses + ApiStub::stubResponses([ + 'post_recurring_application_charges_usage_charges_alt', + ]); + + // Create the shop + $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(), + ]); + + // Login the shop + $this->auth->login($shop); + + // Set up the data for the usage charge and the signature for it + $secret = $this->app['config']->get('shopify-app.api_secret'); + $data = ['description' => 'One email', 'price' => 1.00, 'redirect' => 'https://localhost/usage-success']; + $signature = Util::createHmac(['data' => $data, 'buildQuery' => true], $secret); + + // Run the call + $response = $this->call( + 'post', + '/billing/usage-charge', + array_merge($data, ['signature' => $signature->toNative(), 'shop' => $shop->name]) + ); + $response->assertRedirect($data['redirect']); + $response->assertSessionHas('success'); + $this->assertDatabaseHas('charges', ['description' => 'One email']); + } + + public function testUsageChargeFailWithoutShopParam(): void + { + $this->withoutExceptionHandling(); + $this->expectException(MissingShopDomainException::class); + + // Stub the responses + ApiStub::stubResponses([ + 'post_recurring_application_charges_usage_charges_alt', + ]); + + // Create the shop + $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(), + ]); + + // Login the shop + $this->auth->login($shop); + + // Set up the data for the usage charge and the signature for it + $secret = $this->app['config']->get('shopify-app.api_secret'); + $data = [ + 'description' => 'One email', + 'price' => 1.00, + 'redirect' => 'https://localhost/usage-success',]; + $signature = Util::createHmac(['data' => $data, 'buildQuery' => true], $secret); + + // Run the call + $response = $this->call( + 'post', + '/billing/usage-charge', + array_merge($data, ['signature' => $signature->toNative()]) + ); + $this->assertDatabaseMissing('charges', ['description' => 'One email']); + } }