mirror of
https://github.com/VueFileManager/vuefilemanager.git
synced 2026-04-18 16:22:14 +00:00
- extend setup:dev script
- Stripe taxes refactoring - billing subscription fixes
This commit is contained in:
@@ -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([
|
||||||
|
|||||||
@@ -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()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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']] : [];
|
||||||
|
|||||||
@@ -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,
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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']);
|
||||||
|
|||||||
2
tests/Feature/External/SubscriptionTest.php
vendored
2
tests/Feature/External/SubscriptionTest.php
vendored
@@ -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"
|
||||||
]);
|
]);
|
||||||
|
|||||||
Reference in New Issue
Block a user