diff --git a/src/app/Http/Controllers/API/SignupController.php b/src/app/Http/Controllers/API/SignupController.php --- a/src/app/Http/Controllers/API/SignupController.php +++ b/src/app/Http/Controllers/API/SignupController.php @@ -7,6 +7,7 @@ use App\Discount; use App\Domain; use App\Plan; +use App\Providers\PaymentProvider; use App\Rules\SignupExternalEmail; use App\Rules\SignupToken; use App\Rules\Password; @@ -34,27 +35,25 @@ */ public function plans(Request $request) { - $plans = []; - // Use reverse order just to have individual on left, group on right ;) // But prefer monthly on left, yearly on right - Plan::withEnvTenantContext()->orderBy('months')->orderByDesc('title')->get() - ->map(function ($plan) use (&$plans) { - // Allow themes to set custom button label - $button = \trans('theme::app.planbutton-' . $plan->title); - if ($button == 'theme::app.planbutton-' . $plan->title) { - $button = \trans('app.planbutton', ['plan' => $plan->name]); + $plans = Plan::withEnvTenantContext()->orderBy('months')->orderByDesc('title')->get() + ->map(function ($plan) { + $button = self::trans("app.planbutton-{$plan->title}"); + if (strpos($button, 'app.planbutton') !== false) { + $button = self::trans('app.planbutton', ['plan' => $plan->name]); } - $plans[] = [ + return [ 'title' => $plan->title, 'name' => $plan->name, 'button' => $button, 'description' => $plan->description, - 'mode' => $plan->mode ?: 'email', + 'mode' => $plan->mode ?: Plan::MODE_EMAIL, 'isDomain' => $plan->hasDomain(), ]; - }); + }) + ->all(); return response()->json(['status' => 'success', 'plans' => $plans]); } @@ -91,7 +90,7 @@ $plan = $this->getPlan(); - if ($plan->mode == 'token') { + if ($plan->mode == Plan::MODE_TOKEN) { $rules['token'] = ['required', 'string', new SignupToken()]; } else { $rules['email'] = ['required', 'string', new SignupExternalEmail()]; @@ -106,7 +105,7 @@ // Generate the verification code $code = SignupCode::create([ - 'email' => $plan->mode == 'token' ? $request->token : $request->email, + 'email' => $plan->mode == Plan::MODE_TOKEN ? $request->token : $request->email, 'first_name' => $request->first_name, 'last_name' => $request->last_name, 'plan' => $plan->title, @@ -119,7 +118,7 @@ 'mode' => $plan->mode ?: 'email', ]; - if ($plan->mode == 'token') { + if ($plan->mode == Plan::MODE_TOKEN) { // Token verification, jump to the last step $has_domain = $plan->hasDomain(); @@ -221,13 +220,13 @@ } /** - * Finishes the signup process by creating the user account. + * Validates the input to the final signup request. * * @param \Illuminate\Http\Request $request HTTP request * * @return \Illuminate\Http\JsonResponse JSON response */ - public function signup(Request $request) + public function signupValidate(Request $request) { // Validate input $v = Validator::make( @@ -244,14 +243,13 @@ return response()->json(['status' => 'error', 'errors' => $v->errors()], 422); } - $settings = []; // Plan parameter is required/allowed in mandate mode if (!empty($request->plan) && empty($request->code) && empty($request->invitation)) { $plan = Plan::withEnvTenantContext()->where('title', $request->plan)->first(); - if (!$plan || $plan->mode != 'mandate') { + if (!$plan || $plan->mode != Plan::MODE_MANDATE) { $msg = \trans('validation.exists', ['attribute' => 'plan']); return response()->json(['status' => 'error', 'errors' => ['plan' => $msg]], 422); } @@ -300,7 +298,7 @@ 'last_name' => $code_data->last_name, ]; - if ($plan->mode == 'token') { + if ($plan->mode == Plan::MODE_TOKEN) { $settings['signup_token'] = $code_data->email; } else { $settings['external_email'] = $code_data->email; @@ -323,17 +321,46 @@ } $is_domain = $plan->hasDomain(); - $login = $request->login; - $domain_name = $request->domain; // Validate login - if ($errors = self::validateLogin($login, $domain_name, $is_domain)) { + if ($errors = self::validateLogin($request->login, $request->domain, $is_domain)) { return response()->json(['status' => 'error', 'errors' => $errors], 422); } + // Set some properties for signup() method + $request->settings = $settings; + $request->plan = $plan; + $request->discount = $discount ?? null; + $request->invitation = $invitation ?? null; + + $result = []; + + if ($plan->mode == Plan::MODE_MANDATE) { + $result = $this->mandateForPlan($plan, $request->discount); + } + + return response()->json($result); + } + + /** + * Finishes the signup process by creating the user account. + * + * @param \Illuminate\Http\Request $request HTTP request + * + * @return \Illuminate\Http\JsonResponse JSON response + */ + public function signup(Request $request) + { + $v = $this->signupValidate($request); + if ($v->status() !== 200) { + return $v; + } + + $is_domain = $request->plan->hasDomain(); + // We allow only ASCII, so we can safely lower-case the email address - $login = Str::lower($login); - $domain_name = Str::lower($domain_name); + $login = Str::lower($request->login); + $domain_name = Str::lower($request->domain); $domain = null; DB::beginTransaction(); @@ -353,22 +380,22 @@ 'status' => User::STATUS_RESTRICTED, ]); - if (!empty($discount)) { + if ($request->discount) { $wallet = $user->wallets()->first(); - $wallet->discount()->associate($discount); + $wallet->discount()->associate($request->discount); $wallet->save(); } - $user->assignPlan($plan, $domain); + $user->assignPlan($request->plan, $domain); // Save the external email and plan in user settings - $user->setSettings($settings); + $user->setSettings($request->settings); // Update the invitation - if (!empty($invitation)) { - $invitation->status = SignupInvitation::STATUS_COMPLETED; - $invitation->user_id = $user->id; - $invitation->save(); + if ($request->invitation) { + $request->invitation->status = SignupInvitation::STATUS_COMPLETED; + $request->invitation->user_id = $user->id; + $request->invitation->save(); } // Soft-delete the verification code, and store some more info with it @@ -384,15 +411,68 @@ $response = AuthController::logonResponse($user, $request->password); - // Redirect the user to the specified page - // $data = $response->getData(true); - // $data['redirect'] = 'wallet'; - // $response->setData($data); + if ($request->plan->mode == Plan::MODE_MANDATE) { + $data = $response->getData(true); + $data['checkout'] = $this->mandateForPlan($request->plan, $request->discount, $user); + $response->setData($data); + } return $response; } /** + * Collects some content to display to the user before redirect to a checkout page. + * Optionally creates a recurrent payment mandate for specified user/plan. + */ + protected function mandateForPlan(Plan $plan, Discount $discount = null, User $user = null): array + { + $result = []; + + $min = \App\Payment::MIN_AMOUNT; + $planCost = $plan->cost() * $plan->months; + + if ($discount) { + $planCost -= ceil($planCost * (100 - $discount->discount) / 100); + } + + if ($planCost > $min) { + $min = $planCost; + } + + if ($user) { + $wallet = $user->wallets()->first(); + $wallet->setSettings([ + 'mandate_amount' => sprintf('%.2f', round($min / 100, 2)), + 'mandate_balance' => 0, + ]); + + $mandate = [ + 'currency' => $wallet->currency, + 'description' => \App\Tenant::getConfig($user->tenant_id, 'app.name') . ' Auto-Payment Setup', + 'methodId' => PaymentProvider::METHOD_CREDITCARD, + 'redirectUrl' => \App\Utils::serviceUrl('/payment/status', $user->tenant_id), + ]; + + $provider = PaymentProvider::factory($wallet); + + $result = $provider->createMandate($wallet, $mandate); + } + + $params = [ + 'cost' => \App\Utils::money($planCost, \config('app.currency')), + 'period' => \trans($plan->months == 12 ? 'app.period-year' : 'app.period-month'), + ]; + + $content = '' . self::trans('app.signup-account-tobecreated') . '

' + . self::trans('app.signup-account-summary', $params) . '

' + . self::trans('app.signup-account-mandate', $params); + + $result['content'] = $content; + + return $result; + } + + /** * Returns plan for the signup process * * @returns \App\Plan Plan object selected for current signup process diff --git a/src/app/Http/Controllers/API/V4/PaymentsController.php b/src/app/Http/Controllers/API/V4/PaymentsController.php --- a/src/app/Http/Controllers/API/V4/PaymentsController.php +++ b/src/app/Http/Controllers/API/V4/PaymentsController.php @@ -140,6 +140,36 @@ } /** + * Reset the auto-payment mandate, create a new payment for it. + * + * @param \Illuminate\Http\Request $request The API request. + * + * @return \Illuminate\Http\JsonResponse The response + */ + public function mandateReset(Request $request) + { + $user = $this->guard()->user(); + + // TODO: Wallet selection + $wallet = $user->wallets()->first(); + + $mandate = [ + 'currency' => $wallet->currency, + 'description' => Tenant::getConfig($user->tenant_id, 'app.name') . ' Auto-Payment Setup', + 'methodId' => $request->methodId ?: PaymentProvider::METHOD_CREDITCARD, + 'redirectUrl' => \App\Utils::serviceUrl('/payment/status', $user->tenant_id), + ]; + + $provider = PaymentProvider::factory($wallet); + + $result = $provider->createMandate($wallet, $mandate); + + $result['status'] = 'success'; + + return response()->json($result); + } + + /** * Validate an auto-payment mandate request. * * @param \Illuminate\Http\Request $request The API request. @@ -172,7 +202,7 @@ $label = 'minamount'; if (($plan = $wallet->plan()) && $plan->months >= 1) { - $planCost = (int) ceil($plan->cost() * $plan->months); + $planCost = $plan->cost() * $plan->months; if ($planCost > $min) { $min = $planCost; } @@ -191,6 +221,39 @@ } /** + * Get status of the last payment. + * + * @return \Illuminate\Http\JsonResponse The response + */ + public function paymentStatus() + { + $user = $this->guard()->user(); + $wallet = $user->wallets()->first(); + + $payment = $wallet->payments()->orderBy('created_at', 'desc')->first(); + + if (empty($payment)) { + return $this->errorResponse(404); + } + + $done = [Payment::STATUS_PAID, Payment::STATUS_CANCELED, Payment::STATUS_FAILED, Payment::STATUS_EXPIRED]; + + if (in_array($payment->status, $done)) { + $label = "app.payment-status-{$payment->status}"; + } else { + $label = "app.payment-status-checking"; + } + + return response()->json([ + 'id' => $payment->id, + 'status' => $payment->status, + 'type' => $payment->type, + 'statusMessage' => \trans($label), + 'description' => $payment->description, + ]); + } + + /** * Create a new payment. * * @param \Illuminate\Http\Request $request The API request. @@ -386,7 +449,7 @@ // If this is a multi-month plan, we calculate the expected amount to be payed. if (($plan = $wallet->plan()) && $plan->months >= 1) { - $planCost = (int) ceil(($plan->cost() * $plan->months) / 100); + $planCost = round($plan->cost() * $plan->months / 100, 2); if ($planCost > $mandate['minAmount']) { $mandate['minAmount'] = $planCost; } @@ -514,6 +577,9 @@ /** * Calculates tax for the payment, fills the request with additional properties + * + * @param \App\Wallet $wallet The wallet + * @param array $request The request data with the payment amount */ protected static function addTax(Wallet $wallet, array &$request): void { diff --git a/src/app/Http/Controllers/API/V4/UsersController.php b/src/app/Http/Controllers/API/V4/UsersController.php --- a/src/app/Http/Controllers/API/V4/UsersController.php +++ b/src/app/Http/Controllers/API/V4/UsersController.php @@ -4,6 +4,7 @@ use App\Http\Controllers\RelationController; use App\Domain; +use App\Plan; use App\Rules\Password; use App\Rules\UserEmailDomain; use App\Rules\UserEmailLocal; @@ -203,7 +204,7 @@ 'enableUsers' => $isController, 'enableWallets' => $isController, 'enableWalletMandates' => $isController, - 'enableWalletPayments' => $isController && (!$plan || $plan->mode != 'mandate'), + 'enableWalletPayments' => $isController && (!$plan || $plan->mode != Plan::MODE_MANDATE), 'enableCompanionapps' => $hasBeta, ]; @@ -356,7 +357,7 @@ $wallet = $user->wallet(); // IsLocked flag to lock the user to the Wallet page only - $response['isLocked'] = ($user->isRestricted() && ($plan = $wallet->plan()) && $plan->mode == 'mandate'); + $response['isLocked'] = (!$user->isActive() && ($plan = $wallet->plan()) && $plan->mode == Plan::MODE_MANDATE); // Settings $response['settings'] = []; diff --git a/src/app/Http/Controllers/Controller.php b/src/app/Http/Controllers/Controller.php --- a/src/app/Http/Controllers/Controller.php +++ b/src/app/Http/Controllers/Controller.php @@ -81,4 +81,20 @@ { return Auth::guard(); } + + /** + * A wrapper for \trans() with theme localization support. + * + * @param string $label Localization label + * @param array $params Translation parameters + */ + public static function trans(string $label, array $params = []): string + { + $result = \trans("theme::{$label}", $params); + if ($result === "theme::{$label}") { + $result = \trans($label, $params); + } + + return $result; + } } diff --git a/src/app/Jobs/User/CreateJob.php b/src/app/Jobs/User/CreateJob.php --- a/src/app/Jobs/User/CreateJob.php +++ b/src/app/Jobs/User/CreateJob.php @@ -102,7 +102,14 @@ $user->status |= \App\User::STATUS_IMAP_READY; } - $user->status |= \App\User::STATUS_ACTIVE; + // Make user active in non-mandate mode only + if (!($wallet = $user->wallet()) + || !($plan = $user->wallet()->plan()) + || $plan->mode != \App\Plan::MODE_MANDATE + ) { + $user->status |= \App\User::STATUS_ACTIVE; + } + $user->save(); } } diff --git a/src/app/Payment.php b/src/app/Payment.php --- a/src/app/Payment.php +++ b/src/app/Payment.php @@ -12,6 +12,8 @@ * @property int $credit_amount Amount of money in cents of system currency (wallet balance) * @property string $description Payment description * @property string $id Mollie's Payment ID + * @property string $status Payment status (Payment::STATUS_*) + * @property string $type Payment type (Payment::TYPE_*) * @property ?string $vat_rate_id VAT rate identifier * @property \App\Wallet $wallet The wallet * @property string $wallet_id The ID of the wallet @@ -126,9 +128,16 @@ $this->wallet->setSetting('mandate_disabled', null); } - // Remove RESTRICTED flag from the wallet owner and all users in the wallet - if ($this->wallet->owner && $this->wallet->owner->isRestricted()) { - $this->wallet->owner->unrestrict(true); + if ($owner = $this->wallet->owner) { + // Remove RESTRICTED flag from the wallet owner and all users in the wallet + if ($owner->isRestricted()) { + $owner->unrestrict(true); + } + // Activate the inactive user + if (!$owner->isActive()) { + $owner->status |= User::STATUS_ACTIVE; + $owner->save(); + } } } diff --git a/src/app/Plan.php b/src/app/Plan.php --- a/src/app/Plan.php +++ b/src/app/Plan.php @@ -20,7 +20,7 @@ * @property int $discount_rate * @property int $free_months * @property string $id - * @property string $mode Plan signup mode (email|token) + * @property string $mode Plan signup mode (Plan::MODE_*) * @property string $name * @property \App\Package[] $packages * @property datetime $promo_from @@ -34,6 +34,11 @@ use HasTranslations; use UuidStrKeyTrait; + public const MODE_EMAIL = 'email'; + public const MODE_TOKEN = 'token'; + public const MODE_MANDATE = 'mandate'; + + /** @var bool Indicates if the model should be timestamped. */ public $timestamps = false; /** @var array The attributes that are mass assignable */ diff --git a/src/app/Providers/Payment/Mollie.php b/src/app/Providers/Payment/Mollie.php --- a/src/app/Providers/Payment/Mollie.php +++ b/src/app/Providers/Payment/Mollie.php @@ -54,6 +54,7 @@ * - currency: The operation currency * - description: Operation desc. * - methodId: Payment method + * - redirectUrl: The location to goto after checkout * * @return array Provider payment data: * - id: Operation identifier @@ -80,7 +81,7 @@ 'sequenceType' => 'first', 'description' => $payment['description'], 'webhookUrl' => Utils::serviceUrl('/api/webhooks/payment/mollie'), - 'redirectUrl' => self::redirectUrl(), + 'redirectUrl' => $payment['redirectUrl'] ?? self::redirectUrl(), 'locale' => 'en_US', 'method' => $payment['methodId'] ]; diff --git a/src/app/Providers/Payment/Stripe.php b/src/app/Providers/Payment/Stripe.php --- a/src/app/Providers/Payment/Stripe.php +++ b/src/app/Providers/Payment/Stripe.php @@ -59,6 +59,7 @@ * - amount: Value in cents (not used) * - currency: The operation currency * - description: Operation desc. + * - redirectUrl: The location to goto after checkout * * @return array Provider payment/session data: * - id: Session identifier @@ -70,8 +71,8 @@ $request = [ 'customer' => $customer_id, - 'cancel_url' => self::redirectUrl(), // required - 'success_url' => self::redirectUrl(), // required + 'cancel_url' => $payment['redirectUrl'] ?? self::redirectUrl(), // required + 'success_url' => $payment['redirectUrl'] ?? self::redirectUrl(), // required 'payment_method_types' => ['card'], // required 'locale' => 'en', 'mode' => 'setup', diff --git a/src/app/Providers/PaymentProvider.php b/src/app/Providers/PaymentProvider.php --- a/src/app/Providers/PaymentProvider.php +++ b/src/app/Providers/PaymentProvider.php @@ -89,6 +89,7 @@ * - currency: The operation currency * - description: Operation desc. * - methodId: Payment method + * - redirectUrl: The location to goto after checkout * * @return array Provider payment data: * - id: Operation identifier diff --git a/src/app/Utils.php b/src/app/Utils.php --- a/src/app/Utils.php +++ b/src/app/Utils.php @@ -576,4 +576,25 @@ return floatval($rates[$targetCurrency]); } + + /** + * A helper to display human-readable amount of money using + * for specified currency and locale. + * + * @param int $amount Amount of money (in cents) + * @param string $currency Currency code + * @param string $locale Output locale + * + * @return string String representation, e.g. "9.99 CHF" + */ + public static function money(int $amount, $currency, $locale = 'de_DE'): string + { + $amount = round($amount / 100, 2); + + $nf = new \NumberFormatter($locale, \NumberFormatter::CURRENCY); + $result = $nf->formatCurrency($amount, $currency); + + // Replace non-breaking space + return str_replace("\xC2\xA0", " ", $result); + } } diff --git a/src/app/Wallet.php b/src/app/Wallet.php --- a/src/app/Wallet.php +++ b/src/app/Wallet.php @@ -462,12 +462,7 @@ */ public function money(int $amount, $locale = 'de_DE') { - $amount = round($amount / 100, 2); - - $nf = new \NumberFormatter($locale, \NumberFormatter::CURRENCY); - $result = $nf->formatCurrency($amount, $this->currency); - // Replace non-breaking space - return str_replace("\xC2\xA0", " ", $result); + return \App\Utils::money($amount, $this->currency, $locale); } /** diff --git a/src/resources/js/app.js b/src/resources/js/app.js --- a/src/resources/js/app.js +++ b/src/resources/js/app.js @@ -33,9 +33,9 @@ return } - if (routerState.isLocked && to.meta.requiresAuth && !['login', 'wallet'].includes(to.name)) { - // redirect to the wallet page - next({ name: 'wallet' }) + if (routerState.isLocked && to.meta.requiresAuth && !['login', 'payment-status'].includes(to.name)) { + // redirect to the payment-status page + next({ name: 'payment-status' }) return } @@ -149,9 +149,9 @@ if (dashboard !== false) { this.$router.push(routerState.afterLogin || { name: response.redirect || 'dashboard' }) - } else if (routerState.isLocked && this.$route.name != 'wallet' && this.$route.meta.requiresAuth) { + } else if (routerState.isLocked && this.$route.meta.requiresAuth && this.$route.name != 'payment-status') { // Always redirect locked user, here we can be after router's beforeEach handler - this.$router.push({ name: 'wallet' }) + this.$router.push({ name: 'payment-status' }) } routerState.afterLogin = null diff --git a/src/resources/js/user/routes.js b/src/resources/js/user/routes.js --- a/src/resources/js/user/routes.js +++ b/src/resources/js/user/routes.js @@ -17,6 +17,7 @@ const DomainListComponent = () => import(/* webpackChunkName: "../user/pages" */ '../../vue/Domain/List') const FileInfoComponent = () => import(/* webpackChunkName: "../user/pages" */ '../../vue/File/Info') const FileListComponent = () => import(/* webpackChunkName: "../user/pages" */ '../../vue/File/List') +const PaymentStatusComponent = () => import(/* webpackChunkName: "../user/pages" */ '../../vue/Payment/Status') const ResourceInfoComponent = () => import(/* webpackChunkName: "../user/pages" */ '../../vue/Resource/Info') const ResourceListComponent = () => import(/* webpackChunkName: "../user/pages" */ '../../vue/Resource/List') const RoomInfoComponent = () => import(/* webpackChunkName: "../user/pages" */ '../../vue/Room/Info') @@ -108,6 +109,12 @@ component: PasswordResetComponent }, { + path: '/payment/status', + name: 'payment-status', + component: PaymentStatusComponent, + meta: { requiresAuth: true } + }, + { path: '/profile', name: 'profile', component: UserProfileComponent, diff --git a/src/resources/lang/en/app.php b/src/resources/lang/en/app.php --- a/src/resources/lang/en/app.php +++ b/src/resources/lang/en/app.php @@ -79,6 +79,15 @@ 'file-permissions-update-success' => 'File permissions updated successfully.', 'file-permissions-delete-success' => 'File permissions deleted successfully.', + 'payment-status-paid' => 'The payment has been completed successfully.', + 'payment-status-canceled' => 'The payment has been canceled.', + 'payment-status-failed' => 'The payment failed.', + 'payment-status-expired' => 'The payment expired.', + 'payment-status-checking' => "The payment hasn't been completed yet. Checking the status...", + + 'period-year' => 'year', + 'period-month' => 'month', + 'resource-update-success' => 'Resource updated successfully.', 'resource-create-success' => 'Resource created successfully.', 'resource-delete-success' => 'Resource deleted successfully.', @@ -112,6 +121,10 @@ 'search-foundxshared-folders' => ':x shared folders have been found.', 'search-foundxusers' => ':x user accounts have been found.', + 'signup-account-tobecreated' => 'The account is about to be created!', + 'signup-account-mandate' => 'Now it is required to provide your credit card details.' + . ' This way you agree to charge you with an appropriate amount of money according to the plan you signed up for.', + 'signup-account-summary' => 'You signed up for an account with a base cost of :cost per :period.', 'signup-invitations-created' => 'The invitation has been created.|:count invitations has been created.', 'signup-invitations-csv-empty' => 'Failed to find any valid email addresses in the uploaded file.', 'signup-invitations-csv-invalid-email' => 'Found an invalid email address (:email) on line :line.', diff --git a/src/resources/lang/en/ui.php b/src/resources/lang/en/ui.php --- a/src/resources/lang/en/ui.php +++ b/src/resources/lang/en/ui.php @@ -35,6 +35,7 @@ 'signup' => "Sign Up", 'submit' => "Submit", 'suspend' => "Suspend", + 'tryagain' => "Try again", 'unsuspend' => "Unsuspend", 'verify' => "Verify", ], diff --git a/src/resources/vue/Payment/Status.vue b/src/resources/vue/Payment/Status.vue new file mode 100644 --- /dev/null +++ b/src/resources/vue/Payment/Status.vue @@ -0,0 +1,64 @@ + + + diff --git a/src/resources/vue/Signup.vue b/src/resources/vue/Signup.vue --- a/src/resources/vue/Signup.vue +++ b/src/resources/vue/Signup.vue @@ -100,6 +100,16 @@ + +
+
+
+
+ {{ $t('btn.back') }} + {{ $t('btn.continue') }} +
+
+
@@ -119,6 +129,7 @@ }, data() { return { + checkout: {}, email: '', first_name: '', last_name: '', @@ -296,23 +307,34 @@ submitStep3() { this.$root.clearFormValidation($('#step3 form')) - let post = { - ...this.$root.pick(this, ['login', 'domain', 'voucher', 'plan']), - ...this.pass - } + const post = this.lastStepPostData() - if (this.invitation) { - post.invitation = this.invitation.id - post.first_name = this.first_name - post.last_name = this.last_name + if (this.mode == 'mandate') { + axios.post('/api/auth/signup/validate', post).then(response => { + this.checkout = response.data + this.displayForm(4) + }) } else { - post.code = this.code - post.short_code = this.short_code + axios.post('/api/auth/signup', post).then(response => { + // auto-login and goto dashboard + this.$root.loginUser(response.data) + }) } + }, + submitStep4() { + const post = this.lastStepPostData() axios.post('/api/auth/signup', post).then(response => { - // auto-login and goto dashboard - this.$root.loginUser(response.data) + // auto-login and goto to the payment checkout + this.$root.loginUser(response.data, false) + + let checkout = response.data.checkout + + if (checkout.redirectUrl) { + location.href = checkout.redirectUrl + } else if (checkout.id) { + // TODO: this.stripeCheckout(checkout) + } }) }, // Moves the user a step back in registration form @@ -328,7 +350,7 @@ step = 1 } - if (this.mode == 'mandate') { + if (this.mode == 'mandate' && step < 3) { step = 0 } @@ -340,7 +362,7 @@ } }, displayForm(step, focus) { - [0, 1, 2, 3].filter(value => value != step).forEach(value => { + [0, 1, 2, 3, 4].filter(value => value != step).forEach(value => { $('#step' + value).addClass('d-none') }) @@ -354,6 +376,23 @@ $('#step' + step).find('input').first().focus() } }, + lastStepPostData() { + let post = { + ...this.$root.pick(this, ['login', 'domain', 'voucher', 'plan']), + ...this.pass + } + + if (this.invitation) { + post.invitation = this.invitation.id + post.first_name = this.first_name + post.last_name = this.last_name + } else { + post.code = this.code + post.short_code = this.short_code + } + + return post + }, setDomain(response) { if (response.domains) { this.domains = response.domains diff --git a/src/resources/vue/Wallet.vue b/src/resources/vue/Wallet.vue --- a/src/resources/vue/Wallet.vue +++ b/src/resources/vue/Wallet.vue @@ -1,8 +1,5 @@