From 1ed881f34f452bff139933edd273ee2df9cdb8b9 Mon Sep 17 00:00:00 2001 From: Lukas Kahwe Smith Date: Mon, 7 Oct 2019 10:07:50 +0200 Subject: [PATCH 01/12] allow laravel 6 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 27fe171..5a16fb2 100644 --- a/composer.json +++ b/composer.json @@ -10,7 +10,7 @@ } ], "require": { - "laravel/framework": "5.*", + "laravel/framework": "5.*|6.*", "stripe/stripe-php": "4.*|5.*|6.*" }, "autoload": { From b807c773269cd334b8454cca71256ea6f413e1b9 Mon Sep 17 00:00:00 2001 From: Lukas Kahwe Smith Date: Mon, 7 Oct 2019 11:22:23 +0200 Subject: [PATCH 02/12] update API call --- src/Console/Commands/CreateStripePlans.php | 23 +++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/Console/Commands/CreateStripePlans.php b/src/Console/Commands/CreateStripePlans.php index a0ade3f..da64aa6 100644 --- a/src/Console/Commands/CreateStripePlans.php +++ b/src/Console/Commands/CreateStripePlans.php @@ -63,15 +63,20 @@ protected function createStripePlans($plans) $this->line('Stripe plan ' . $plan->id . ' already exists'); } else { Stripe\Plan::create([ - 'id' => $plan->id, - 'name' => Spark::$details['product'] . ' ' . $plan->name . ' (' . - Cashier::usesCurrencySymbol() . $plan->price . ' ' . $plan->interval . - ')', - 'amount' => $plan->price * 100, - 'interval' => str_replace('ly', '', $plan->interval), - 'currency' => Cashier::usesCurrency(), - 'statement_descriptor' => Spark::$details['vendor'], - 'trial_period_days' => $plan->trialDays, + 'id' => $plan->id, + 'product' => [ + 'name' => Spark::$details['product'] . ' ' . $plan->name . ' (' . + config('cashier.currency') . $plan->price . ' ' . $plan->interval . + ')', + 'statement_descriptor' => Spark::$details['vendor'], + 'unit_label' => 'JobAds', + ], + 'amount' => $plan->price * 100, + 'interval' => str_replace('ly', '', $plan->interval), + 'currency' => config('cashier.currency'), + 'trial_period_days' => $plan->trialDays, + 'billing_scheme' => 'per_unit', + 'usage_type' => Spark::noProrate() ? 'licensed' : 'metered', ]); $this->info('Stripe plan created: ' . $plan->id); From c98b659c6c2cdabf52317e783e4cfbf156f260be Mon Sep 17 00:00:00 2001 From: Lukas Kahwe Smith Date: Mon, 7 Oct 2019 11:36:40 +0200 Subject: [PATCH 03/12] tweak plan creation --- src/Console/Commands/CreateStripePlans.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Console/Commands/CreateStripePlans.php b/src/Console/Commands/CreateStripePlans.php index da64aa6..0f96002 100644 --- a/src/Console/Commands/CreateStripePlans.php +++ b/src/Console/Commands/CreateStripePlans.php @@ -64,10 +64,9 @@ protected function createStripePlans($plans) } else { Stripe\Plan::create([ 'id' => $plan->id, + 'nickname' => $plan->name, 'product' => [ - 'name' => Spark::$details['product'] . ' ' . $plan->name . ' (' . - config('cashier.currency') . $plan->price . ' ' . $plan->interval . - ')', + 'name' => Spark::$details['product'] . ' ' . $plan->name, 'statement_descriptor' => Spark::$details['vendor'], 'unit_label' => 'JobAds', ], From 6d4711ed50664cf9f24bbd2ec9a472ee7f66bc1c Mon Sep 17 00:00:00 2001 From: Lukas Kahwe Smith Date: Mon, 7 Oct 2019 12:08:14 +0200 Subject: [PATCH 04/12] create product explicitly --- src/Console/Commands/CreateStripePlans.php | 41 +++++++++++++++++++--- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/src/Console/Commands/CreateStripePlans.php b/src/Console/Commands/CreateStripePlans.php index 0f96002..3daaab9 100644 --- a/src/Console/Commands/CreateStripePlans.php +++ b/src/Console/Commands/CreateStripePlans.php @@ -42,6 +42,9 @@ public function handle() { Stripe\Stripe::setApiKey(config('services.stripe.secret')); + $this->info('Creating product ...'); + $this->createProduct(); + $this->info('Creating user plans...'); $this->createStripePlans(Spark::$plans); @@ -51,6 +54,38 @@ public function handle() $this->info('Finished'); } + protected function getProductId() + { + return Spark::$details['product_id'] + ?? strtolower(str_replace(' ', '-', Spark::$details['product'])); + } + + /** + * Try and create product in Stripe + * + * @param array $plans + */ + protected function createProduct() + { + $id = $this->getProductId(); + + try { + Stripe\Product::retrieve($id); + + $this->line('Stripe product ' . $id . ' already exists'); + } catch (\Exception $e) { + Stripe\Product::create([ + 'id' => $id, + 'name' => Spark::$details['product'], + 'statement_descriptor' => Spark::$details['vendor'], + 'unit_label' => 'JobAds', + 'type' => 'service', + ]); + + $this->info('Stripe product created: ' . $id); + } + + } /** * Try and create plans in Stripe * @@ -65,11 +100,7 @@ protected function createStripePlans($plans) Stripe\Plan::create([ 'id' => $plan->id, 'nickname' => $plan->name, - 'product' => [ - 'name' => Spark::$details['product'] . ' ' . $plan->name, - 'statement_descriptor' => Spark::$details['vendor'], - 'unit_label' => 'JobAds', - ], + 'product' => $this->getProductId(), 'amount' => $plan->price * 100, 'interval' => str_replace('ly', '', $plan->interval), 'currency' => config('cashier.currency'), From 3fd34e24bc2467477117ecd2d01ad8d11a87ab67 Mon Sep 17 00:00:00 2001 From: Lukas Kahwe Smith Date: Thu, 10 Oct 2019 15:22:39 +0200 Subject: [PATCH 05/12] add command to create endpoints --- .../Commands/CreateStripeEndpoints.php | 70 +++++++++++++++++++ src/CreateStripePlansServiceProvider.php | 3 +- 2 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 src/Console/Commands/CreateStripeEndpoints.php diff --git a/src/Console/Commands/CreateStripeEndpoints.php b/src/Console/Commands/CreateStripeEndpoints.php new file mode 100644 index 0000000..41f5c6e --- /dev/null +++ b/src/Console/Commands/CreateStripeEndpoints.php @@ -0,0 +1,70 @@ +info('Creating endpoints ...'); + $this->createEndpoints(); + + $this->info('Finished'); + } + + /** + * Try and create endpoints in Stripe + * + * @param array $plans + */ + protected function createEndpoints() + { + $endpoint = \Stripe\WebhookEndpoint::create([ + 'url' => config('app.url').'/webhook/stripe', + 'enabled_events' => [ + 'customer.subscription.updated', + 'customer.subscription.deleted', + 'customer.updated', + 'customer.deleted', + 'invoice.payment_action_required', + 'invoice.payment_succeeded', + ] + ]); + } +} diff --git a/src/CreateStripePlansServiceProvider.php b/src/CreateStripePlansServiceProvider.php index a1b35d8..e93d3de 100644 --- a/src/CreateStripePlansServiceProvider.php +++ b/src/CreateStripePlansServiceProvider.php @@ -8,6 +8,7 @@ class CreateStripePlansServiceProvider extends ServiceProvider { protected $commands = [ \Gilbitron\Laravel\Spark\Console\Commands\CreateStripePlans::class, + \Gilbitron\Laravel\Spark\Console\Commands\CreateStripeEndpoints::class, ]; /** @@ -29,4 +30,4 @@ public function register() { $this->commands($this->commands); } -} \ No newline at end of file +} From 3cbfe132f06494da896d7bc4f6b518f6c0cdd603 Mon Sep 17 00:00:00 2001 From: Lukas Kahwe Smith Date: Sat, 12 Oct 2019 11:30:13 +0200 Subject: [PATCH 06/12] skip free plan --- src/Console/Commands/CreateStripePlans.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Console/Commands/CreateStripePlans.php b/src/Console/Commands/CreateStripePlans.php index 3daaab9..cf5e060 100644 --- a/src/Console/Commands/CreateStripePlans.php +++ b/src/Console/Commands/CreateStripePlans.php @@ -94,6 +94,11 @@ protected function createProduct() protected function createStripePlans($plans) { foreach ($plans as $plan) { + if ($plan->id === 'free') { + $this->line('Skipping free plan, since the "free" plan is handled by Spark internally.'); + continue; + } + if ($this->planExists($plan)) { $this->line('Stripe plan ' . $plan->id . ' already exists'); } else { From a482e41c7026ba1876887914fb8fe86f9a493a2d Mon Sep 17 00:00:00 2001 From: Lukas Kahwe Smith Date: Tue, 15 Oct 2019 16:58:17 +0200 Subject: [PATCH 07/12] make the key for the product_id clearer --- src/Console/Commands/CreateStripePlans.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Console/Commands/CreateStripePlans.php b/src/Console/Commands/CreateStripePlans.php index cf5e060..30ae344 100644 --- a/src/Console/Commands/CreateStripePlans.php +++ b/src/Console/Commands/CreateStripePlans.php @@ -56,7 +56,7 @@ public function handle() protected function getProductId() { - return Spark::$details['product_id'] + return Spark::$details['stripe_product_id'] ?? strtolower(str_replace(' ', '-', Spark::$details['product'])); } From 9199acfaea0c06a7817fb3dc0507ea2b5ea4b121 Mon Sep 17 00:00:00 2001 From: Lukas Kahwe Smith Date: Tue, 15 Oct 2019 17:09:22 +0200 Subject: [PATCH 08/12] improve error handling --- .../Commands/CreateStripeEndpoints.php | 7 ++++- src/Console/Commands/CreateStripePlans.php | 29 ++++--------------- 2 files changed, 11 insertions(+), 25 deletions(-) diff --git a/src/Console/Commands/CreateStripeEndpoints.php b/src/Console/Commands/CreateStripeEndpoints.php index 41f5c6e..476e977 100644 --- a/src/Console/Commands/CreateStripeEndpoints.php +++ b/src/Console/Commands/CreateStripeEndpoints.php @@ -43,7 +43,12 @@ public function handle() Stripe\Stripe::setApiKey(config('services.stripe.secret')); $this->info('Creating endpoints ...'); - $this->createEndpoints(); + + try { + $this->createEndpoints(); + } catch (\Stripe\Error\InvalidRequest $e) { + $this->error($e->getMessage()); + } $this->info('Finished'); } diff --git a/src/Console/Commands/CreateStripePlans.php b/src/Console/Commands/CreateStripePlans.php index 30ae344..cafe4f6 100644 --- a/src/Console/Commands/CreateStripePlans.php +++ b/src/Console/Commands/CreateStripePlans.php @@ -70,10 +70,6 @@ protected function createProduct() $id = $this->getProductId(); try { - Stripe\Product::retrieve($id); - - $this->line('Stripe product ' . $id . ' already exists'); - } catch (\Exception $e) { Stripe\Product::create([ 'id' => $id, 'name' => Spark::$details['product'], @@ -83,6 +79,8 @@ protected function createProduct() ]); $this->info('Stripe product created: ' . $id); + } catch (\Stripe\Error\InvalidRequest $e) { + $this->line('Stripe product ' . $id . ' already exists'); } } @@ -99,9 +97,7 @@ protected function createStripePlans($plans) continue; } - if ($this->planExists($plan)) { - $this->line('Stripe plan ' . $plan->id . ' already exists'); - } else { + try { Stripe\Plan::create([ 'id' => $plan->id, 'nickname' => $plan->name, @@ -115,24 +111,9 @@ protected function createStripePlans($plans) ]); $this->info('Stripe plan created: ' . $plan->id); + } catch (\Stripe\Error\InvalidRequest $e) { + $this->line('Stripe plan ' . $plan->id . ' already exists'); } } } - - /** - * Check if a plan already exists - * - * @param $plan - * @return bool - */ - private function planExists($plan) - { - try { - Stripe\Plan::retrieve($plan->id); - return true; - } catch (\Exception $e) { - } - - return false; - } } From 534efaeac44645bfefd51dd83a71d5bcf62e5016 Mon Sep 17 00:00:00 2001 From: Lukas Kahwe Smith Date: Thu, 14 Nov 2019 22:55:53 +0100 Subject: [PATCH 09/12] stripe v7 compatibility --- composer.json | 1 + src/Console/Commands/CreateStripeEndpoints.php | 4 ++-- src/Console/Commands/CreateStripePlans.php | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index 5a16fb2..1b12a0c 100644 --- a/composer.json +++ b/composer.json @@ -10,6 +10,7 @@ } ], "require": { + "php": "^7.1.3", "laravel/framework": "5.*|6.*", "stripe/stripe-php": "4.*|5.*|6.*" }, diff --git a/src/Console/Commands/CreateStripeEndpoints.php b/src/Console/Commands/CreateStripeEndpoints.php index 476e977..cda38f1 100644 --- a/src/Console/Commands/CreateStripeEndpoints.php +++ b/src/Console/Commands/CreateStripeEndpoints.php @@ -46,7 +46,7 @@ public function handle() try { $this->createEndpoints(); - } catch (\Stripe\Error\InvalidRequest $e) { + } catch (\Stripe\Error\InvalidRequest | \Stripe\Exception\InvalidRequest $e) { $this->error($e->getMessage()); } @@ -60,7 +60,7 @@ public function handle() */ protected function createEndpoints() { - $endpoint = \Stripe\WebhookEndpoint::create([ + \Stripe\WebhookEndpoint::create([ 'url' => config('app.url').'/webhook/stripe', 'enabled_events' => [ 'customer.subscription.updated', diff --git a/src/Console/Commands/CreateStripePlans.php b/src/Console/Commands/CreateStripePlans.php index cafe4f6..77f188e 100644 --- a/src/Console/Commands/CreateStripePlans.php +++ b/src/Console/Commands/CreateStripePlans.php @@ -79,7 +79,7 @@ protected function createProduct() ]); $this->info('Stripe product created: ' . $id); - } catch (\Stripe\Error\InvalidRequest $e) { + } catch (\Stripe\Error\InvalidRequest | \Stripe\Exception\InvalidRequest $e) { $this->line('Stripe product ' . $id . ' already exists'); } @@ -111,7 +111,7 @@ protected function createStripePlans($plans) ]); $this->info('Stripe plan created: ' . $plan->id); - } catch (\Stripe\Error\InvalidRequest $e) { + } catch (\Stripe\Error\InvalidRequest | \Stripe\Exception\InvalidRequest $e) { $this->line('Stripe plan ' . $plan->id . ' already exists'); } } From 28ca22da96aabc5e743150d9ed07b5aea74263fd Mon Sep 17 00:00:00 2001 From: Lukas Kahwe Smith Date: Wed, 20 Nov 2019 09:25:55 +0100 Subject: [PATCH 10/12] unit_label isn't hardcoded anymore, fixed incorrect useage of protation --- src/Console/Commands/CreateStripePlans.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Console/Commands/CreateStripePlans.php b/src/Console/Commands/CreateStripePlans.php index 77f188e..372ce28 100644 --- a/src/Console/Commands/CreateStripePlans.php +++ b/src/Console/Commands/CreateStripePlans.php @@ -74,7 +74,7 @@ protected function createProduct() 'id' => $id, 'name' => Spark::$details['product'], 'statement_descriptor' => Spark::$details['vendor'], - 'unit_label' => 'JobAds', + 'unit_label' => Spark::$details['unit_label'] ?? null, 'type' => 'service', ]); @@ -107,7 +107,7 @@ protected function createStripePlans($plans) 'currency' => config('cashier.currency'), 'trial_period_days' => $plan->trialDays, 'billing_scheme' => 'per_unit', - 'usage_type' => Spark::noProrate() ? 'licensed' : 'metered', + 'usage_type' => 'licensed', ]); $this->info('Stripe plan created: ' . $plan->id); From 9c18b863136c42761a8986ee036d11b8c97fd02b Mon Sep 17 00:00:00 2001 From: Lukas Kahwe Smith Date: Wed, 20 Nov 2019 11:23:08 +0100 Subject: [PATCH 11/12] group plans by product --- src/Console/Commands/CreateStripePlans.php | 59 +++++++++++++++++----- 1 file changed, 47 insertions(+), 12 deletions(-) diff --git a/src/Console/Commands/CreateStripePlans.php b/src/Console/Commands/CreateStripePlans.php index 372ce28..38cc23a 100644 --- a/src/Console/Commands/CreateStripePlans.php +++ b/src/Console/Commands/CreateStripePlans.php @@ -23,6 +23,12 @@ class CreateStripePlans extends Command */ protected $description = 'Creates plans in Stripe based on the plans defined in Spark'; + /** + * Product Stripe IDs + * + * @var array + */ + private $productStripeIds = []; /** * Create a new command instance. * @@ -42,8 +48,8 @@ public function handle() { Stripe\Stripe::setApiKey(config('services.stripe.secret')); - $this->info('Creating product ...'); - $this->createProduct(); + $this->info('Fetch products...'); + $this->fetchProducts(); $this->info('Creating user plans...'); $this->createStripePlans(Spark::$plans); @@ -54,36 +60,65 @@ public function handle() $this->info('Finished'); } - protected function getProductId() + /** + * Try and create product in Stripe + * + * @param array $plans + */ + protected function fetchProducts() { - return Spark::$details['stripe_product_id'] - ?? strtolower(str_replace(' ', '-', Spark::$details['product'])); + try { + /** @var \Stripe\Product[] $products */ + $products = Stripe\Product::all(); + foreach ($products as $product) { + $this->productStripeIds[] = $product->id; + } + + $this->info('Fetched products'); + } catch (\Stripe\Error\InvalidRequest | \Stripe\Exception\InvalidRequest $e) { + $this->line('Unable to fetch products'); + } + } /** - * Try and create product in Stripe + * Create product in Stripe if needed and return the id * * @param array $plans */ - protected function createProduct() + protected function getProductId($name = null) { - $id = $this->getProductId(); + $name = $name ?? Spark::$details['product']; + + $id = strtolower(str_replace(' ', '-', $name)); + + $this->info('Creating looking up id for: '.$id); + + if (in_array($id, $this->productStripeIds)) { + return $id; + } + + $this->info('Creating product: '.$id); try { - Stripe\Product::create([ + $product = Stripe\Product::create([ 'id' => $id, - 'name' => Spark::$details['product'], + 'name' => $name, 'statement_descriptor' => Spark::$details['vendor'], 'unit_label' => Spark::$details['unit_label'] ?? null, 'type' => 'service', ]); + $this->productStripeIds[] = $product->id; + $this->info('Stripe product created: ' . $id); } catch (\Stripe\Error\InvalidRequest | \Stripe\Exception\InvalidRequest $e) { $this->line('Stripe product ' . $id . ' already exists'); } + return $id; } + /** * Try and create plans in Stripe * @@ -101,7 +136,7 @@ protected function createStripePlans($plans) Stripe\Plan::create([ 'id' => $plan->id, 'nickname' => $plan->name, - 'product' => $this->getProductId(), + 'product' => $this->getProductId($plan->attribute('product')), 'amount' => $plan->price * 100, 'interval' => str_replace('ly', '', $plan->interval), 'currency' => config('cashier.currency'), @@ -112,7 +147,7 @@ protected function createStripePlans($plans) $this->info('Stripe plan created: ' . $plan->id); } catch (\Stripe\Error\InvalidRequest | \Stripe\Exception\InvalidRequest $e) { - $this->line('Stripe plan ' . $plan->id . ' already exists'); + $this->line('Stripe plan ' . $plan->id . ' already exists: '.$e->getMessage()); } } } From 47f0f761c6cc6495877c76739041122cc99693fe Mon Sep 17 00:00:00 2001 From: Lukas Kahwe Smith Date: Sun, 8 Mar 2020 18:26:16 +0100 Subject: [PATCH 12/12] remove all current endpoints --- .../Commands/CreateStripeEndpoints.php | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/Console/Commands/CreateStripeEndpoints.php b/src/Console/Commands/CreateStripeEndpoints.php index cda38f1..e10de25 100644 --- a/src/Console/Commands/CreateStripeEndpoints.php +++ b/src/Console/Commands/CreateStripeEndpoints.php @@ -21,7 +21,7 @@ class CreateStripeEndpoints extends Command * * @var string */ - protected $description = 'Creates endpoiints in Stripe based on the Spark app'; + protected $description = 'Removes all current endpoints and creates endpoints in Stripe based on the Spark app'; /** * Create a new command instance. @@ -45,6 +45,7 @@ public function handle() $this->info('Creating endpoints ...'); try { + $this->deleteEndpoints(); $this->createEndpoints(); } catch (\Stripe\Error\InvalidRequest | \Stripe\Exception\InvalidRequest $e) { $this->error($e->getMessage()); @@ -53,6 +54,21 @@ public function handle() $this->info('Finished'); } + /** + * Delete all endpoints in Stripe + * + * @param array $plans + */ + protected function deleteEndpoints() + { + $endpoints =\Stripe\WebhookEndpoint::all(); + foreach ($endpoints as $endpoint) { + $this->info('Deleted webhook endpoint:'.$endpoint->url); + + $endpoint->delete(); + } + } + /** * Try and create endpoints in Stripe * @@ -60,8 +76,10 @@ public function handle() */ protected function createEndpoints() { + $url = config('app.url').'webhook/stripe'; + \Stripe\WebhookEndpoint::create([ - 'url' => config('app.url').'/webhook/stripe', + 'url' => $url, 'enabled_events' => [ 'customer.subscription.updated', 'customer.subscription.deleted', @@ -69,7 +87,10 @@ protected function createEndpoints() 'customer.deleted', 'invoice.payment_action_required', 'invoice.payment_succeeded', + 'invoice.created', ] ]); + + $this->info('Created webhook endpoint:'.$url); } }