- extend setup:dev script

- Stripe taxes refactoring
- billing subscription fixes
This commit is contained in:
Peter Papp
2021-03-24 08:19:52 +01:00
parent 09d2032a70
commit 0e3c6e286e
9 changed files with 106 additions and 48 deletions

View File

@@ -824,6 +824,38 @@ class SetupDevEnvironment extends Command
[ [
'name' => 'purchase_code', 'name' => 'purchase_code',
'value' => '26b889eb-3602-4bf2-beb3-3sc378fcf484', 'value' => '26b889eb-3602-4bf2-beb3-3sc378fcf484',
],
[
'name' => 'billing_address',
'value' => 'Palo Alto 20',
],
[
'name' => 'billing_city',
'value' => 'Palo Alto',
],
[
'name' => 'billing_country',
'value' => 'US',
],
[
'name' => 'billing_name',
'value' => 'VueFileManager Inc.',
],
[
'name' => 'billing_phone_number',
'value' => '312343141243214',
],
[
'name' => 'billing_postal_code',
'value' => '43213',
],
[
'name' => 'billing_state',
'value' => 'California',
],
[
'name' => 'billing_vat_number',
'value' => '41241241234',
] ]
])->each(function ($col) { ])->each(function ($col) {
Setting::forceCreate([ Setting::forceCreate([

View File

@@ -9,34 +9,33 @@ use App\Services\DemoService;
use App\Models\User; use App\Models\User;
use App\Services\StripeService; use App\Services\StripeService;
use Auth; use Auth;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\Routing\ResponseFactory; use Illuminate\Contracts\Routing\ResponseFactory;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Cache;
use Stripe\SetupIntent;
class SubscriptionController extends Controller class SubscriptionController extends Controller
{ {
private $stripe; private $stripe;
private $demo;
/** public function __construct()
* SubscriptionController constructor.
* @param $payment
*/
public function __construct(StripeService $stripe)
{ {
$this->stripe = $stripe; $this->stripe = resolve(StripeService::class);
$this->demo = DemoService::class; $this->demo = DemoService::class;
} }
/** /**
* Generate setup intent * Generate setup intent
* *
* @return \Stripe\SetupIntent * @return Application|ResponseFactory|Response|SetupIntent
*/ */
public function setup_intent() public function setup_intent()
{ {
return $this->stripe return response(
->getSetupIntent( $this->stripe->getSetupIntent(Auth::user()), 201
Auth::user() );
);
} }
/** /**
@@ -69,7 +68,7 @@ class SubscriptionController extends Controller
* Upgrade account to subscription * Upgrade account to subscription
* *
* @param StoreUpgradeAccountRequest $request * @param StoreUpgradeAccountRequest $request
* @return ResponseFactory|\Illuminate\Http\Response * @return ResponseFactory|Response
*/ */
public function upgrade(StoreUpgradeAccountRequest $request) public function upgrade(StoreUpgradeAccountRequest $request)
{ {
@@ -107,7 +106,7 @@ class SubscriptionController extends Controller
/** /**
* Cancel Subscription * Cancel Subscription
* *
* @return ResponseFactory|\Illuminate\Http\Response * @return ResponseFactory|Response
*/ */
public function cancel() public function cancel()
{ {
@@ -130,7 +129,7 @@ class SubscriptionController extends Controller
/** /**
* Resume Subscription * Resume Subscription
* *
* @return ResponseFactory|\Illuminate\Http\Response * @return ResponseFactory|Response
*/ */
public function resume() public function resume()
{ {

View File

@@ -67,7 +67,7 @@ class User extends Authenticatable
// Find tax rate // Find tax rate
$user_tax_rate = $rates->first(function ($item) { $user_tax_rate = $rates->first(function ($item) {
return $item['jurisdiction'] === $this->settings->billing_country && $item['active']; return $item['country'] === $this->settings->country && $item['active'];
}); });
return $user_tax_rate ? [$user_tax_rate['id']] : []; return $user_tax_rate ? [$user_tax_rate['id']] : [];

View File

@@ -70,7 +70,7 @@ class StripeService
array_push($rates_public, [ array_push($rates_public, [
'id' => $rate['id'], 'id' => $rate['id'],
'active' => $rate['active'], 'active' => $rate['active'],
'jurisdiction' => $rate['jurisdiction'], 'country' => $rate['country'],
'percentage' => $rate['percentage'], 'percentage' => $rate['percentage'],
'plan_price_formatted' => Cashier::formatAmount(round($amount + $tax)), 'plan_price_formatted' => Cashier::formatAmount(round($amount + $tax)),
]); ]);
@@ -283,7 +283,7 @@ class StripeService
$product = $this->stripe->products()->find($plan['product']); $product = $this->stripe->products()->find($plan['product']);
return [ return [
'plan' => $plan, 'plan' => $plan,
'product' => $product, 'product' => $product,
]; ];
}); });

View File

@@ -1,7 +1,7 @@
<template> <template>
<div id="single-page"> <div id="single-page">
<div id="page-content" class="large-width center-page" v-show="! isLoading"> <div id="page-content" class="large-width center-page" v-show="! isLoading">
<MobileHeader :title="$router.currentRoute.meta.title"/> <MobileHeader :title="$router.currentRoute.meta.title" />
<div class="content-page"> <div class="content-page">
<div class="plan-title"> <div class="plan-title">
<credit-card-icon size="42" class="title-icon"></credit-card-icon> <credit-card-icon size="42" class="title-icon"></credit-card-icon>
@@ -91,7 +91,7 @@
<ValidationProvider tag="div" mode="passive" class="input-wrapper" <ValidationProvider tag="div" mode="passive" class="input-wrapper"
rules="required" rules="required"
name="billing_name" v-slot="{ errors }"> name="billing_name" v-slot="{ errors }">
<input v-model="billing.billing_name" <input v-model="billing.name"
:placeholder="$t('user_settings.name_plac')" :placeholder="$t('user_settings.name_plac')"
type="text" type="text"
:class="{'is-error': errors[0]}" :class="{'is-error': errors[0]}"
@@ -105,7 +105,7 @@
<ValidationProvider tag="div" mode="passive" class="input-wrapper" <ValidationProvider tag="div" mode="passive" class="input-wrapper"
rules="required" rules="required"
name="billing_address" v-slot="{ errors }"> name="billing_address" v-slot="{ errors }">
<input v-model="billing.billing_address" <input v-model="billing.address"
:placeholder="$t('user_settings.address_plac')" :placeholder="$t('user_settings.address_plac')"
type="text" type="text"
:class="{'is-error': errors[0]}" :class="{'is-error': errors[0]}"
@@ -120,7 +120,7 @@
<ValidationProvider tag="div" mode="passive" class="input-wrapper" <ValidationProvider tag="div" mode="passive" class="input-wrapper"
rules="required" name="billing_city" rules="required" name="billing_city"
v-slot="{ errors }"> v-slot="{ errors }">
<input v-model="billing.billing_city" <input v-model="billing.city"
:placeholder="$t('user_settings.city_plac')" :placeholder="$t('user_settings.city_plac')"
type="text" type="text"
:class="{'is-error': errors[0]}" :class="{'is-error': errors[0]}"
@@ -134,7 +134,7 @@
<ValidationProvider tag="div" mode="passive" class="input-wrapper" <ValidationProvider tag="div" mode="passive" class="input-wrapper"
rules="required" name="billing_postal_code" rules="required" name="billing_postal_code"
v-slot="{ errors }"> v-slot="{ errors }">
<input v-model="billing.billing_postal_code" <input v-model="billing.postal_code"
:placeholder="$t('user_settings.postal_code_plac')" :placeholder="$t('user_settings.postal_code_plac')"
type="text" type="text"
:class="{'is-error': errors[0]}" :class="{'is-error': errors[0]}"
@@ -149,8 +149,8 @@
<ValidationProvider tag="div" mode="passive" class="input-wrapper" <ValidationProvider tag="div" mode="passive" class="input-wrapper"
rules="required" rules="required"
name="billing_country" v-slot="{ errors }"> name="billing_country" v-slot="{ errors }">
<SelectInput v-model="billing.billing_country" <SelectInput v-model="billing.country"
:default="billing.billing_country" :default="billing.country"
:options="countries" :options="countries"
:placeholder="$t('user_settings.country_plac')" :placeholder="$t('user_settings.country_plac')"
:isError="errors[0]" /> :isError="errors[0]" />
@@ -163,7 +163,7 @@
<ValidationProvider tag="div" mode="passive" class="input-wrapper" <ValidationProvider tag="div" mode="passive" class="input-wrapper"
rules="required" rules="required"
name="billing_state" v-slot="{ errors }"> name="billing_state" v-slot="{ errors }">
<input v-model="billing.billing_state" <input v-model="billing.state"
:placeholder="$t('user_settings.state_plac')" :placeholder="$t('user_settings.state_plac')"
type="text" type="text"
:class="{'is-error': errors[0]}" :class="{'is-error': errors[0]}"
@@ -180,7 +180,7 @@
<ValidationProvider tag="div" mode="passive" class="input-wrapper" <ValidationProvider tag="div" mode="passive" class="input-wrapper"
rules="required" rules="required"
name="billing_phone_number" v-slot="{ errors }"> name="billing_phone_number" v-slot="{ errors }">
<input v-model="billing.billing_phone_number" <input v-model="billing.phone_number"
:placeholder="$t('user_settings.phone_number_plac')" :placeholder="$t('user_settings.phone_number_plac')"
type="text" type="text"
:class="{'is-error': errors[0]}" :class="{'is-error': errors[0]}"
@@ -206,7 +206,7 @@
</div> </div>
<div class="row" v-if="taxRates"> <div class="row" v-if="taxRates">
<div class="cell"> <div class="cell">
<b>{{ $t('page_upgrade_account.summary.vat') }} - ({{ taxRates.jurisdiction }} {{ taxRates.percentage }}%)</b> <b>{{ $t('page_upgrade_account.summary.vat') }} - ({{ taxRates.country }} {{ taxRates.percentage }}%)</b>
</div> </div>
<div class="cell"> <div class="cell">
<b>{{ taxRates.plan_price_formatted }}</b> <b>{{ taxRates.plan_price_formatted }}</b>
@@ -291,11 +291,11 @@
computed: { computed: {
...mapGetters(['requestedPlan', 'config', 'countries']), ...mapGetters(['requestedPlan', 'config', 'countries']),
billing() { billing() {
return this.$store.getters.user.relationships.settings.data.attributes return this.$store.getters.user.data.relationships.settings.data.attributes
}, },
taxRates() { taxRates() {
return this.requestedPlan.data.attributes.tax_rates.find(taxRate => { return this.requestedPlan.data.attributes.tax_rates.find(taxRate => {
return taxRate.jurisdiction === this.billing.billing_country return taxRate.country === this.billing.country
}) })
} }
}, },
@@ -405,8 +405,16 @@
} else { } else {
axios axios
.post('/api/subscription/upgrade', { .post('/api/user/subscription/upgrade', {
billing: this.billing, billing: {
billing_address: this.billing.address,
billing_city: this.billing.city,
billing_country: this.billing.country,
billing_name: this.billing.name,
billing_phone_number: this.billing.phone_number,
billing_postal_code: this.billing.postal_code,
billing_state: this.billing.state,
},
plan: this.requestedPlan, plan: this.requestedPlan,
payment: { payment: {
type: 'stripe', type: 'stripe',
@@ -415,8 +423,12 @@
} }
} }
}) })
.then(() => this.successOrder()) .then(() => {
.catch((error) => this.errorOrder(error)) this.successOrder()
})
.catch((error) => {
this.errorOrder(error)
})
.finally(() => { .finally(() => {
this.isSubmitted = false this.isSubmitted = false
}) })
@@ -427,15 +439,27 @@
if (this.defaultPaymentMethod && !this.payByNewCard) { if (this.defaultPaymentMethod && !this.payByNewCard) {
axios axios
.post('/api/subscription/upgrade', { .post('/api/user/subscription/upgrade', {
billing: this.billing, billing: {
billing_address: this.billing.address,
billing_city: this.billing.city,
billing_country: this.billing.country,
billing_name: this.billing.name,
billing_phone_number: this.billing.phone_number,
billing_postal_code: this.billing.postal_code,
billing_state: this.billing.state,
},
plan: this.requestedPlan, plan: this.requestedPlan,
payment: { payment: {
type: 'stripe', type: 'stripe',
} }
}) })
.then(() => this.successOrder()) .then(() => {
.catch((error) => this.errorOrder(error)) this.successOrder()
})
.catch((error) => {
this.errorOrder(error)
})
.finally(() => { .finally(() => {
this.isSubmitted = false this.isSubmitted = false
}) })
@@ -452,11 +476,13 @@
created() { created() {
// Get setup intent for stripe // Get setup intent for stripe
axios.get('/api/subscription/setup-intent') axios.get('/api/user/subscription/setup-intent')
.then(response => { .then(response => {
this.clientSecret = response.data.client_secret this.clientSecret = response.data.client_secret
}) })
.catch(() => this.$isSomethingWrong()) .catch(() => {
this.$isSomethingWrong()
})
axios.get('/api/user/payments') axios.get('/api/user/payments')
.then(response => { .then(response => {
@@ -464,11 +490,12 @@
this.defaultPaymentMethod = response.data.default this.defaultPaymentMethod = response.data.default
this.PaymentMethods = response.data.others this.PaymentMethods = response.data.others
}) })
.catch(() => this.$isSomethingWrong()) .catch(() => {
this.$isSomethingWrong()
})
.finally(() => { .finally(() => {
this.isLoading = false this.isLoading = false
} })
)
} }
} }
</script> </script>

View File

@@ -161,7 +161,7 @@
document.head.appendChild(StripeElementsScript) document.head.appendChild(StripeElementsScript)
// Get setup intent for stripe // Get setup intent for stripe
axios.get('/api/subscription/setup-intent') axios.get('/api/user/subscription/setup-intent')
.then(response => { .then(response => {
this.clientSecret = response.data.client_secret this.clientSecret = response.data.client_secret

View File

@@ -232,7 +232,7 @@
@foreach ($invoice->taxes() as $tax) @foreach ($invoice->taxes() as $tax)
<tr> <tr>
<td colspan="2" style="text-align: right;"> <td colspan="2" style="text-align: right;">
{{ $tax->display_name }} {{ $tax->jurisdiction ? ' - '.$tax->jurisdiction : '' }} {{ $tax->display_name }} {{ $tax->country ? ' - '.$tax->country : '' }}
({{ $tax->percentage }}%{{ $tax->isInclusive() ? ' incl.' : '' }}) ({{ $tax->percentage }}%{{ $tax->isInclusive() ? ' incl.' : '' }})
</td> </td>
<td>{{ $tax->amount() }}</td> <td>{{ $tax->amount() }}</td>
@@ -335,7 +335,7 @@
@foreach ($invoice->taxes() as $tax) @foreach ($invoice->taxes() as $tax)
<tr> <tr>
<td colspan="3" style="text-align: right;"> <td colspan="3" style="text-align: right;">
{{ $tax->display_name }} {{ $tax->jurisdiction ? ' - '.$tax->jurisdiction : '' }} {{ $tax->display_name }} {{ $tax->country ? ' - '.$tax->country : '' }}
({{ $tax->percentage }}%{{ $tax->isInclusive() ? ' ' . __('vuefilemanager.vat_included') : '' }}) ({{ $tax->percentage }}%{{ $tax->isInclusive() ? ' ' . __('vuefilemanager.vat_included') : '' }})
</td> </td>
<td>{{ $tax->amount() }}</td> <td>{{ $tax->amount() }}</td>

View File

@@ -17,7 +17,7 @@ Route::group(['middleware' => ['auth:sanctum']], function () {
Route::get('/storage', [AccountController::class, 'storage']); Route::get('/storage', [AccountController::class, 'storage']);
Route::get('/', [AccountController::class, 'user']); Route::get('/', [AccountController::class, 'user']);
// Payment cards TODO: tests // Payment cards
Route::delete('/payment-cards/{id}', [PaymentMethodsController::class, 'delete']); Route::delete('/payment-cards/{id}', [PaymentMethodsController::class, 'delete']);
Route::patch('/payment-cards/{id}', [PaymentMethodsController::class, 'update']); Route::patch('/payment-cards/{id}', [PaymentMethodsController::class, 'update']);
Route::post('/payment-cards', [PaymentMethodsController::class, 'store']); Route::post('/payment-cards', [PaymentMethodsController::class, 'store']);

View File

@@ -71,7 +71,7 @@ class SubscriptionTest extends TestCase
Sanctum::actingAs($user); Sanctum::actingAs($user);
$this->getJson('/api/user/subscription/setup-intent') $this->getJson('/api/user/subscription/setup-intent')
->assertStatus(200) ->assertStatus(201)
->assertJsonFragment([ ->assertJsonFragment([
"object" => "setup_intent" "object" => "setup_intent"
]); ]);