diff --git a/config/subscription.php b/config/subscription.php new file mode 100644 index 00000000..1620400e --- /dev/null +++ b/config/subscription.php @@ -0,0 +1,54 @@ + env('SUBSCRIPTION_DRIVER', 'stripe'), + + /* + * Activate drivers to handle subscription + */ + 'available_drivers' => [ + 'paystack', + 'paypal', + 'stripe', + ], + 'metered_billing' => [ + 'settlement_period' => 30, + /* + * Drivers which have native support for metered billing. + * This native support doesn't use subscription package credit system, but prefer + * auto-renew and maintain metered subscription on behalf of external service. + */ + 'native_support' => [ + 'stripe', + ], + ], + 'paystack' => [ + /* + * List of allowed ip address to verify paystack webhook request + */ + 'allowed_ips' => [ + '52.214.14.220', + '52.49.173.169', + '52.31.139.75', + ], + ], + + /* + * Get gateway credentials + */ + 'credentials' => [ + 'stripe' => [ + 'secret' => env('STRIPE_SECRET_KEY'), + 'public_key' => env('STRIPE_PUBLIC_KEY'), + ], + 'paystack' => [ + 'secret' => env('PAYSTACK_SECRET'), + 'public_key' => env('PAYSTACK_PUBLIC_KEY'), + ], + 'paypal' => [ + 'id' => env('PAYPAL_CLIENT_ID'), + 'secret' => env('PAYPAL_CLIENT_SECRET'), + 'webhook_id' => env('PAYPAL_WEBHOOK_ID'), + ], + ], +]; diff --git a/src/App/Users/Actions/AutoSubscribeForMeteredBillingAction.php b/src/App/Users/Actions/AutoSubscribeForMeteredBillingAction.php new file mode 100644 index 00000000..1855def6 --- /dev/null +++ b/src/App/Users/Actions/AutoSubscribeForMeteredBillingAction.php @@ -0,0 +1,34 @@ +where('type', 'metered') + ->first(); + + // TODO: add bonus + + // Create user balance + $user->balance()->create([ + 'amount' => 0, + 'currency' => $plan->currency, + ]); + + // Create user subscription + $user->subscription()->create([ + 'plan_id' => $plan->id, + 'name' => $plan->name, + 'status' => 'active', + 'renews_at' => now()->addDays(config('subscription.metered_billing.settlement_period')), + 'type' => 'pre-paid', + ]); + } +} \ No newline at end of file diff --git a/src/App/Users/Actions/CreateNewUserAction.php b/src/App/Users/Actions/CreateNewUserAction.php index 85df1dd5..7f288b27 100644 --- a/src/App/Users/Actions/CreateNewUserAction.php +++ b/src/App/Users/Actions/CreateNewUserAction.php @@ -1,16 +1,20 @@ $data->email, ]); - $user - ->settings() - ->create([ - 'name' => $data->name, - 'avatar' => $data->avatar, - ]); + $user->settings()->create([ + 'name' => $data->name, + 'avatar' => $data->avatar, + ]); + + // Subscribe user for metered billing + if ($settings['subscription_type'] === 'metered') { + ($this->autoSubscribeForMeteredBilling)($user); + } // Mark as verified if verification is disabled - if (! $data->password || ! intval($settings['user_verification'])) { + if (!$data->password || !intval($settings['user_verification'])) { $user->markEmailAsVerified(); } event(new Registered($user)); // Log in if verification is disabled - if (! $data->password || ! intval($settings['user_verification'])) { + if (!$data->password || !intval($settings['user_verification'])) { $this->guard->login($user); } } diff --git a/tests/App/Restrictions/MeteredBillingRestrictionsTest.php b/tests/App/Restrictions/MeteredBillingRestrictionsTest.php index 6f6998a5..58470200 100644 --- a/tests/App/Restrictions/MeteredBillingRestrictionsTest.php +++ b/tests/App/Restrictions/MeteredBillingRestrictionsTest.php @@ -5,7 +5,7 @@ use Tests\TestCase; use App\Users\Models\User; use Domain\Files\Models\File; use Domain\Sharing\Models\Share; -use Illuminate\Support\Facades\DB; +use Domain\Settings\Models\Setting; class MeteredBillingRestrictionsTest extends TestCase { @@ -13,8 +13,9 @@ class MeteredBillingRestrictionsTest extends TestCase { parent::setUp(); - DB::table('settings')->insert([ + Setting::updateOrCreate([ 'name' => 'subscription_type', + ], [ 'value' => 'metered', ]); } diff --git a/tests/App/Socialite/SocialiteTest.php b/tests/App/Socialite/SocialiteTest.php index b6e58ee0..af0f885d 100644 --- a/tests/App/Socialite/SocialiteTest.php +++ b/tests/App/Socialite/SocialiteTest.php @@ -1,7 +1,6 @@ get('api/socialite/google/redirect'); @@ -25,19 +24,8 @@ class SocialiteTest extends TestCase /** * @test */ - public function it_socialite_callback() + public function socialite_execute_provider_callback() { - // Set default settings - DB::table('settings')->insert([ - [ - 'name' => 'registration', - 'value' => 1, - ], [ - 'name' => 'storage_default', - 'value' => 5, - ], - ]); - // Create fake image $fakeImage = UploadedFile::fake() ->image('fake-avatar.jpg'); diff --git a/tests/App/Users/SignFlowTest.php b/tests/App/Users/SignFlowTest.php index f0567b57..509cde64 100644 --- a/tests/App/Users/SignFlowTest.php +++ b/tests/App/Users/SignFlowTest.php @@ -9,30 +9,28 @@ use Domain\Settings\Models\Setting; use Illuminate\Support\Facades\Password; use App\Users\Notifications\ResetPassword; use Illuminate\Auth\Notifications\VerifyEmail; +use VueFileManager\Subscription\Domain\Plans\Models\Plan; class SignFlowTest extends TestCase { /** * @test */ - public function it_register_user() + public function it_create_user_from_register_form() { collect([ - [ - 'name' => 'default_max_storage_amount', - 'value' => 12, - ], - [ - 'name' => 'registration', - 'value' => 1, - ], [ 'name' => 'user_verification', 'value' => 1, ], + [ + 'name' => 'default_max_storage_amount', + 'value' => 10, + ], ])->each(function ($setting) { - Setting::create([ - 'name' => $setting['name'], + Setting::updateOrCreate([ + 'name' => $setting['name'], + ], [ 'value' => $setting['value'], ]); }); @@ -44,14 +42,17 @@ class SignFlowTest extends TestCase 'name' => 'John Doe', ])->assertStatus(201); - $this->assertDatabaseHas('users', [ - 'email' => 'john@doe.com', - 'email_verified_at' => null, - ])->assertDatabaseHas('user_settings', [ - 'name' => 'John Doe', - ])->assertDatabaseHas('user_limitations', [ - 'max_storage_amount' => 12, - ]); + $this + ->assertDatabaseHas('users', [ + 'email' => 'john@doe.com', + 'email_verified_at' => null, + ]) + ->assertDatabaseHas('user_settings', [ + 'name' => 'John Doe', + ]) + ->assertDatabaseHas('user_limitations', [ + 'max_storage_amount' => 10, + ]); Storage::disk('local') ->assertExists('files/' . User::first()->id); @@ -59,13 +60,60 @@ class SignFlowTest extends TestCase Notification::assertTimesSent(1, VerifyEmail::class); } + /** + * @test + */ + public function it_register_user_when_metered_billing_is_active() + { + // Seed default settings + Setting::updateOrCreate([ + 'name' => 'subscription_type', + ], [ + 'value' => 'metered', + ]); + + // Create metered plan + $plan = Plan::factory() + ->create([ + 'status' => 'active', + 'type' => 'metered', + 'currency' => 'USD', + ]); + + $this->postJson('api/register', [ + 'email' => 'john@doe.com', + 'password' => 'SecretPassword', + 'password_confirmation' => 'SecretPassword', + 'name' => 'John Doe', + ])->assertStatus(201); + + $this + ->assertDatabaseHas('users', [ + 'email' => 'john@doe.com', + ]) + ->assertDatabaseHas('subscriptions', [ + 'status' => 'active', + 'name' => $plan->name, + 'ends_at' => null, + 'renews_at' => now()->addDays(config('subscription.metered_billing.settlement_period')), + ]) + ->assertDatabaseHas('balances', [ + 'currency' => 'USD', + 'amount' => 0, + ]) + ->assertDatabaseHas('user_settings', [ + 'name' => 'John Doe', + ]); + } + /** * @test */ public function it_try_register_when_registration_is_disabled() { - Setting::create([ + Setting::updateOrCreate([ 'name' => 'registration', + ], [ 'value' => 0, ]); diff --git a/tests/TestCase.php b/tests/TestCase.php index 373e3d1a..613dda0f 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -46,6 +46,18 @@ abstract class TestCase extends BaseTestCase 'name' => 'language', 'value' => 'en', ], + [ + 'name' => 'user_verification', + 'value' => 0, + ], + [ + 'name' => 'registration', + 'value' => 1, + ], + [ + 'name' => 'subscription_type', + 'value' => null, + ], ]); } }