Merge pull request #6961 from turbo124/v5-develop

Restrict API access for free/unpaid accounts
This commit is contained in:
David Bomba 2021-11-13 07:19:16 +11:00 committed by GitHub
commit 2505b86360
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 319 additions and 589 deletions

View File

@ -59,7 +59,6 @@ class InvoiceController extends Controller
$invoice->service()->removeUnpaidGatewayFees()->save();
$invitation = $invoice->invitations()->where('client_contact_id', auth()->user()->id)->first();
if ($invitation && auth()->guard('contact') && ! request()->has('silent') && ! $invitation->viewed_date) {

View File

@ -13,12 +13,31 @@
namespace App\Http\Controllers\ClientPortal;
use App\Http\Controllers\Controller;
use App\Models\RecurringInvoice;
use App\Utils\Ninja;
use Illuminate\Http\Request;
class SubscriptionController extends Controller
{
public function index()
{
if(Ninja::isHosted()){
$count = RecurringInvoice::query()
->where('client_id', auth('contact')->user()->client->id)
->where('company_id', auth('contact')->user()->client->company_id)
->where('status_id', RecurringInvoice::STATUS_ACTIVE)
->whereNotNull('subscription_id')
->count();
if($count == 0)
return redirect()->route('client.ninja_contact_login', ['contact_key' => auth('contact')->user()->contact_key, 'company_key' => auth('contact')->user()->company->company_key]);
}
return render('subscriptions.index');
}
}

View File

@ -83,7 +83,7 @@ class ImportController extends Controller {
$contents = file_get_contents( $file->getPathname() );
// Store the csv in cache with an expiry of 10 minutes
Cache::put( $hash . '-' . $entityType, base64_encode( $contents ), 3600 );
Cache::put( $hash . '-' . $entityType, base64_encode( $contents ), 600 );
// Parse CSV
$csv_array = $this->getCsvData( $contents );
@ -111,7 +111,7 @@ class ImportController extends Controller {
$contents = file_get_contents( $file->getPathname() );
// Store the csv in cache with an expiry of 10 minutes
Cache::put( $hash . '-' . $entityType, base64_encode( $contents ), 3600 );
Cache::put( $hash . '-' . $entityType, base64_encode( $contents ), 600 );
}
}

View File

@ -522,9 +522,6 @@ class InvoiceController extends BaseController
$ids = request()->input('ids');
nlog($action);
nlog($ids);
$invoices = Invoice::withTrashed()->whereIn('id', $this->transformKeys($ids))->company()->get();
if (! $invoices) {
@ -542,7 +539,7 @@ nlog($ids);
return response()->json(['message' => ctrans('text.access_denied')]);
}
});
nlog("bulky");
ZipInvoices::dispatch($invoices, $invoices->first()->company, auth()->user());
return response()->json(['message' => ctrans('texts.sent_message')], 200);

View File

@ -293,7 +293,7 @@ class BillingPortalPurchase extends Component
return $this;
}
if ((int)$this->subscription->price == 0)
if ((int)$this->price == 0)
$this->steps['payment_required'] = false;
else
$this->steps['fetched_payment_methods'] = true;

View File

@ -42,7 +42,7 @@ class RecurringInvoicesTable extends Component
$query = $query
->where('client_id', auth('contact')->user()->client->id)
->where('company_id', $this->company->id)
->whereIn('status_id', [RecurringInvoice::STATUS_PENDING, RecurringInvoice::STATUS_ACTIVE, RecurringInvoice::STATUS_PAUSED,RecurringInvoice::STATUS_COMPLETED])
->whereIn('status_id', [RecurringInvoice::STATUS_ACTIVE])
->orderBy('status_id', 'asc')
->with('client')
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')

View File

@ -38,6 +38,7 @@ class SubscriptionRecurringInvoicesTable extends Component
->where('client_id', auth('contact')->user()->client->id)
->where('company_id', $this->company->id)
->whereNotNull('subscription_id')
->where('status_id', RecurringInvoice::STATUS_ACTIVE)
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
->withTrashed()
->paginate($this->per_page);

View File

