Skip to content
This repository was archived by the owner on Nov 30, 2022. It is now read-only.

Commit 7d27600

Browse files
authored
Usage Charge "ID on Null" Fix (#1210)
1 parent 00f5958 commit 7d27600

File tree

2 files changed

+102
-6
lines changed

2 files changed

+102
-6
lines changed

src/Traits/BillingController.php

+20-3
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,13 @@
1010
use Osiset\ShopifyApp\Actions\ActivatePlan;
1111
use Osiset\ShopifyApp\Actions\ActivateUsageCharge;
1212
use Osiset\ShopifyApp\Actions\GetPlanUrl;
13+
use Osiset\ShopifyApp\Exceptions\ChargeNotRecurringException;
14+
use Osiset\ShopifyApp\Exceptions\MissingShopDomainException;
1315
use Osiset\ShopifyApp\Http\Requests\StoreUsageCharge;
1416
use Osiset\ShopifyApp\Objects\Transfers\UsageChargeDetails as UsageChargeDetailsTransfer;
1517
use Osiset\ShopifyApp\Objects\Values\ChargeReference;
1618
use Osiset\ShopifyApp\Objects\Values\NullablePlanId;
19+
use Osiset\ShopifyApp\Objects\Values\NullableShopDomain;
1720
use Osiset\ShopifyApp\Objects\Values\PlanId;
1821
use Osiset\ShopifyApp\Objects\Values\ShopDomain;
1922
use Osiset\ShopifyApp\Storage\Queries\Shop as ShopQuery;
@@ -100,11 +103,25 @@ public function process(
100103
*
101104
* @param StoreUsageCharge $request The verified request.
102105
* @param ActivateUsageCharge $activateUsageCharge The action for activating a usage charge.
106+
* @param ShopQuery $shopQuery The shop querier.
107+
*
108+
* @throws MissingShopDomainException|ChargeNotRecurringException
103109
*
104110
* @return RedirectResponse
105111
*/
106-
public function usageCharge(StoreUsageCharge $request, ActivateUsageCharge $activateUsageCharge): RedirectResponse
107-
{
112+
public function usageCharge(
113+
StoreUsageCharge $request,
114+
ActivateUsageCharge $activateUsageCharge,
115+
ShopQuery $shopQuery
116+
): RedirectResponse {
117+
$shopDomain = NullableShopDomain::fromNative($request->get('shop'));
118+
// Get the shop from the shop param after it has been validated.
119+
if ($shopDomain->isNull()) {
120+
throw new MissingShopDomainException('Shop parameter is missing from request');
121+
}
122+
$shop = $shopQuery->getByDomain($shopDomain);
123+
124+
// Valid the request params.
108125
$validated = $request->validated();
109126

110127
// Create the transfer object
@@ -113,7 +130,7 @@ public function usageCharge(StoreUsageCharge $request, ActivateUsageCharge $acti
113130
$ucd->description = $validated['description'];
114131

115132
// Activate and save the usage charge
116-
$activateUsageCharge($request->user()->getId(), $ucd);
133+
$activateUsageCharge($shop->getId(), $ucd);
117134

118135
// All done, return with success
119136
return isset($validated['redirect'])

tests/Traits/BillingControllerTest.php

+82-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Osiset\ShopifyApp\Test\Traits;
44

55
use Illuminate\Auth\AuthManager;
6+
use Osiset\ShopifyApp\Exceptions\MissingShopDomainException;
67
use Osiset\ShopifyApp\Storage\Models\Charge;
78
use Osiset\ShopifyApp\Storage\Models\Plan;
89
use Osiset\ShopifyApp\Test\Stubs\Api as ApiStub;
@@ -112,7 +113,7 @@ public function testUsageChargeSuccess(): void
112113
$response = $this->call(
113114
'post',
114115
'/billing/usage-charge',
115-
array_merge($data, ['signature' => $signature->toNative()])
116+
array_merge($data, ['signature' => $signature->toNative(), 'shop' => $shop->name])
116117
);
117118
$response->assertRedirect($data['redirect']);
118119
$response->assertSessionHas('success');
@@ -125,13 +126,13 @@ public function testUsageChargeSuccess(): void
125126
$response = $this->call(
126127
'post',
127128
'/billing/usage-charge',
128-
array_merge($data, ['signature' => $signature->toNative()])
129+
array_merge($data, ['signature' => $signature->toNative(), 'shop' => $shop->name])
129130
);
130131
$response->assertRedirect('http://localhost');
131132
$response->assertSessionHas('success');
132133
}
133134

134-
public function testReturnToSettingScreenNoPlan()
135+
public function testReturnToSettingScreenNoPlan(): void
135136
{
136137
// Set up a shop
137138
$shop = factory($this->model)->create([
@@ -149,4 +150,82 @@ public function testReturnToSettingScreenNoPlan()
149150
//Confirm we get sent back to the homepage of the app
150151
$response->assertRedirect('https://example-app.com?shop='.$shop->name);
151152
}
153+
154+
public function testUsageChargeSuccessWithShopParam(): void
155+
{
156+
// Stub the responses
157+
ApiStub::stubResponses([
158+
'post_recurring_application_charges_usage_charges_alt',
159+
]);
160+
161+
// Create the shop
162+
$plan = factory(Util::getShopifyConfig('models.plan', Plan::class))->states('type_recurring')->create();
163+
$shop = factory($this->model)->create([
164+
'plan_id' => $plan->getId()->toNative(),
165+
]);
166+
factory(Util::getShopifyConfig('models.charge', Charge::class))->states('type_recurring')->create([
167+
'plan_id' => $plan->getId()->toNative(),
168+
'user_id' => $shop->getId()->toNative(),
169+
]);
170+
171+
// Login the shop
172+
$this->auth->login($shop);
173+
174+
// Set up the data for the usage charge and the signature for it
175+
$secret = $this->app['config']->get('shopify-app.api_secret');
176+
$data = ['description' => 'One email', 'price' => 1.00, 'redirect' => 'https://localhost/usage-success'];
177+
$signature = Util::createHmac(['data' => $data, 'buildQuery' => true], $secret);
178+
179+
// Run the call
180+
$response = $this->call(
181+
'post',
182+
'/billing/usage-charge',
183+
array_merge($data, ['signature' => $signature->toNative(), 'shop' => $shop->name])
184+
);
185+
$response->assertRedirect($data['redirect']);
186+
$response->assertSessionHas('success');
187+
$this->assertDatabaseHas('charges', ['description' => 'One email']);
188+
}
189+
190+
public function testUsageChargeFailWithoutShopParam(): void
191+
{
192+
$this->withoutExceptionHandling();
193+
$this->expectException(MissingShopDomainException::class);
194+
195+
// Stub the responses
196+
ApiStub::stubResponses([
197+
'post_recurring_application_charges_usage_charges_alt',
198+
]);
199+
200+
// Create the shop
201+
$plan = factory(Util::getShopifyConfig('models.plan', Plan::class))
202+
->states('type_recurring')->create();
203+
$shop = factory($this->model)->create([
204+
'plan_id' => $plan->getId()->toNative(),
205+
]);
206+
factory(Util::getShopifyConfig('models.charge', Charge::class))
207+
->states('type_recurring')->create([
208+
'plan_id' => $plan->getId()->toNative(),
209+
'user_id' => $shop->getId()->toNative(),
210+
]);
211+
212+
// Login the shop
213+
$this->auth->login($shop);
214+
215+
// Set up the data for the usage charge and the signature for it
216+
$secret = $this->app['config']->get('shopify-app.api_secret');
217+
$data = [
218+
'description' => 'One email',
219+
'price' => 1.00,
220+
'redirect' => 'https://localhost/usage-success',];
221+
$signature = Util::createHmac(['data' => $data, 'buildQuery' => true], $secret);
222+
223+
// Run the call
224+
$response = $this->call(
225+
'post',
226+
'/billing/usage-charge',
227+
array_merge($data, ['signature' => $signature->toNative()])
228+
);
229+
$this->assertDatabaseMissing('charges', ['description' => 'One email']);
230+
}
152231
}

0 commit comments

Comments
 (0)