From 69b72d24a9391284e54e5f9e0a69da4220ce5811 Mon Sep 17 00:00:00 2001 From: Peter Papp Date: Thu, 4 Mar 2021 12:48:51 +0100 Subject: [PATCH] added it_get_setup_intent, it_upgrade_plan, it_cancel_subscription test --- .env.testing | 8 + .gitignore | 1 + app/Console/Commands/SetupDevEnvironment.php | 7 +- .../User/SubscriptionController.php | 15 +- app/Http/Controllers/WebhookController.php | 28 ++-- app/Models/Page.php | 7 + app/Models/User.php | 14 +- app/Services/StripeService.php | 33 ++-- composer.json | 4 +- composer.lock | 62 +++---- config/cashier.php | 2 +- .../2014_10_12_000000_create_users_table.php | 2 +- ...000003_create_subscription_items_table.php | 2 +- routes/user.php | 2 +- tests/Feature/SubscriptionTest.php | 152 ++++++++++++++++++ 15 files changed, 253 insertions(+), 86 deletions(-) create mode 100644 tests/Feature/SubscriptionTest.php diff --git a/.env.testing b/.env.testing index 7abb73f5..79768a66 100644 --- a/.env.testing +++ b/.env.testing @@ -43,6 +43,14 @@ PUSHER_APP_KEY= PUSHER_APP_SECRET= PUSHER_APP_CLUSTER=mt1 +CASHIER_LOGGER=stack +CASHIER_CURRENCY=EUR +STRIPE_KEY=pk_test_51GsACaCBETHMUxzVsYkeApHtqb85paMuye7G77PDDQ28kXqDJ5HTmqLi13aM6xee81OQK1fhkTZ7vmDiWLStU9160061Yb2MtL +STRIPE_SECRET=sk_test_51GsACaCBETHMUxzVviYCrv0CeZMyWAOfBPe4uH5rkKJcJxrXhIciWQTr7UB1sgw9geoJMkNDVSWBQW36tuAsVznd00zhNHXhok +STRIPE_WEBHOOK_SECRET=whsec_eKrDhqtpbMUXOKqrUHf78SrZxHHYOdrf +CASHIER_PAYMENT_NOTIFICATION=App\Notifications\ConfirmPayment +CASHIER_MODEL=App\Models\User + MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}" MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" diff --git a/.gitignore b/.gitignore index 9855b8f9..5fee680f 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ .idea .env .env.backup +.env.testing .phpunit.result.cache .phpstorm.meta.php .vscode/ diff --git a/app/Console/Commands/SetupDevEnvironment.php b/app/Console/Commands/SetupDevEnvironment.php index a55c2fc8..358325d1 100644 --- a/app/Console/Commands/SetupDevEnvironment.php +++ b/app/Console/Commands/SetupDevEnvironment.php @@ -2,10 +2,10 @@ namespace App\Console\Commands; -use App\Page; +use App\Models\Page; use App\Services\SetupService; -use App\Setting; -use App\User; +use App\Models\Setting; +use App\Models\User; use Illuminate\Console\Command; use Illuminate\Support\Facades\Hash; use Faker; @@ -66,7 +66,6 @@ class SetupDevEnvironment extends Command { $user = User::forceCreate([ 'role' => 'admin', - 'name' => 'John Doe', 'email' => 'john@doe.com', 'password' => Hash::make('secret'), ]); diff --git a/app/Http/Controllers/User/SubscriptionController.php b/app/Http/Controllers/User/SubscriptionController.php index 2c50a01c..40f28643 100644 --- a/app/Http/Controllers/User/SubscriptionController.php +++ b/app/Http/Controllers/User/SubscriptionController.php @@ -7,6 +7,7 @@ use App\Http\Requests\Subscription\StoreUpgradeAccountRequest; use App\Http\Resources\UserSubscription; use App\Http\Tools\Demo; use App\Invoice; +use App\Models\User; use App\Services\StripeService; use Auth; use Cartalyst\Stripe\Exception\CardErrorException; @@ -14,6 +15,7 @@ use Illuminate\Contracts\Routing\ResponseFactory; use Illuminate\Http\Request; use Illuminate\Support\Facades\Cache; use Laravel\Cashier\Exceptions\IncompletePayment; +use Laravel\Cashier\Subscription; use Symfony\Component\HttpKernel\Exception\HttpException; class SubscriptionController extends Controller @@ -34,11 +36,12 @@ class SubscriptionController extends Controller * * @return \Stripe\SetupIntent */ - public function stripe_setup_intent() + public function setup_intent() { - $user = Auth::user(); - - return $this->stripe->getSetupIntent($user); + return $this->stripe + ->getSetupIntent( + Auth::user() + ); } /** @@ -50,7 +53,7 @@ class SubscriptionController extends Controller { $user = Auth::user(); - if (! $user->subscription('main')) { + if (!$user->subscription('main')) { return abort(204, 'User don\'t have any subscription'); } @@ -113,7 +116,7 @@ class SubscriptionController extends Controller */ public function cancel() { - $user = Auth::user(); + $user = User::find(Auth::id()); // Check if is demo if (is_demo($user->id)) { diff --git a/app/Http/Controllers/WebhookController.php b/app/Http/Controllers/WebhookController.php index 05aef00f..49615e81 100644 --- a/app/Http/Controllers/WebhookController.php +++ b/app/Http/Controllers/WebhookController.php @@ -3,9 +3,8 @@ namespace App\Http\Controllers; use App\Services\StripeService; -use App\Setting; -use App\User; -use Illuminate\Http\Request; +use App\Models\Setting; +use App\Models\User; use Laravel\Cashier\Http\Controllers\WebhookController as CashierController; @@ -33,13 +32,19 @@ class WebhookController extends CashierController } // Get user - $user = User::where('stripe_id', $payload['data']['object']['customer'])->firstOrFail(); + $user = User::where('stripe_id', $payload['data']['object']['customer']) + ->firstOrFail(); // Get default storage capacity - $default_storage = Setting::where('name', 'storage_default')->first(); + $default_storage = Setting::where('name', 'storage_default') + ->first(); // Update storage capacity - $user->settings()->update(['storage_capacity' => $default_storage->value]); + $user + ->settings() + ->update([ + 'storage_capacity' => $default_storage->value + ]); return $this->successMethod(); } @@ -53,15 +58,18 @@ class WebhookController extends CashierController public function handleInvoicePaymentSucceeded($payload) { // Get user - $user = User::where('stripe_id', $payload['data']['object']['customer'])->firstOrFail(); + $user = User::where('stripe_id', $payload['data']['object']['customer']) + ->firstOrFail(); // Get requested plan $plan = $this->stripe->getPlan($user->subscription('main')->stripe_plan); // Update user storage limit - $user->settings()->update([ - 'storage_capacity' => $plan['product']['metadata']['capacity'] - ]); + $user + ->settings() + ->update([ + 'storage_capacity' => $plan['product']['metadata']['capacity'] + ]); return $this->successMethod(); } diff --git a/app/Models/Page.php b/app/Models/Page.php index ae5e7ff8..6221da76 100644 --- a/app/Models/Page.php +++ b/app/Models/Page.php @@ -21,5 +21,12 @@ class Page extends Model 'visibility', ]; + public $fillable = [ + 'slug', + 'title', + 'visibility', + 'content', + ]; + public $timestamps = false; } diff --git a/app/Models/User.php b/app/Models/User.php index f07037ad..ec31ea28 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -173,13 +173,13 @@ class User extends Authenticatable public function setBilling($billing) { $this->settings()->update([ - 'billing_address' => $billing['billing_address'], - 'billing_city' => $billing['billing_city'], - 'billing_country' => $billing['billing_country'], - 'billing_name' => $billing['billing_name'], - 'billing_phone_number' => $billing['billing_phone_number'], - 'billing_postal_code' => $billing['billing_postal_code'], - 'billing_state' => $billing['billing_state'], + 'address' => $billing['billing_address'], + 'city' => $billing['billing_city'], + 'country' => $billing['billing_country'], + 'name' => $billing['billing_name'], + 'phone_number' => $billing['billing_phone_number'], + 'postal_code' => $billing['billing_postal_code'], + 'state' => $billing['billing_state'], ]); return $this->settings; diff --git a/app/Services/StripeService.php b/app/Services/StripeService.php index b2762e04..90863f97 100644 --- a/app/Services/StripeService.php +++ b/app/Services/StripeService.php @@ -4,7 +4,6 @@ namespace App\Services; use App\Models\User; -use Artisan; use Illuminate\Http\Request; use Illuminate\Support\Str; use Laravel\Cashier\Exceptions\IncompletePayment; @@ -22,18 +21,6 @@ class StripeService $this->stripe = Stripe::make(config('cashier.secret'), '2020-03-02'); } - /** - * Get Stripe account details - * - * @return mixed - */ - public function getAccountDetails() - { - $account = $this->stripe->account()->details(); - - return $account; - } - /** * Get setup intent * @@ -55,9 +42,9 @@ class StripeService */ public function getTaxRates() { - $tax_rates = $this->stripe->taxRates()->all(); - - return $tax_rates['data']; + return $this->stripe + ->taxRates() + ->all()['data']; } /** @@ -171,14 +158,14 @@ class StripeService public function updateCustomerDetails($user) { $user->updateStripeCustomer([ - 'name' => $user->settings->billing_name, - 'phone' => $user->settings->billing_phone_number, + 'name' => $user->settings->name, + 'phone' => $user->settings->phone_number, 'address' => [ - 'line1' => $user->settings->billing_address, - 'city' => $user->settings->billing_city, - 'country' => $user->settings->billing_country, - 'postal_code' => $user->settings->billing_postal_code, - 'state' => $user->settings->billing_state, + 'line1' => $user->settings->address, + 'city' => $user->settings->city, + 'country' => $user->settings->country, + 'postal_code' => $user->settings->postal_code, + 'state' => $user->settings->state, ] ]); } diff --git a/composer.json b/composer.json index 5e1c121c..980a4653 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ "php": "^7.3", "guzzlehttp/guzzle": "^7.2.0", "cartalyst/stripe-laravel": "^13.1", - "doctrine/dbal": "^2.10", + "doctrine/dbal": "^2.12.1", "fideloper/proxy": "^4.0", "fruitcake/laravel-cors": "^2.0", "gabrielelana/byte-units": "^0.5.0", @@ -20,7 +20,7 @@ "kyslik/column-sortable": "^6.4", "laravel/fortify": "^1.7.7", "laravel/sanctum": "^2.9", - "laravel/cashier": "^12.0", + "laravel/cashier": "^12.9.1", "laravel/framework": "^8.26.1", "teamtnt/laravel-scout-tntsearch-driver": "^11.1", "laravel/tinker": "^2.0", diff --git a/composer.lock b/composer.lock index 009e5dac..9109f193 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "9f04ec7084949f2d0a1c343da44c30cd", + "content-hash": "2ef5e239378565dc1264a14613c2a6cc", "packages": [ { "name": "asm89/stack-cors", @@ -60,16 +60,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.173.16", + "version": "3.173.21", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "e401bcb34e4ae88586a1f63bec6c0ddd12591e7a" + "reference": "914c68fb45bd4d1141d6c48ca9c88e0e9f426611" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/e401bcb34e4ae88586a1f63bec6c0ddd12591e7a", - "reference": "e401bcb34e4ae88586a1f63bec6c0ddd12591e7a", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/914c68fb45bd4d1141d6c48ca9c88e0e9f426611", + "reference": "914c68fb45bd4d1141d6c48ca9c88e0e9f426611", "shasum": "" }, "require": { @@ -77,9 +77,9 @@ "ext-pcre": "*", "ext-simplexml": "*", "guzzlehttp/guzzle": "^5.3.3|^6.2.1|^7.0", - "guzzlehttp/promises": "^1.0", - "guzzlehttp/psr7": "^1.4.1", - "mtdowling/jmespath.php": "^2.5", + "guzzlehttp/promises": "^1.4.0", + "guzzlehttp/psr7": "^1.7.0", + "mtdowling/jmespath.php": "^2.6", "php": ">=5.5" }, "require-dev": { @@ -141,7 +141,7 @@ "s3", "sdk" ], - "time": "2021-02-24T19:43:19+00:00" + "time": "2021-03-03T19:19:53+00:00" }, { "name": "bacon/bacon-qr-code", @@ -1576,16 +1576,16 @@ }, { "name": "jaybizzle/crawler-detect", - "version": "v1.2.104", + "version": "v1.2.105", "source": { "type": "git", "url": "https://github.com/JayBizzle/Crawler-Detect.git", - "reference": "a581e89a9212c4e9d18049666dc735718c29de9c" + "reference": "719c1ed49224857800c3dc40838b6b761d046105" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/JayBizzle/Crawler-Detect/zipball/a581e89a9212c4e9d18049666dc735718c29de9c", - "reference": "a581e89a9212c4e9d18049666dc735718c29de9c", + "url": "https://api.github.com/repos/JayBizzle/Crawler-Detect/zipball/719c1ed49224857800c3dc40838b6b761d046105", + "reference": "719c1ed49224857800c3dc40838b6b761d046105", "shasum": "" }, "require": { @@ -1620,7 +1620,7 @@ "crawlerdetect", "php crawler detect" ], - "time": "2021-01-13T15:25:20+00:00" + "time": "2021-03-03T20:55:48+00:00" }, { "name": "jaybizzle/laravel-crawler-detect", @@ -1878,16 +1878,16 @@ }, { "name": "laravel/framework", - "version": "v8.29.0", + "version": "v8.30.1", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "d2eba352b3b3a3c515b18c5726b373fe5026733e" + "reference": "ab7e1c19ee0403e15fc59983b7ccb86d85db45e6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/d2eba352b3b3a3c515b18c5726b373fe5026733e", - "reference": "d2eba352b3b3a3c515b18c5726b373fe5026733e", + "url": "https://api.github.com/repos/laravel/framework/zipball/ab7e1c19ee0403e15fc59983b7ccb86d85db45e6", + "reference": "ab7e1c19ee0403e15fc59983b7ccb86d85db45e6", "shasum": "" }, "require": { @@ -2038,7 +2038,7 @@ "framework", "laravel" ], - "time": "2021-02-23T14:27:41+00:00" + "time": "2021-03-03T14:59:13+00:00" }, { "name": "laravel/sanctum", @@ -2167,16 +2167,16 @@ }, { "name": "laravel/tinker", - "version": "v2.6.0", + "version": "v2.6.1", "source": { "type": "git", "url": "https://github.com/laravel/tinker.git", - "reference": "daae1c43f1300fe88c05d83db6f3d8f76677ad88" + "reference": "04ad32c1a3328081097a181875733fa51f402083" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/tinker/zipball/daae1c43f1300fe88c05d83db6f3d8f76677ad88", - "reference": "daae1c43f1300fe88c05d83db6f3d8f76677ad88", + "url": "https://api.github.com/repos/laravel/tinker/zipball/04ad32c1a3328081097a181875733fa51f402083", + "reference": "04ad32c1a3328081097a181875733fa51f402083", "shasum": "" }, "require": { @@ -2227,7 +2227,7 @@ "laravel", "psysh" ], - "time": "2021-01-26T20:35:18+00:00" + "time": "2021-03-02T16:53:12+00:00" }, { "name": "laravel/ui", @@ -7411,16 +7411,16 @@ }, { "name": "facade/ignition", - "version": "2.5.13", + "version": "2.5.14", "source": { "type": "git", "url": "https://github.com/facade/ignition.git", - "reference": "5e9ef386aaad9985cee2ac23281a27568d083b7e" + "reference": "17097f7a83e200d90d1cf9f4d1b35c1001513a47" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/facade/ignition/zipball/5e9ef386aaad9985cee2ac23281a27568d083b7e", - "reference": "5e9ef386aaad9985cee2ac23281a27568d083b7e", + "url": "https://api.github.com/repos/facade/ignition/zipball/17097f7a83e200d90d1cf9f4d1b35c1001513a47", + "reference": "17097f7a83e200d90d1cf9f4d1b35c1001513a47", "shasum": "" }, "require": { @@ -7478,7 +7478,7 @@ "laravel", "page" ], - "time": "2021-02-16T12:46:19+00:00" + "time": "2021-03-04T08:48:01+00:00" }, { "name": "facade/ignition-contracts", @@ -9838,6 +9838,8 @@ "platform": { "php": "^7.3" }, - "platform-dev": [], + "platform-dev": { + "ext-json": "*" + }, "plugin-api-version": "1.1.0" } diff --git a/config/cashier.php b/config/cashier.php index 07cd7c26..bb2fd137 100644 --- a/config/cashier.php +++ b/config/cashier.php @@ -57,7 +57,7 @@ return [ | */ - 'model' => env('CASHIER_MODEL', App\User::class), + 'model' => env('CASHIER_MODEL', App\Models\User::class), /* |-------------------------------------------------------------------------- diff --git a/database/migrations/2014_10_12_000000_create_users_table.php b/database/migrations/2014_10_12_000000_create_users_table.php index 6334ae67..0e93237c 100644 --- a/database/migrations/2014_10_12_000000_create_users_table.php +++ b/database/migrations/2014_10_12_000000_create_users_table.php @@ -14,7 +14,7 @@ class CreateUsersTable extends Migration public function up() { Schema::create('users', function (Blueprint $table) { - $table->uuid('id'); + $table->uuid('id')->primary(); $table->enum('role', ['admin', 'user'])->default('user'); $table->string('email')->unique(); $table->timestamp('email_verified_at')->nullable(); diff --git a/database/migrations/2019_05_03_000003_create_subscription_items_table.php b/database/migrations/2019_05_03_000003_create_subscription_items_table.php index 127eff6d..a9e82391 100644 --- a/database/migrations/2019_05_03_000003_create_subscription_items_table.php +++ b/database/migrations/2019_05_03_000003_create_subscription_items_table.php @@ -18,7 +18,7 @@ class CreateSubscriptionItemsTable extends Migration $table->unsignedBigInteger('subscription_id'); $table->string('stripe_id')->index(); $table->string('stripe_plan'); - $table->integer('quantity'); + $table->integer('quantity')->nullable(); $table->timestamps(); $table->unique(['subscription_id', 'stripe_plan']); diff --git a/routes/user.php b/routes/user.php index 275de4da..7690e3d9 100644 --- a/routes/user.php +++ b/routes/user.php @@ -27,7 +27,7 @@ Route::group(['middleware' => ['auth:sanctum']], function () { // Subscription Route::group(['prefix' => 'subscription'], function () { - Route::get('/setup-intent', [SubscriptionController::class, 'stripe_setup_intent']); + Route::get('/setup-intent', [SubscriptionController::class, 'setup_intent']); Route::post('/upgrade', [SubscriptionController::class, 'upgrade']); Route::post('/cancel', [SubscriptionController::class, 'cancel']); Route::post('/resume', [SubscriptionController::class, 'resume']); diff --git a/tests/Feature/SubscriptionTest.php b/tests/Feature/SubscriptionTest.php new file mode 100644 index 00000000..cff43698 --- /dev/null +++ b/tests/Feature/SubscriptionTest.php @@ -0,0 +1,152 @@ +subscription = app()->make(StripeService::class); + } + + /** + * + */ + public function it_get_setup_intent() + { + $user = User::factory(User::class) + ->create(); + + Sanctum::actingAs($user); + + $this->getJson('/api/user/subscription/setup-intent') + ->assertStatus(200) + ->assertJsonFragment([ + "object" => "setup_intent" + ]); + + $this->assertDatabaseMissing('users', [ + 'stripe_id' => null, + ]); + } + + /** + * @test + */ + public function it_upgrade_plan() + { + $user = User::factory(User::class) + ->create([ + 'email' => 'howdy@hi5ve.digital', + 'stripe_id' => 'cus_HgbhvNCbSwV8Qg', + 'card_brand' => 'visa', + 'card_last_four' => 4242, + ]); + + Sanctum::actingAs($user); + + $this->postJson('/api/user/subscription/upgrade', [ + 'billing' => [ + 'billing_address' => $user->settings->address, + 'billing_city' => $user->settings->city, + 'billing_country' => $user->settings->country, + 'billing_name' => $user->settings->name, + 'billing_phone_number' => $user->settings->phone_number, + 'billing_postal_code' => $user->settings->postal_code, + 'billing_state' => $user->settings->state, + ], + 'payment' => [ + 'type' => 'stripe', + ], + 'plan' => [ + 'data' => [ + 'id' => "business-pack", + 'type' => "plans", + 'attributes' => [ + 'name' => "Business Packs", + 'description' => "When your business start grow up.", + 'price' => "$44.99", + 'capacity' => 1000, + 'capacity_formatted' => "1TB", + 'currency' => "USD", + 'tax_rates' => [], + ], + ], + ], + ])->assertStatus(204); + + $this->assertDatabaseHas('subscriptions', [ + 'stripe_status' => 'active' + ]); + + $this->assertDatabaseHas('user_settings', [ + 'storage_capacity' => 1000 + ]); + } + + /** + * @test + */ + public function it_cancel_subscription() + { + $user = User::factory(User::class) + ->create([ + 'email' => 'howdy@hi5ve.digital', + 'stripe_id' => 'cus_HgbhvNCbSwV8Qg', + 'card_brand' => 'visa', + 'card_last_four' => 4242, + ]); + + Sanctum::actingAs($user); + + $this->postJson('/api/user/subscription/upgrade', [ + 'billing' => [ + 'billing_address' => $user->settings->address, + 'billing_city' => $user->settings->city, + 'billing_country' => $user->settings->country, + 'billing_name' => $user->settings->name, + 'billing_phone_number' => $user->settings->phone_number, + 'billing_postal_code' => $user->settings->postal_code, + 'billing_state' => $user->settings->state, + ], + 'payment' => [ + 'type' => 'stripe', + ], + 'plan' => [ + 'data' => [ + 'id' => "business-pack", + 'type' => "plans", + 'attributes' => [ + 'name' => "Business Packs", + 'description' => "When your business start grow up.", + 'price' => "$44.99", + 'capacity' => 1000, + 'capacity_formatted' => "1TB", + 'currency' => "USD", + 'tax_rates' => [], + ], + ], + ], + ])->assertStatus(204); + + $this->postJson('/api/user/subscription/cancel') + ->assertStatus(204); + + $this->assertDatabaseMissing('subscriptions', [ + 'ends_at' => 'canceled' + ]); + } +}