@ -42,6 +42,16 @@ class TokenAuth
return response()->json($error, 403);
}
if(Ninja::isHosted() && $company_token->is_system == 0 && !$user->account->isPaid()){
$error = [
'message' => 'Feature not available with free / unpaid account.',
'errors' => new stdClass,
];
return response()->json($error, 403);
}
/*
|
| Necessary evil here: As we are authenticating on CompanyToken,

View File

@ -221,6 +221,9 @@ class BaseTransformer
{
$name = strtolower(trim($name));
if(strlen($name) == 2)
return $this->getCountryIdBy2($name);
return isset($this->maps['countries'][$name]) ? $this->maps['countries'][$name] : null;
}

View File

@ -587,7 +587,7 @@ class CSVImport implements ShouldQueue {
}
private function getCsvData( $entityType ) {
$base64_encoded_csv = Cache::get( $this->hash . '-' . $entityType );
$base64_encoded_csv = Cache::pull( $this->hash . '-' . $entityType );
if ( empty( $base64_encoded_csv ) ) {
return null;
}

View File

@ -70,7 +70,7 @@ class ZipInvoices implements ShouldQueue
*/
public function handle()
{nlog("bulky");
{
# create new zip object
$zip = new ZipArchive();

View File

@ -232,6 +232,7 @@ class Import implements ShouldQueue
$account = $this->company->account;
$account->default_company_id = $this->company->id;
$account->is_migrated = true;
$account->save();
//company size check

View File

@ -49,15 +49,19 @@ class SystemLogger implements ShouldQueue
public function handle() :void
{
if(!$this->company)
if(!$this->company){
nlog("SystemLogger:: No company");
return;
}
MultiDB::setDb($this->company->db);
$client_id = $this->client ? $this->client->id : null;
if(!$this->client && !$this->company->owner())
if(!$this->client && !$this->company->owner()){
nlog("SystemLogger:: could not find client and/or company owner");
return;
}
$user_id = $this->client ? $this->client->user_id : $this->company->owner()->id;
@ -71,9 +75,16 @@ class SystemLogger implements ShouldQueue
'type_id' => $this->type_id,
];
if(!$this->log)
if(!$this->log){
nlog("SystemLogger:: no log to store");
return;
}
SystemLog::create($sl);
}
public function failed($e)
{
nlog($e->getMessage());
}
}

View File

@ -52,6 +52,10 @@ class InvoiceCreatedNotification implements ShouldQueue
/* The User */
$user = $company_user->user;
if(!$user)
continue;
/* This is only here to handle the alternate message channels - ie Slack */
// $notification = new EntitySentNotification($event->invitation, 'invoice');
@ -71,11 +75,6 @@ class InvoiceCreatedNotification implements ShouldQueue
}
/* Override the methods in the Notification Class */
// $notification->method = $methods;
// Notify on the alternate channels
// $user->notify($notification);
}
}
}

View File

@ -53,6 +53,9 @@ class QuoteCreatedNotification implements ShouldQueue
/* The User */
$user = $company_user->user;
if(!$user)
continue;
/* This is only here to handle the alternate message channels - ie Slack */
// $notification = new EntitySentNotification($event->invitation, 'quote');

View File

