diff --git a/src/ShopifyApp/Libraries/BillingPlan.php b/src/ShopifyApp/Libraries/BillingPlan.php index fa6dc802..7e1bad98 100644 --- a/src/ShopifyApp/Libraries/BillingPlan.php +++ b/src/ShopifyApp/Libraries/BillingPlan.php @@ -56,11 +56,13 @@ public function __construct(Shop $shop, string $chargeType = 'recurring') * * @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. + * '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. + * 'capped_amount' => (float) Capped price if using UsageCharge API. + * 'terms' => (string) Terms for the usage. Required if using capped_amount. * ] * * @return $this @@ -143,19 +145,26 @@ public function getConfirmationUrl() throw new Exception('Plan details are missing for confirmation URL request.'); } + // Build the charge array + $chargeDetails = [ + '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'], + ]; + + // Handle capped amounts for UsageCharge API + if (isset($this->details['capped_amount'])) { + $chargeDetails['capped_amount'] = $this->details['capped_amount']; + $chargeDetails['terms'] = $this->details['terms']; + } + // Begin the charge request $charge = $this->shop->api()->request( 'POST', "/admin/{$this->chargeType}s.json", - [ - "{$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'], - 'price' => $this->details['price'], - 'return_url' => $this->details['return_url'], - ], - ] + ["{$this->chargeType}" => $chargeDetails] )->body->{$this->chargeType}; return $charge->confirmation_url; diff --git a/src/ShopifyApp/Traits/BillingControllerTrait.php b/src/ShopifyApp/Traits/BillingControllerTrait.php index 499ffbb0..82102185 100644 --- a/src/ShopifyApp/Traits/BillingControllerTrait.php +++ b/src/ShopifyApp/Traits/BillingControllerTrait.php @@ -65,13 +65,21 @@ public function process() */ protected function planDetails() { - return [ + $plan = [ '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')), ]; + + // Handle capped amounts for UsageCharge API + if (config('shopify-app.billing_capped_amount')) { + $plan['capped_amount'] = config('shopify-app.billing_capped_amount'); + $plan['terms'] = config('shopify-app.billing_terms'); + } + + return $plan; } /** diff --git a/src/ShopifyApp/resources/config/shopify-app.php b/src/ShopifyApp/resources/config/shopify-app.php index fa33d81f..728026d3 100644 --- a/src/ShopifyApp/resources/config/shopify-app.php +++ b/src/ShopifyApp/resources/config/shopify-app.php @@ -184,6 +184,28 @@ 'billing_redirect' => env('SHOPIFY_BILLING_REDIRECT', '/billing/process'), + /* + |-------------------------------------------------------------------------- + | Billing Capped Amount + |-------------------------------------------------------------------------- + | + | The capped price for charging a customer when using the UsageCharge API. + | + */ + + 'billing_capped_amount' => env('SHOPIFY_BILLING_CAPPED_AMOUNT'), + + /* + |-------------------------------------------------------------------------- + | Billing Terms + |-------------------------------------------------------------------------- + | + | Terms for the usage. Required if using capped amount. + | + */ + + 'billing_terms' => env('SHOPIFY_BILLING_TERMS'), + /* |-------------------------------------------------------------------------- | Shopify Webhooks diff --git a/tests/Controllers/BillingControllerTest.php b/tests/Controllers/BillingControllerTest.php index 66de9568..20bcf47c 100644 --- a/tests/Controllers/BillingControllerTest.php +++ b/tests/Controllers/BillingControllerTest.php @@ -67,7 +67,30 @@ public function testReturnsBasePlanDetails() '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 testReturnsBasePlanDetailsWithUsage() + { + config(['shopify-app.billing_capped_amount' => 100.00]); + config(['shopify-app.billing_terms' => '$1 for 100 emails.']); + + $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'), + 'capped_amount' => config('shopify-app.billing_capped_amount'), + 'terms' => config('shopify-app.billing_terms'), + 'return_url' => url(config('shopify-app.billing_redirect')), ], $method->invoke($controller, 'planDetails') ); diff --git a/tests/Libraries/BillingPlanTest.php b/tests/Libraries/BillingPlanTest.php index 4ffbc8e4..16be9d53 100644 --- a/tests/Libraries/BillingPlanTest.php +++ b/tests/Libraries/BillingPlanTest.php @@ -36,6 +36,20 @@ public function testShouldReturnConfirmationUrl() ); } + public function testShouldReturnConfirmationUrlWhenUsageIsEnabled() + { + $plan = array_merge($this->plan, [ + 'capped_amount' => 100.00, + 'terms' => '$1 for 500 emails', + ]); + $url = (new BillingPlan($this->shop))->setDetails($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.