mirror of
https://github.com/VueFileManager/vuefilemanager.git
synced 2026-04-18 16:22:14 +00:00
v1.7 beta.8
This commit is contained in:
@@ -33,7 +33,7 @@ MAIL_USERNAME=
|
||||
MAIL_PASSWORD=
|
||||
MAIL_ENCRYPTION=
|
||||
MAIL_FROM_ADDRESS="${MAIL_USERNAME}"
|
||||
MAIL_FROM_NAME="${APP_NAME}"
|
||||
MAIL_FROM_NAME="${MAIL_USERNAME}"
|
||||
|
||||
AWS_ACCESS_KEY_ID=
|
||||
AWS_SECRET_ACCESS_KEY=
|
||||
|
||||
@@ -18,6 +18,14 @@ class WebhookController extends CashierController
|
||||
*/
|
||||
public function handleCustomerSubscriptionDeleted($payload)
|
||||
{
|
||||
if ($user = $this->getUserByStripeId($payload['data']['object']['customer'])) {
|
||||
$user->subscriptions->filter(function ($subscription) use ($payload) {
|
||||
return $subscription->stripe_id === $payload['data']['object']['id'];
|
||||
})->each(function ($subscription) {
|
||||
$subscription->markAsCancelled();
|
||||
});
|
||||
}
|
||||
|
||||
// Get user
|
||||
$user = User::where('stripe_id', $payload['data']['object']['customer'])->firstOrFail();
|
||||
|
||||
@@ -29,4 +37,26 @@ class WebhookController extends CashierController
|
||||
|
||||
return $this->successMethod();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Invoice Payment Succeeded
|
||||
*
|
||||
* @param $payload
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function handleInvoicePaymentSucceeded($payload)
|
||||
{
|
||||
// Get user
|
||||
$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']
|
||||
]);
|
||||
|
||||
return $this->successMethod();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ class UserResource extends JsonResource
|
||||
'attributes' => [
|
||||
'storage_capacity' => $this->settings->storage_capacity,
|
||||
'subscription' => $this->subscribed('main'),
|
||||
'incomplete_payment' => $this->hasIncompletePayment('main') ? route('cashier.payment', $this->subscription('main')->latestPayment()->id) : null,
|
||||
'stripe_customer' => is_null($this->stripe_id) ? false : true,
|
||||
'name' => $this->name,
|
||||
'email' => env('APP_DEMO') ? obfuscate_email($this->email) : $this->email,
|
||||
|
||||
@@ -31,8 +31,8 @@ class UserSubscription extends JsonResource
|
||||
'id' => $subscription['plan']['id'],
|
||||
'type' => 'subscription',
|
||||
'attributes' => [
|
||||
/*'is_highest' => is_highest_plan($this->plan),*/
|
||||
'active' => $subscription['plan']['active'],
|
||||
'incomplete' => $this->subscription('main')->incomplete(),
|
||||
'active' => $this->subscription('main')->active(),
|
||||
'canceled' => $this->subscription('main')->cancelled(),
|
||||
'name' => $subscription['product']['name'],
|
||||
'capacity' => (int)$subscription['product']['metadata']['capacity'],
|
||||
|
||||
@@ -8,6 +8,7 @@ use Artisan;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Str;
|
||||
use Laravel\Cashier\Exceptions\IncompletePayment;
|
||||
use Laravel\Cashier\Exceptions\PaymentActionRequired;
|
||||
use Stripe;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
|
||||
@@ -116,6 +117,7 @@ class StripeService
|
||||
* @param $request
|
||||
* @param $user
|
||||
* @param $paymentMethod
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function createOrReplaceSubscription($request, $user)
|
||||
{
|
||||
@@ -138,7 +140,15 @@ class StripeService
|
||||
|
||||
} catch (IncompletePayment $exception) {
|
||||
|
||||
throw new HttpException(400, 'We can\'t charge your card');
|
||||
if ($exception instanceof PaymentActionRequired) {
|
||||
|
||||
$cashier_route = route('cashier.payment', [$exception->payment->id, 'redirect' => url('/settings/subscription')]);
|
||||
|
||||
throw new HttpException(402, $cashier_route);
|
||||
} else {
|
||||
throw new HttpException(400, $exception->getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
2
public/js/main.js
vendored
2
public/js/main.js
vendored
File diff suppressed because one or more lines are too long
@@ -23,7 +23,7 @@
|
||||
{{ $t('page_index.menu.log_in') }}
|
||||
</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<li v-if="config.userRegistration">
|
||||
<router-link class="cta-button" :to="{name: 'SignUp'}">
|
||||
{{ $t('page_index.menu.sign_in') }}
|
||||
</router-link>
|
||||
|
||||
@@ -28,6 +28,10 @@
|
||||
p, a {
|
||||
color: $danger;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<template>
|
||||
<div class="user-avatar" :class="size">
|
||||
<span v-if="isIncompletePayment || isNearlyFullStorageCapacity" class="notification"></span>
|
||||
<img :src="user.data.attributes.avatar" :alt="user.data.attributes.name">
|
||||
</div>
|
||||
</template>
|
||||
@@ -14,6 +15,12 @@
|
||||
],
|
||||
computed: {
|
||||
...mapGetters(['user']),
|
||||
isIncompletePayment() {
|
||||
return this.user.data.attributes.incomplete_payment
|
||||
},
|
||||
isNearlyFullStorageCapacity() {
|
||||
return this.config.storageLimit && this.user.relationships.storage.data.attributes.used > 95
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -24,6 +31,22 @@
|
||||
|
||||
.user-avatar {
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
width: 40px;
|
||||
margin: 0 auto;
|
||||
|
||||
.notification {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
display: block;
|
||||
position: absolute;
|
||||
bottom: -5px;
|
||||
right: -4px;
|
||||
border-radius: 10px;
|
||||
z-index: 2;
|
||||
background: $red;
|
||||
border: 2px solid $light_background;
|
||||
}
|
||||
|
||||
img {
|
||||
border-radius: 6px;
|
||||
@@ -32,6 +55,8 @@
|
||||
}
|
||||
|
||||
&.large {
|
||||
margin: 0;
|
||||
width: 54px;
|
||||
|
||||
img {
|
||||
border-radius: 9px;
|
||||
@@ -42,6 +67,11 @@
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.user-avatar {
|
||||
|
||||
.notification {
|
||||
border-color: $dark_mode_foreground;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -297,6 +297,7 @@
|
||||
"item_counts": "{count} 个文件 | {count} 个文件"
|
||||
},
|
||||
"global": {
|
||||
"incomplete": "Incomplete",
|
||||
"active": "活性",
|
||||
"admin": "管理员",
|
||||
"cancel": "取消",
|
||||
@@ -671,6 +672,10 @@
|
||||
"description": "You nearly reach your storage capacity.",
|
||||
"title": "You reach your storage capacity. Please upgrade."
|
||||
},
|
||||
"incomplete_payment": {
|
||||
"description": "Your latest payment is incomplete. {0}",
|
||||
"href": "Please confirm your payment."
|
||||
},
|
||||
"uploading": {
|
||||
"progress": "上传文件 {current}/{total}"
|
||||
},
|
||||
|
||||
@@ -297,6 +297,7 @@
|
||||
"item_counts": "{count} Item | {count} Items"
|
||||
},
|
||||
"global": {
|
||||
"incomplete": "Incomplete",
|
||||
"active": "Active",
|
||||
"admin": "Admin",
|
||||
"cancel": "Cancel",
|
||||
@@ -671,6 +672,10 @@
|
||||
"description": "You nearly reach your storage capacity.",
|
||||
"title": "You reach your storage capacity. Please upgrade."
|
||||
},
|
||||
"incomplete_payment": {
|
||||
"description": "Your latest payment is incomplete. {0}",
|
||||
"href": "Please confirm your payment."
|
||||
},
|
||||
"uploading": {
|
||||
"progress": "Uploading files {current}/{total}"
|
||||
},
|
||||
|
||||
@@ -297,6 +297,7 @@
|
||||
"item_counts": "{count} Položka | {count} Položky"
|
||||
},
|
||||
"global": {
|
||||
"incomplete": "Incomplete",
|
||||
"active": "Aktívny",
|
||||
"admin": "Admin",
|
||||
"cancel": "Zrušiť",
|
||||
@@ -671,6 +672,10 @@
|
||||
"description": "Takmer ste dosiahli kapacitu úložiska",
|
||||
"title": "Dosiahli ste kapacitu úložiska. Prosím inovujte úložisko."
|
||||
},
|
||||
"incomplete_payment": {
|
||||
"description": "Vaša posledná platba nie je dokončená. {0}",
|
||||
"href": "Prosím potvrďte Vašu platbu."
|
||||
},
|
||||
"uploading": {
|
||||
"progress": "Nahrávam súbory {current}/{total}"
|
||||
},
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
<span class="email">{{ user.data.attributes.email }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="config.storageLimit && config.isSaaS && config.app_payments_active" class="headline-actions">
|
||||
<div v-if="config.storageLimit && config.isSaaS && config.app_payments_active && !canShowIncompletePayment" class="headline-actions">
|
||||
<router-link :to="{name: 'UpgradePlan'}" v-if="canShowUpgradeButton">
|
||||
<ButtonBase class="upgrade-button" button-style="secondary" type="button">
|
||||
{{ $t('global.upgrade_plan') }}
|
||||
@@ -100,7 +100,15 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<InfoBox v-if="canShowUpgradeWarning" type="error" class="upgrade-box">
|
||||
<!--Incomplete Payment Warning-->
|
||||
<InfoBox v-if="canShowIncompletePayment" type="error" class="message-box">
|
||||
<i18n path="incomplete_payment.description" tag="p">
|
||||
<a :href="user.data.attributes.incomplete_payment">{{ $t('incomplete_payment.href') }}</a>
|
||||
</i18n>
|
||||
</InfoBox>
|
||||
|
||||
<!--Upgrade Storage Plan Warning-->
|
||||
<InfoBox v-if="canShowUpgradeWarning && !canShowIncompletePayment" type="error" class="message-box">
|
||||
<p>{{ $t('upgrade_banner.title') }}</p>
|
||||
</InfoBox>
|
||||
|
||||
@@ -171,6 +179,9 @@
|
||||
},
|
||||
canShowUpgradeWarning() {
|
||||
return this.config.storageLimit && this.user.relationships.storage.data.attributes.used > 95
|
||||
},
|
||||
canShowIncompletePayment() {
|
||||
return this.user.data.attributes.incomplete_payment
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -226,8 +237,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
.upgrade-box {
|
||||
margin-top: -30px;
|
||||
.message-box {
|
||||
margin-top: -15px;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
|
||||
@@ -305,9 +305,6 @@
|
||||
// Update user data
|
||||
this.$store.dispatch('getAppData')
|
||||
|
||||
// End loading
|
||||
this.isSubmitted = false
|
||||
|
||||
// Show toaster
|
||||
events.$emit('toaster', {
|
||||
type: 'success',
|
||||
@@ -319,13 +316,16 @@
|
||||
},
|
||||
errorOrder(error) {
|
||||
|
||||
if (error.response.status = 402) {
|
||||
// Redirect user to confirmation payment page
|
||||
if (error.response.status === 402) {
|
||||
window.location.href = error.response.data.message;
|
||||
}
|
||||
|
||||
// Show user error message
|
||||
if (error.response.status === 400) {
|
||||
this.isError = true
|
||||
this.errorMessage = error.response.data.message
|
||||
}
|
||||
|
||||
// End loading
|
||||
this.isSubmitted = false
|
||||
},
|
||||
async submitOrder() {
|
||||
|
||||
@@ -375,6 +375,9 @@
|
||||
})
|
||||
.then(() => this.successOrder())
|
||||
.catch((error) => this.errorOrder(error))
|
||||
.finally(() => {
|
||||
this.isSubmitted = false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -391,6 +394,9 @@
|
||||
})
|
||||
.then(() => this.successOrder())
|
||||
.catch((error) => this.errorOrder(error))
|
||||
.finally(() => {
|
||||
this.isSubmitted = false
|
||||
})
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -2,14 +2,16 @@
|
||||
<div id="single-page">
|
||||
<div id="page-content" class="large-width center-page" v-show="! isLoading">
|
||||
<MobileHeader :title="$router.currentRoute.meta.title"/>
|
||||
|
||||
<div class="content-page">
|
||||
|
||||
<!--Page Title-->
|
||||
<div class="plan-title">
|
||||
<cloud-icon size="42" class="title-icon"></cloud-icon>
|
||||
<h1>{{ $t('page_pricing_tables.title') }}</h1>
|
||||
<h2>{{ $t('page_pricing_tables.description') }}</h2>
|
||||
</div>
|
||||
|
||||
<!--Pricing Tables-->
|
||||
<PlanPricingTables @load="onLoadPricingTables" @selected-plan="onSelectTable"/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -55,6 +57,10 @@
|
||||
StripeElementsScript.setAttribute('src', 'https://js.stripe.com/v3/')
|
||||
document.head.appendChild(StripeElementsScript)
|
||||
},
|
||||
mounted() {
|
||||
// Reload user data
|
||||
this.$store.dispatch('getAppData')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -92,6 +92,9 @@
|
||||
return this.isConfirmedResume ? 'theme-solid' : 'secondary'
|
||||
},
|
||||
status() {
|
||||
if (this.subscription.data.attributes.incomplete) {
|
||||
return this.$t('global.incomplete')
|
||||
}
|
||||
if (this.subscription.data.attributes.canceled) {
|
||||
return this.$t('global.canceled')
|
||||
}
|
||||
|
||||
@@ -34,7 +34,11 @@
|
||||
<div id="invoice-wrapper">
|
||||
<header class="invoice-header">
|
||||
<div class="logo">
|
||||
<img src="/assets/images/vuefilemanager-horizontal-logo.svg" alt="VueFileManager">
|
||||
@if(isset($settings->app_logo_horizontal))
|
||||
<img src="{{ url($settings->app_logo_horizontal) }}" alt="{{ $settings->app_title ?? 'VueFileManager' }}">
|
||||
@else
|
||||
<h1>{{ $settings->app_title ?? 'VueFileManager' }}</h1>
|
||||
@endif
|
||||
</div>
|
||||
<div class="title">
|
||||
<h1>@lang('vuefilemanager.invoice_title')</h1>
|
||||
|
||||
Reference in New Issue
Block a user