@ -3,9 +3,11 @@
namespace App\Mail;
use App\Models\Company;
use App\Utils\Ninja;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\App;
class MigrationCompleted extends Mailable
{
@ -33,6 +35,11 @@ class MigrationCompleted extends Mailable
*/
public function build()
{
App::forgetInstance('translator');
$t = app('translator');
$t->replace(Ninja::transformTranslations($this->company->settings));
$data['settings'] = $this->company->settings;
$data['company'] = $this->company->fresh();
$data['whitelabel'] = $this->company->account->isPaid() ? true : false;

View File

@ -57,7 +57,7 @@ class HandleReversal extends AbstractService
$paymentables->each(function ($paymentable) use ($total_paid) {
//new concept - when reversing, we unwind the payments
$payment = Payment::find($paymentable->payment_id);
$payment = Payment::withTrashed()->find($paymentable->payment_id);
$reversable_amount = $paymentable->amount - $paymentable->refunded;
$total_paid -= $reversable_amount;

View File

@ -37,34 +37,37 @@ class MarkSent extends AbstractService
return $this->invoice;
}
$adjustment = $this->invoice->amount;
/*Set status*/
$this->invoice
->service()
->setStatus(Invoice::STATUS_SENT)
->updateBalance($adjustment, true)
->save();
$this->invoice
/*Adjust client balance*/
$this->client
->service()
->updateBalance($adjustment)
->save();
/*Update ledger*/
$this->invoice
->ledger()
->updateInvoiceBalance($adjustment, "Invoice {$this->invoice->number} marked as sent.");
/* Perform additional actions on invoice */
$this->invoice
->service()
->applyNumber()
->setDueDate()
->updateBalance($this->invoice->amount, true)
->deletePdf()
->setReminder()
->save();
$this->invoice->markInvitationsSent();
/*Adjust client balance*/
$this->client
->service()
->updateBalance($this->invoice->balance)
->save();
/*Update ledger*/
$this->invoice
->ledger()
->updateInvoiceBalance($this->invoice->balance, "Invoice {$this->invoice->number} marked as sent.");
event(new InvoiceWasUpdated($this->invoice, $this->invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
return $this->invoice->fresh();

View File

@ -290,6 +290,9 @@ class SubscriptionService
$days_in_frequency = $this->getDaysInFrequency();
if($days_of_subscription_used >= $days_in_frequency)
return 0;
$pro_rata_refund = round((($days_in_frequency - $days_of_subscription_used)/$days_in_frequency) * $invoice->amount ,2);
// nlog("days in frequency = {$days_in_frequency} - days of subscription used {$days_of_subscription_used}");
@ -322,7 +325,8 @@ class SubscriptionService
$days_of_subscription_used = $start_date->diffInDays($current_date);
$days_in_frequency = $this->getDaysInFrequency();
// $days_in_frequency = $this->getDaysInFrequency();
$days_in_frequency = $invoice->subscription->service()->getDaysInFrequency();
$ratio = ($days_in_frequency - $days_of_subscription_used)/$days_in_frequency;
@ -427,6 +431,8 @@ class SubscriptionService
nlog("total payable = {$total_payable}");
$credit = false;
/* Only generate a credit if the previous invoice was paid in full. */
if($last_invoice->balance == 0)
$credit = $this->createCredit($last_invoice, $target_subscription, $is_credit);
@ -436,7 +442,7 @@ class SubscriptionService
$context = [
'context' => 'change_plan',
'recurring_invoice' => $new_recurring_invoice->hashed_id,
'credit' => $credit->hashed_id,
'credit' => $credit ? $credit->hashed_id : null,
'client' => $new_recurring_invoice->client->hashed_id,
'subscription' => $target_subscription->hashed_id,
'contact' => auth('contact')->user()->hashed_id,
@ -446,7 +452,10 @@ class SubscriptionService
nlog($response);
return $this->handleRedirect('/client/credits/'.$credit->hashed_id);
if($credit)
return $this->handleRedirect('/client/credits/'.$credit->hashed_id);
else
return $this->handleRedirect('/client/credits');
}
@ -545,6 +554,9 @@ class SubscriptionService
$old_recurring_invoice = RecurringInvoice::find($payment_hash->data->billing_context->recurring_invoice);
if(!$old_recurring_invoice)
return $this->handleRedirect('/client/recurring_invoices/');
$recurring_invoice = $this->createNewRecurringInvoice($old_recurring_invoice);
$context = [
@ -702,7 +714,7 @@ class SubscriptionService
$recurring_invoice = RecurringInvoiceFactory::create($this->subscription->company_id, $this->subscription->user_id);
$recurring_invoice->client_id = $client_id;
$recurring_invoice->line_items = $subscription_repo->generateLineItems($this->subscription, true);
$recurring_invoice->line_items = $subscription_repo->generateLineItems($this->subscription, true, false);
$recurring_invoice->subscription_id = $this->subscription->id;
$recurring_invoice->frequency_id = $this->subscription->frequency_id ?: RecurringInvoice::FREQUENCY_MONTHLY;
$recurring_invoice->date = now();

View File

@ -82,6 +82,7 @@ class AccountTransformer extends EntityTransformer
'disable_auto_update' => (bool) config('ninja.disable_auto_update'),
'emails_sent' => (int) $account->emailsSent(),
'email_quota' => (int) $account->getDailyEmailLimit(),
'is_migrated' => (bool) $account->is_migrated,
];
}

View File

@ -113,8 +113,14 @@ class Ninja
public static function eventVars($user_id = null)
{
if(request()->hasHeader('Cf-Connecting-Ip'))
$ip = request()->header('Cf-Connecting-Ip');
else
$ip = request()->getClientIp();
return [
'ip' => request()->getClientIp(),
'ip' => $ip,
'token' => request()->header('X-API-TOKEN'),
'is_system' => app()->runningInConsole(),
'user_id' => $user_id,

View File

@ -207,7 +207,7 @@ return [
['options' => [
'replication' => 'sentinel',
'service' => env('REDIS_SENTINEL_SERVICE', 'mymaster'),
'sentinel_timeout' => 1.0,
'sentinel_timeout' => 2.0,
'parameters' => [
'password' => env('REDIS_PASSWORD', null),
'database' => env('REDIS_DB', 0),
@ -226,7 +226,7 @@ return [
['options' => [
'replication' => 'sentinel',
'service' => env('REDIS_SENTINEL_SERVICE', 'mymaster'),
'sentinel_timeout' => 1.0,
'sentinel_timeout' => 2.0,
'parameters' => [
'password' => env('REDIS_PASSWORD', null),
'database' => env('REDIS_CACHE_DB', 1),

View File

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddIsMigrateColumnToAccountsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('accounts', function (Blueprint $table) {
$table->boolean('is_migrated')->default(false);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('accounts', function (Blueprint $table) {
//
});
}
}

663
package-lock.json generated

File diff suppressed because it is too large Load Diff

2
public/js/app.js vendored

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
(()=>{var e,t={8945:(e,t,r)=>{"use strict";const a=r(920),n=r(3523),s=r(2263),o=new Set(n);e.exports=e=>{if((e=Object.assign({name:"div",attributes:{},html:""},e)).html&&e.text)throw new Error("The `html` and `text` options are mutually exclusive");const t=e.text?s.escape(e.text):e.html;let r=`<${e.name}${a(e.attributes)}>`;return o.has(e.name)||(r+=`${t}</${e.name}>`),r}},3523:(e,t,r)=>{"use strict";e.exports=r(8346)},2263:(e,t)=>{"use strict";t.escape=e=>e.replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/'/g,"&#39;").replace(/</g,"&lt;").replace(/>/g,"&gt;"),t.unescape=e=>e.replace(/&gt;/g,">").replace(/&lt;/g,"<").replace(/&#39;/g,"'").replace(/&quot;/g,'"').replace(/&amp;/g,"&"),t.escapeTag=function(e){let r=e[0];for(let a=1;a<arguments.length;a++)r=r+t.escape(arguments[a])+e[a];return r},t.unescapeTag=function(e){let r=e[0];for(let a=1;a<arguments.length;a++)r=r+t.unescape(arguments[a])+e[a];return r}},1881:(e,t,r)=>{"use strict";const a=r(8945),n=(e,t)=>a({name:"a",attributes:{href:"",...t.attributes,href:e},text:void 0===t.value?e:void 0,html:void 0===t.value?void 0:"function"==typeof t.value?t.value(e):t.value});e.exports=(e,t)=>{if("string"===(t={attributes:{},type:"string",...t}).type)return((e,t)=>e.replace(/((?<!\+)(?:https?(?::\/\/))(?:www\.)?(?:[a-zA-Z\d-_.]+(?:(?:\.|@)[a-zA-Z\d]{2,})|localhost)(?:(?:[-a-zA-Z\d:%_+.~#*$!?&//=@]*)(?:[,](?![\s]))*)*)/g,(e=>n(e,t))))(e,t);if("dom"===t.type)return((e,t)=>{const r=document.createDocumentFragment();for(const[s,o]of Object.entries(e.split(/((?<!\+)(?:https?(?::\/\/))(?:www\.)?(?:[a-zA-Z\d-_.]+(?:(?:\.|@)[a-zA-Z\d]{2,})|localhost)(?:(?:[-a-zA-Z\d:%_+.~#*$!?&//=@]*)(?:[,](?![\s]))*)*)/g)))s%2?r.append((a=n(o,t),document.createRange().createContextualFragment(a))):o.length>0&&r.append(o);var a;return r})(e,t);throw new Error("The type option must be either `dom` or `string`")}},920:(e,t,r)=>{"use strict";const a=r(2263);e.exports=e=>{const t=[];for(const r of Object.keys(e)){let n=e[r];if(!1===n)continue;Array.isArray(n)&&(n=n.join(" "));let s=a.escape(r);!0!==n&&(s+=`="${a.escape(String(n))}"`),t.push(s)}return t.length>0?" "+t.join(" "):""}},8346:e=>{"use strict";e.exports=JSON.parse('["area","base","br","col","embed","hr","img","input","link","menuitem","meta","param","source","track","wbr"]')}},r={};function a(e){var n=r[e];if(void 0!==n)return n.exports;var s=r[e]={exports:{}};return t[e](s,s.exports,a),s.exports}e=a(1881),document.querySelectorAll("[data-ref=entity-terms]").forEach((function(t){t.innerHTML=e(t.innerText,{attributes:{target:"_blank",class:"text-primary"}})}))})();
(()=>{var e,t={2623:(e,t,r)=>{"use strict";e.exports=r(4666)},1886:(e,t)=>{"use strict";const r=e=>e.replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/'/g,"&#39;").replace(/</g,"&lt;").replace(/>/g,"&gt;"),n=e=>e.replace(/&gt;/g,">").replace(/&lt;/g,"<").replace(/&#0?39;/g,"'").replace(/&quot;/g,'"').replace(/&amp;/g,"&");t.T=(e,...t)=>{if("string"==typeof e)return r(e);let n=e[0];for(const[o,a]of t.entries())n=n+r(String(a))+e[o+1];return n}},7636:(e,t,r)=>{"use strict";r.r(t),r.d(t,{default:()=>s});var n=r(1886);var o=r(2623);const a=e=>e.replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/'/g,"&#39;").replace(/</g,"&lt;").replace(/>/g,"&gt;");const i=new Set(o);function c({name:e="div",attributes:t={},html:r="",text:o}={}){if(r&&o)throw new Error("The `html` and `text` options are mutually exclusive");const c=o?function(e,...t){if("string"==typeof e)return a(e);let r=e[0];for(const[n,o]of t.entries())r=r+a(String(o))+e[n+1];return r}(o):r;let l=`<${e}${function(e){const t=[];for(let[r,o]of Object.entries(e)){if(!1===o)continue;Array.isArray(o)&&(o=o.join(" "));let e=(0,n.T)(r);!0!==o&&(e+=`="${(0,n.T)(String(o))}"`),t.push(e)}return t.length>0?" "+t.join(" "):""}(t)}>`;return i.has(e)||(l+=`${c}</${e}>`),l}const l=(e,t)=>c({name:"a",attributes:{href:"",...t.attributes,href:e},text:void 0===t.value?e:void 0,html:void 0===t.value?void 0:"function"==typeof t.value?t.value(e):t.value});function s(e,t){if("string"===(t={attributes:{},type:"string",...t}).type)return((e,t)=>e.replace(/((?<!\+)https?:\/\/(?:www\.)?(?:[-\w.]+?[.@][a-zA-Z\d]{2,}|localhost)(?:[-\w.:%+~#*$!?&/=@]*?(?:,(?!\s))*?)*)/g,(e=>l(e,t))))(e,t);if("dom"===t.type)return((e,t)=>{const r=document.createDocumentFragment();for(const[o,a]of Object.entries(e.split(/((?<!\+)https?:\/\/(?:www\.)?(?:[-\w.]+?[.@][a-zA-Z\d]{2,}|localhost)(?:[-\w.:%+~#*$!?&/=@]*?(?:,(?!\s))*?)*)/g)))o%2?r.append((n=l(a,t),document.createRange().createContextualFragment(n))):a.length>0&&r.append(a);var n;return r})(e,t);throw new TypeError("The type option must be either `dom` or `string`")}},4666:e=>{"use strict";e.exports=JSON.parse('["area","base","br","col","embed","hr","img","input","link","menuitem","meta","param","source","track","wbr"]')}},r={};function n(e){var o=r[e];if(void 0!==o)return o.exports;var a=r[e]={exports:{}};return t[e](a,a.exports,n),a.exports}n.d=(e,t)=>{for(var r in t)n.o(t,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),n.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},e=n(7636),document.querySelectorAll("[data-ref=entity-terms]").forEach((function(t){t.innerHTML=e(t.innerText,{attributes:{target:"_blank",class:"text-primary"}})}))})();

View File

@ -1,2 +1,2 @@
/*! For license information please see wepay-bank-account.js.LICENSE.txt */
(()=>{function e(e,n){for(var t=0;t<n.length;t++){var o=n[t];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(e,o.key,o)}}var n=function(){function n(){!function(e,n){if(!(e instanceof n))throw new TypeError("Cannot call a class as a function")}(this,n)}var t,o,r;return t=n,(o=[{key:"initializeWePay",value:function(){var e,n=null===(e=document.querySelector('meta[name="wepay-environment"]'))||void 0===e?void 0:e.content;return WePay.set_endpoint("staging"===n?"stage":"production"),this}},{key:"showBankPopup",value:function(){var e,n;WePay.bank_account.create({client_id:null===(e=document.querySelector("meta[name=wepay-client-id]"))||void 0===e?void 0:e.content,email:null===(n=document.querySelector("meta[name=contact-email]"))||void 0===n?void 0:n.content},(function(e){e.error?(errors.textContent="",errors.textContent=e.error_description,errors.hidden=!1):(document.querySelector('input[name="bank_account_id"]').value=e.bank_account_id,document.getElementById("server_response").submit())}),(function(e){e.error&&(errors.textContent="",errors.textContent=e.error_description,errors.hidden=!1)}))}},{key:"handle",value:function(){this.initializeWePay().showBankPopup()}}])&&e(t.prototype,o),r&&e(t,r),n}();document.addEventListener("DOMContentLoaded",(function(){(new n).handle()}))})();
(()=>{function e(e,n){for(var t=0;t<n.length;t++){var o=n[t];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(e,o.key,o)}}var n=function(){function n(){!function(e,n){if(!(e instanceof n))throw new TypeError("Cannot call a class as a function")}(this,n)}var t,o,r;return t=n,(o=[{key:"initializeWePay",value:function(){var e,n=null===(e=document.querySelector('meta[name="wepay-environment"]'))||void 0===e?void 0:e.content;return WePay.set_endpoint("staging"===n?"stage":"production"),this}},{key:"showBankPopup",value:function(){var e,n;WePay.bank_account.create({client_id:null===(e=document.querySelector("meta[name=wepay-client-id]"))||void 0===e?void 0:e.content,email:null===(n=document.querySelector("meta[name=contact-email]"))||void 0===n?void 0:n.content,options:{avoidMicrodeposits:!0}},(function(e){e.error?(errors.textContent="",errors.textContent=e.error_description,errors.hidden=!1):(document.querySelector('input[name="bank_account_id"]').value=e.bank_account_id,document.getElementById("server_response").submit())}),(function(e){e.error&&(errors.textContent="",errors.textContent=e.error_description,errors.hidden=!1)}))}},{key:"handle",value:function(){this.initializeWePay().showBankPopup()}}])&&e(t.prototype,o),r&&e(t,r),n}();document.addEventListener("DOMContentLoaded",(function(){(new n).handle()}))})();

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,5 @@
{
"/js/app.js": "/js/app.js?id=0d1e02ebdcc97462d422",
"/js/app.js": "/js/app.js?id=0e3959ab851d3350364d",
"/js/clients/payment_methods/authorize-authorize-card.js": "/js/clients/payment_methods/authorize-authorize-card.js?id=de4468c682d6861847de",
"/js/clients/payments/authorize-credit-card-payment.js": "/js/clients/payments/authorize-credit-card-payment.js?id=cfe5de1cf87a0b01568d",
"/js/clients/payments/stripe-ach.js": "/js/clients/payments/stripe-ach.js?id=5e74bc0d346beeb57ee9",
@ -11,15 +11,15 @@
"/js/clients/quotes/action-selectors.js": "/js/clients/quotes/action-selectors.js?id=63f0688329be80ee8693",
"/js/clients/quotes/approve.js": "/js/clients/quotes/approve.js?id=795d2f44cf3d117a554e",
"/js/clients/payments/stripe-credit-card.js": "/js/clients/payments/stripe-credit-card.js?id=ea4250be693260798735",
"/js/setup/setup.js": "/js/setup/setup.js?id=6b870beeb350d83668c5",
"/js/setup/setup.js": "/js/setup/setup.js?id=7e19431f4cb9ad45e177",
"/js/clients/payments/card-js.min.js": "/js/clients/payments/card-js.min.js?id=8ce33c3deae058ad314f",
"/js/clients/shared/pdf.js": "/js/clients/shared/pdf.js?id=73a0d914ad3577f257f4",
"/js/clients/shared/multiple-downloads.js": "/js/clients/shared/multiple-downloads.js?id=c2caa29f753ad1f3a12c",
"/js/clients/linkify-urls.js": "/js/clients/linkify-urls.js?id=448d055fa1e8357130e6",
"/js/clients/linkify-urls.js": "/js/clients/linkify-urls.js?id=44c51b4838d1f135bbe3",
"/js/clients/payments/braintree-credit-card.js": "/js/clients/payments/braintree-credit-card.js?id=a334dd9257dd510a1feb",
"/js/clients/payments/braintree-paypal.js": "/js/clients/payments/braintree-paypal.js?id=37950e8a39281d2f596a",
"/js/clients/payments/wepay-credit-card.js": "/js/clients/payments/wepay-credit-card.js?id=ba4d5b7175117ababdb2",
"/js/clients/payment_methods/wepay-bank-account.js": "/js/clients/payment_methods/wepay-bank-account.js?id=b1704cb9bd7975605310",
"/js/clients/payment_methods/wepay-bank-account.js": "/js/clients/payment_methods/wepay-bank-account.js?id=8328c6c32a65cd3e8a3d",
"/js/clients/payments/paytrace-credit-card.js": "/js/clients/payments/paytrace-credit-card.js?id=59d9913b746fe5a540ff",
"/js/clients/payments/mollie-credit-card.js": "/js/clients/payments/mollie-credit-card.js?id=c2cf632fb3cc91b4ff7c",
"/js/clients/payments/eway-credit-card.js": "/js/clients/payments/eway-credit-card.js?id=ff17e039dd15d505448f",

View File

@ -20,7 +20,10 @@ class WePayBank {
showBankPopup() {
WePay.bank_account.create({
client_id: document.querySelector('meta[name=wepay-client-id]')?.content,
email: document.querySelector('meta[name=contact-email]')?.content
email: document.querySelector('meta[name=contact-email]')?.content,
options: {
avoidMicrodeposits:true
}
}, function (data) {
if (data.error) {
errors.textContent = '';

View File

@ -61,7 +61,7 @@
@endif
</div>
</div>
@elseif($amount < 0)
@elseif($amount <= 0)
<div class="relative flex justify-center text-sm leading-5">
<h1 class="text-2xl font-bold tracking-wide bg-gray-100 px-6 py-0">
{{ ctrans('texts.total') }}: {{ \App\Utils\Number::formatMoney($amount, $subscription->company) }}

View File

@ -2,18 +2,18 @@
use Illuminate\Support\Facades\Route;
Route::get('client', 'Auth\ContactLoginController@showLoginForm')->name('client.catchall')->middleware(['domain_db', 'contact_account','locale']); //catch all
Route::get('client', 'Auth\ContactLoginController@showLoginForm')->name('client.catchall')->middleware(['throttle:10,1','domain_db', 'contact_account','locale']); //catch all
Route::get('client/login', 'Auth\ContactLoginController@showLoginForm')->name('client.login')->middleware(['domain_db', 'contact_account','locale']);
Route::get('client/login', 'Auth\ContactLoginController@showLoginForm')->name('client.login')->middleware(['throttle:10,1','domain_db', 'contact_account','locale']);
Route::post('client/login', 'Auth\ContactLoginController@login')->name('client.login.submit');
Route::get('client/register/{company_key?}', 'Auth\ContactRegisterController@showRegisterForm')->name('client.register')->middleware(['domain_db', 'contact_account', 'contact_register','locale']);
Route::post('client/register/{company_key?}', 'Auth\ContactRegisterController@register')->middleware(['domain_db', 'contact_account', 'contact_register', 'locale']);
Route::get('client/register/{company_key?}', 'Auth\ContactRegisterController@showRegisterForm')->name('client.register')->middleware(['throttle:10,1','domain_db', 'contact_account', 'contact_register','locale']);
Route::post('client/register/{company_key?}', 'Auth\ContactRegisterController@register')->middleware(['throttle:10,1','domain_db', 'contact_account', 'contact_register', 'locale']);
Route::get('client/password/reset', 'Auth\ContactForgotPasswordController@showLinkRequestForm')->name('client.password.request')->middleware(['domain_db', 'contact_account','locale']);
Route::post('client/password/email', 'Auth\ContactForgotPasswordController@sendResetLinkEmail')->name('client.password.email')->middleware('locale');
Route::get('client/password/reset/{token}', 'Auth\ContactResetPasswordController@showResetForm')->name('client.password.reset')->middleware(['domain_db', 'contact_account','locale']);
Route::post('client/password/reset', 'Auth\ContactResetPasswordController@reset')->name('client.password.update')->middleware(['domain_db', 'contact_account','locale']);
Route::get('client/password/reset', 'Auth\ContactForgotPasswordController@showLinkRequestForm')->name('client.password.request')->middleware(['throttle:10,1','domain_db', 'contact_account','locale']);
Route::post('client/password/email', 'Auth\ContactForgotPasswordController@sendResetLinkEmail')->name('client.password.email')->middleware('throttle:10,1','locale');
Route::get('client/password/reset/{token}', 'Auth\ContactResetPasswordController@showResetForm')->name('client.password.reset')->middleware(['throttle:10,1','domain_db', 'contact_account','locale']);
Route::post('client/password/reset', 'Auth\ContactResetPasswordController@reset')->name('client.password.update')->middleware(['throttle:10,1','domain_db', 'contact_account','locale']);
Route::get('view/{entity_type}/{invitation_key}', 'ClientPortal\EntityViewController@index')->name('client.entity_view');
Route::get('view/{entity_type}/{invitation_key}/password', 'ClientPortal\EntityViewController@password')->name('client.entity_view.password');
@ -21,14 +21,14 @@ Route::post('view/{entity_type}/{invitation_key}/password', 'ClientPortal\Entity
Route::get('tmp_pdf/{hash}', 'ClientPortal\TempRouteController@index')->name('tmp_pdf');
Route::get('client/key_login/{contact_key}', 'ClientPortal\ContactHashLoginController@login')->name('client.contact_login')->middleware(['domain_db','contact_key_login']);
Route::get('client/magic_link/{magic_link}', 'ClientPortal\ContactHashLoginController@magicLink')->name('client.contact_magic_link')->middleware(['domain_db','contact_key_login']);
Route::get('documents/{document_hash}', 'ClientPortal\DocumentController@publicDownload')->name('documents.public_download')->middleware(['document_db']);
Route::get('client/key_login/{contact_key}', 'ClientPortal\ContactHashLoginController@login')->name('client.contact_login')->middleware(['throttle:40,1','domain_db','contact_key_login']);
Route::get('client/magic_link/{magic_link}', 'ClientPortal\ContactHashLoginController@magicLink')->name('client.contact_magic_link')->middleware(['throttle:40,1','domain_db','contact_key_login']);
Route::get('documents/{document_hash}', 'ClientPortal\DocumentController@publicDownload')->name('documents.public_download')->middleware(['throttle:40,1','document_db']);
Route::get('error', 'ClientPortal\ContactHashLoginController@errorPage')->name('client.error');
Route::get('client/payment/{contact_key}/{payment_id}', 'ClientPortal\InvitationController@paymentRouter')->middleware(['domain_db','contact_key_login']);
Route::get('client/ninja/{contact_key}/{company_key}', 'ClientPortal\NinjaPlanController@index')->name('client.ninja_contact_login')->middleware(['domain_db']);
Route::get('client/payment/{contact_key}/{payment_id}', 'ClientPortal\InvitationController@paymentRouter')->middleware(['throttle:40,1','domain_db','contact_key_login']);
Route::get('client/ninja/{contact_key}/{company_key}', 'ClientPortal\NinjaPlanController@index')->name('client.ninja_contact_login')->middleware(['throttle:40,1','domain_db']);
Route::group(['middleware' => ['auth:contact', 'locale', 'check_client_existence','domain_db'], 'prefix' => 'client', 'as' => 'client.'], function () {
Route::group(['middleware' => ['throttle:60,1','auth:contact', 'locale', 'check_client_existence','domain_db'], 'prefix' => 'client', 'as' => 'client.'], function () {
Route::get('dashboard', 'ClientPortal\DashboardController@index')->name('dashboard'); // name = (dashboard. index / create / show / update / destroy / edit
Route::get('invoices', 'ClientPortal\InvoiceController@index')->name('invoices.index')->middleware('portal_enabled');
@ -92,7 +92,7 @@ Route::group(['middleware' => ['auth:contact', 'locale', 'check_client_existence
Route::get('client/subscriptions/{subscription}/purchase', 'ClientPortal\SubscriptionPurchaseController@index')->name('client.subscription.purchase')->middleware('domain_db');
Route::group(['middleware' => ['invite_db'], 'prefix' => 'client', 'as' => 'client.'], function () {
Route::group(['middleware' => ['throttle:40,1','invite_db'], 'prefix' => 'client', 'as' => 'client.'], function () {
/*Invitation catches*/
Route::get('recurring_invoice/{invitation_key}', 'ClientPortal\InvitationController@recurringRouter');
Route::get('invoice/{invitation_key}', 'ClientPortal\InvitationController@invoiceRouter');
@ -108,6 +108,6 @@ Route::group(['middleware' => ['invite_db'], 'prefix' => 'client', 'as' => 'clie
});
Route::get('phantom/{entity}/{invitation_key}', '\App\Utils\PhantomJS\Phantom@displayInvitation')->middleware(['invite_db', 'phantom_secret'])->name('phantom_view');
Route::get('phantom/{entity}/{invitation_key}', '\App\Utils\PhantomJS\Phantom@displayInvitation')->middleware(['throttle:60,1','invite_db', 'phantom_secret'])->name('phantom_view');
Route::fallback('BaseController@notFoundClient');

View File

@ -297,6 +297,8 @@ class ClientTest extends TestCase
$company_token->account_id = $account->id;
$company_token->name = $user->first_name.' '.$user->last_name;
$company_token->token = Str::random(64);
$company_token->is_system = true;
$company_token->save();
$this->token = $company_token->token;
@ -353,6 +355,7 @@ class ClientTest extends TestCase
$company_token->account_id = $account->id;
$company_token->name = $user->first_name.' '.$user->last_name;
$company_token->token = Str::random(64);
$company_token->is_system = true;
$company_token->save();
$this->token = $company_token->token;

View File

@ -160,6 +160,7 @@ class LoginTest extends TestCase
$company_token->account_id = $account->id;
$company_token->name = $user->first_name.' '.$user->last_name;
$company_token->token = \Illuminate\Support\Str::random(64);
$company_token->is_system = true;
$company_token->save();
$user->companies()->attach($company->id, [

View File

@ -167,6 +167,7 @@ class UserTest extends TestCase
$company_token->account_id = $this->account->id;
$company_token->name = 'test token';
$company_token->token = \Illuminate\Support\Str::random(64);
$company_token->is_system = true;
$company_token->save();
/*Manually link this user to the company*/

View File

@ -128,6 +128,7 @@ class CompanyLedgerTest extends TestCase
$company_token->account_id = $this->account->id;
$company_token->name = 'test token';
$company_token->token = $this->token;
$company_token->is_system = true;
$company_token->save();
$this->client = Client::factory()->create([