mirror of
				https://github.com/invoiceninja/invoiceninja.git
				synced 2025-11-03 20:18:33 -05:00 
			
		
		
		
	Merge pull request #8346 from turbo124/v5-develop
Fixes for FROM address
This commit is contained in:
		
						commit
						0841ecb124
					
				@ -1 +1 @@
 | 
			
		||||
5.5.84
 | 
			
		||||
5.5.85
 | 
			
		||||
@ -85,7 +85,8 @@ class InvitationController extends Controller
 | 
			
		||||
                                    ->with('contact.client')
 | 
			
		||||
                                    ->firstOrFail();
 | 
			
		||||
 | 
			
		||||
        if ($invitation->{$entity}->is_deleted) {
 | 
			
		||||
        //09-03-2023 do not show entity if the invitation has been trashed.
 | 
			
		||||
        if ($invitation->trashed() || $invitation->{$entity}->is_deleted) {
 | 
			
		||||
            return $this->render('generic.not_available', ['account' => $invitation->company->account, 'company' => $invitation->company]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -93,7 +93,7 @@ class UpdateCompanyRequest extends Request
 | 
			
		||||
     * are saveable
 | 
			
		||||
     *
 | 
			
		||||
     * @param  object $settings
 | 
			
		||||
     * @return stdClass $settings
 | 
			
		||||
     * @return \stdClass $settings
 | 
			
		||||
     */
 | 
			
		||||
    private function filterSaveableSettings($settings)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
@ -73,6 +73,11 @@ class UpdateUserRequest extends Request
 | 
			
		||||
            $input['oauth_user_id'] = '';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (array_key_exists('oauth_user_token', $input) && $input['oauth_user_token'] == '***') {
 | 
			
		||||
            unset($input['oauth_user_token']);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        $this->replace($input);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										16
									
								
								app/Http/Requests/Vendor/StoreVendorRequest.php
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										16
									
								
								app/Http/Requests/Vendor/StoreVendorRequest.php
									
									
									
									
										vendored
									
									
								
							@ -23,26 +23,20 @@ class StoreVendorRequest extends Request
 | 
			
		||||
    /**
 | 
			
		||||
     * Determine if the user is authorized to make this request.
 | 
			
		||||
     *
 | 
			
		||||
     * @return bool
 | 
			
		||||
     * @method static \Illuminate\Contracts\Auth\Authenticatable|null user()
 | 
			
		||||
     */
 | 
			
		||||
    public function authorize() : bool
 | 
			
		||||
    {
 | 
			
		||||
        /** @var \App\User|null $user */
 | 
			
		||||
        $user = auth()->user();
 | 
			
		||||
 | 
			
		||||
        return $user->can('create', Vendor::class);
 | 
			
		||||
        return auth()->user()->can('create', Vendor::class);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function rules()
 | 
			
		||||
    {
 | 
			
		||||
        /** @var \App\User|null $user */
 | 
			
		||||
        $user = auth()->user();
 | 
			
		||||
        $rules = [];
 | 
			
		||||
        
 | 
			
		||||
        $rules['contacts.*.email'] = 'bail|nullable|distinct|sometimes|email';
 | 
			
		||||
 | 
			
		||||
        if (isset($this->number)) {
 | 
			
		||||
            $rules['number'] = Rule::unique('vendors')->where('company_id', $user->company()->id);
 | 
			
		||||
            $rules['number'] = Rule::unique('vendors')->where('company_id', auth()->user()->company()->id);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        $rules['currency_id'] = 'bail|required|exists:currencies,id';
 | 
			
		||||
@ -63,13 +57,11 @@ class StoreVendorRequest extends Request
 | 
			
		||||
 | 
			
		||||
    public function prepareForValidation()
 | 
			
		||||
    {
 | 
			
		||||
        /** @var \App\User|null $user */
 | 
			
		||||
        $user = auth()->user();
 | 
			
		||||
 | 
			
		||||
        $input = $this->all();
 | 
			
		||||
 | 
			
		||||
        if (!array_key_exists('currency_id', $input) || empty($input['currency_id'])) {
 | 
			
		||||
            $input['currency_id'] = $user->company()->settings->currency_id;
 | 
			
		||||
            $input['currency_id'] = auth()->user()->company()->settings->currency_id;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $input = $this->decodePrimaryKeys($input);
 | 
			
		||||
 | 
			
		||||
@ -24,11 +24,8 @@ class UpdateReminders implements ShouldQueue
 | 
			
		||||
{
 | 
			
		||||
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 | 
			
		||||
 | 
			
		||||
    public Company $company;
 | 
			
		||||
 | 
			
		||||
    public function __construct(Company $company)
 | 
			
		||||
    public function __construct(public Company $company)
 | 
			
		||||
    {
 | 
			
		||||
        $this->company = $company;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 | 
			
		||||
@ -22,7 +22,7 @@ use Illuminate\Queue\InteractsWithQueue;
 | 
			
		||||
use Illuminate\Queue\SerializesModels;
 | 
			
		||||
use Illuminate\Support\Facades\Cache;
 | 
			
		||||
use Turbo124\Beacon\Facades\LightLogs;
 | 
			
		||||
 | 
			
		||||
use Illuminate\Support\Facades\Redis;
 | 
			
		||||
class AdjustEmailQuota implements ShouldQueue
 | 
			
		||||
{
 | 
			
		||||
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 | 
			
		||||
@ -61,18 +61,47 @@ class AdjustEmailQuota implements ShouldQueue
 | 
			
		||||
        Account::query()->cursor()->each(function ($account) {
 | 
			
		||||
            nlog("resetting email quota for {$account->key}");
 | 
			
		||||
 | 
			
		||||
            $email_count = Cache::get($account->key);
 | 
			
		||||
            $email_count = Cache::get("email_quota".$account->key);
 | 
			
		||||
 | 
			
		||||
            if ($email_count > 0) {
 | 
			
		||||
                try {
 | 
			
		||||
                    LightLogs::create(new EmailCount($email_count, $account->key))->send();
 | 
			
		||||
                    LightLogs::create(new EmailCount($email_count, $account->key))->send(); // this runs syncronously
 | 
			
		||||
                } catch(\Exception $e) {
 | 
			
		||||
                    nlog($e->getMessage());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            Cache::forget($account->key);
 | 
			
		||||
            Cache::forget("throttle_notified:{$account->key}");
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        /** Use redis pipelines to execute bulk deletes efficiently */
 | 
			
		||||
        $redis = Redis::connection('sentinel-cache');
 | 
			
		||||
        $prefix =  config('cache.prefix'). ":email_quota*";
 | 
			
		||||
 | 
			
		||||
        $keys = $redis->keys($prefix);
 | 
			
		||||
 | 
			
		||||
        if(is_array($keys))
 | 
			
		||||
        {
 | 
			
		||||
            $redis->pipeline(function ($pipe) use ($keys) {
 | 
			
		||||
 | 
			
		||||
                foreach ($keys as $key) {
 | 
			
		||||
                    $pipe->del($key);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        $keys = null;
 | 
			
		||||
 | 
			
		||||
        $prefix =  config('cache.prefix'). ":throttle_notified*";
 | 
			
		||||
 | 
			
		||||
        $keys = $redis->keys($prefix);
 | 
			
		||||
 | 
			
		||||
        if (is_array($keys)) {
 | 
			
		||||
            $redis->pipeline(function ($pipe) use ($keys) {
 | 
			
		||||
                foreach ($keys as $key) {
 | 
			
		||||
                    $pipe->del($key);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -26,8 +26,6 @@ class CleanStaleInvoiceOrder implements ShouldQueue
 | 
			
		||||
    /**
 | 
			
		||||
     * Create a new job instance.
 | 
			
		||||
     *
 | 
			
		||||
     * @param int invoice_id
 | 
			
		||||
     * @param string $db
 | 
			
		||||
     */
 | 
			
		||||
    public function __construct()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
@ -339,7 +339,8 @@ class Account extends BaseModel
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($this->plan_expires && Carbon::parse($this->plan_expires)->lt(now())) {
 | 
			
		||||
        // 09-03-2023 - winds forward expiry checks to ensure we don't cut off users prior to billing cycle being commenced
 | 
			
		||||
        if ($this->plan_expires && Carbon::parse($this->plan_expires)->lt(now()->subHours(12))) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -352,7 +353,7 @@ class Account extends BaseModel
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($this->plan_expires && Carbon::parse($this->plan_expires)->lt(now())) {
 | 
			
		||||
        if ($this->plan_expires && Carbon::parse($this->plan_expires)->lt(now()->subHours(12))) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -526,12 +527,12 @@ class Account extends BaseModel
 | 
			
		||||
 | 
			
		||||
    public function emailQuotaExceeded() :bool
 | 
			
		||||
    {
 | 
			
		||||
        if (is_null(Cache::get($this->key))) {
 | 
			
		||||
        if (is_null(Cache::get("email_quota".$this->key))) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            if (Cache::get($this->key) > $this->getDailyEmailLimit()) {
 | 
			
		||||
            if (Cache::get("email_quota".$this->key) > $this->getDailyEmailLimit()) {
 | 
			
		||||
                if (is_null(Cache::get("throttle_notified:{$this->key}"))) {
 | 
			
		||||
                    App::forgetInstance('translator');
 | 
			
		||||
                    $t = app('translator');
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										19
									
								
								app/Models/License.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								app/Models/License.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
			
		||||
<?php
 | 
			
		||||
/**
 | 
			
		||||
 * Invoice Ninja (https://invoiceninja.com).
 | 
			
		||||
 *
 | 
			
		||||
 * @link https://github.com/invoiceninja/invoiceninja source repository
 | 
			
		||||
 *
 | 
			
		||||
 * @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
 | 
			
		||||
 *
 | 
			
		||||
 * @license https://www.elastic.co/licensing/elastic-license
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace App\Models;
 | 
			
		||||
 | 
			
		||||
use Illuminate\Database\Eloquent\SoftDeletes;
 | 
			
		||||
 | 
			
		||||
class License extends StaticModel
 | 
			
		||||
{
 | 
			
		||||
    use SoftDeletes;
 | 
			
		||||
}
 | 
			
		||||
@ -182,8 +182,8 @@ class User extends Authenticatable implements MustVerifyEmail
 | 
			
		||||
        'accepted_terms_version',
 | 
			
		||||
        'oauth_user_id',
 | 
			
		||||
        'oauth_provider_id',
 | 
			
		||||
        'oauth_user_token',
 | 
			
		||||
        'oauth_user_refresh_token',
 | 
			
		||||
        // 'oauth_user_token',
 | 
			
		||||
        // 'oauth_user_refresh_token',
 | 
			
		||||
        'custom_value1',
 | 
			
		||||
        'custom_value2',
 | 
			
		||||
        'custom_value3',
 | 
			
		||||
 | 
			
		||||
@ -356,6 +356,20 @@ class ACH
 | 
			
		||||
        $response = json_decode($request->gateway_response);
 | 
			
		||||
        $bank_account_response = json_decode($request->bank_account_response);
 | 
			
		||||
 | 
			
		||||
        if($response->status == 'requires_source_action' && $response->next_action->type == 'verify_with_microdeposits')
 | 
			
		||||
        {
 | 
			
		||||
            $method = $bank_account_response->payment_method->us_bank_account;
 | 
			
		||||
            $method = $bank_account_response->payment_method->us_bank_account;
 | 
			
		||||
            $method->id = $response->payment_method;
 | 
			
		||||
            $method->state = 'unauthorized';
 | 
			
		||||
            $method->next_action = $response->next_action->verify_with_microdeposits->hosted_verification_url;
 | 
			
		||||
 | 
			
		||||
            $customer = $this->stripe->getCustomer($request->customer);
 | 
			
		||||
            $cgt = $this->storePaymentMethod($method, GatewayType::BANK_TRANSFER, $customer);
 | 
			
		||||
 | 
			
		||||
            return redirect()->route('client.payment_methods.show', ['payment_method' => $cgt->hashed_id]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $method = $bank_account_response->payment_method->us_bank_account;
 | 
			
		||||
        $method->id = $response->payment_method;
 | 
			
		||||
        $method->state = 'authorized';
 | 
			
		||||
@ -547,6 +561,10 @@ class ACH
 | 
			
		||||
            $payment_meta->type = GatewayType::BANK_TRANSFER;
 | 
			
		||||
            $payment_meta->state = $state;
 | 
			
		||||
 | 
			
		||||
            if(property_exists($method, 'next_action')) {
 | 
			
		||||
                $payment_meta->next_action = $method->next_action;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $data = [
 | 
			
		||||
                'payment_meta' => $payment_meta,
 | 
			
		||||
                'token' => $method->id,
 | 
			
		||||
 | 
			
		||||
@ -78,8 +78,19 @@ class PaymentIntentProcessingWebhook implements ShouldQueue
 | 
			
		||||
    
 | 
			
		||||
                $this->payment_completed = true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
            if(isset($transaction['payment_method']))
 | 
			
		||||
            {
 | 
			
		||||
                $cgt = ClientGatewayToken::where('token', $transaction['payment_method'])->first();
 | 
			
		||||
 | 
			
		||||
                if($cgt && $cgt->meta?->state == 'unauthorized'){
 | 
			
		||||
                    $meta = $cgt->meta;
 | 
			
		||||
                    $meta->state = 'authorized';
 | 
			
		||||
                    $cgt->meta = $meta;
 | 
			
		||||
                    $cgt->save();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($this->payment_completed) {
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
@ -90,15 +90,21 @@ class UpdatePaymentMethods
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        foreach ($bank_methods->data as $method) {
 | 
			
		||||
            $token_exists = ClientGatewayToken::where([
 | 
			
		||||
            $token = ClientGatewayToken::where([
 | 
			
		||||
                'gateway_customer_reference' => $customer->id,
 | 
			
		||||
                'token' => $method->id,
 | 
			
		||||
                'client_id' => $client->id,
 | 
			
		||||
                'company_id' => $client->company_id,
 | 
			
		||||
            ])->exists();
 | 
			
		||||
            ])->first();
 | 
			
		||||
 | 
			
		||||
            /* Already exists return */
 | 
			
		||||
            if ($token_exists) {
 | 
			
		||||
            if ($token) {
 | 
			
		||||
            
 | 
			
		||||
                $meta = $token->meta;
 | 
			
		||||
                $meta->state = 'authorized';
 | 
			
		||||
                $token->meta = $meta;
 | 
			
		||||
                $token->save();
 | 
			
		||||
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -11,16 +11,20 @@
 | 
			
		||||
 | 
			
		||||
namespace App\Providers;
 | 
			
		||||
 | 
			
		||||
use App\Utils\Ninja;
 | 
			
		||||
use App\Models\Scheduler;
 | 
			
		||||
use App\Utils\Traits\MakesHash;
 | 
			
		||||
use Illuminate\Support\Facades\Route;
 | 
			
		||||
use Illuminate\Cache\RateLimiting\Limit;
 | 
			
		||||
use Illuminate\Support\Facades\RateLimiter;
 | 
			
		||||
use Illuminate\Database\Eloquent\ModelNotFoundException as ModelNotFoundException;
 | 
			
		||||
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
 | 
			
		||||
use Illuminate\Support\Facades\Route;
 | 
			
		||||
 | 
			
		||||
class RouteServiceProvider extends ServiceProvider
 | 
			
		||||
{
 | 
			
		||||
    use MakesHash;
 | 
			
		||||
 | 
			
		||||
    private int $default_rate_limit = 5000;
 | 
			
		||||
    /**
 | 
			
		||||
     * Define your route model bindings, pattern filters, etc.
 | 
			
		||||
     *
 | 
			
		||||
@ -40,6 +44,37 @@ class RouteServiceProvider extends ServiceProvider
 | 
			
		||||
                ->company()
 | 
			
		||||
                ->where('id', $this->decodePrimaryKey($value))->firstOrFail();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        RateLimiter::for('login', function () {
 | 
			
		||||
 | 
			
		||||
            if (Ninja::isSelfHost()) {
 | 
			
		||||
                return Limit::none();
 | 
			
		||||
            }else {
 | 
			
		||||
                return Limit::perMinute(50);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        RateLimiter::for('api', function () {
 | 
			
		||||
 | 
			
		||||
            if (Ninja::isSelfHost()) {
 | 
			
		||||
                return Limit::none();
 | 
			
		||||
            }else {
 | 
			
		||||
                return Limit::perMinute(300);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        RateLimiter::for('refresh', function () {
 | 
			
		||||
 | 
			
		||||
            if (Ninja::isSelfHost()) {
 | 
			
		||||
                return Limit::none();
 | 
			
		||||
            }else {
 | 
			
		||||
                return Limit::perMinute(200);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 | 
			
		||||
@ -134,7 +134,7 @@ class EmailDefaults
 | 
			
		||||
            return $this;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->email->email_object->from = new Address($this->email->company->owner()->email, $this->email->company->owner()->name());
 | 
			
		||||
        $this->email->email_object->from = new Address(config('mail.from.address'), config('mail.from.name'));
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -11,38 +11,42 @@
 | 
			
		||||
 | 
			
		||||
namespace App\Services\Subscription;
 | 
			
		||||
 | 
			
		||||
use App\DataMapper\InvoiceItem;
 | 
			
		||||
use App\Factory\CreditFactory;
 | 
			
		||||
use App\Factory\InvoiceFactory;
 | 
			
		||||
use App\Factory\PaymentFactory;
 | 
			
		||||
use App\Factory\RecurringInvoiceFactory;
 | 
			
		||||
use App\Jobs\Mail\NinjaMailer;
 | 
			
		||||
use App\Jobs\Mail\NinjaMailerJob;
 | 
			
		||||
use App\Jobs\Mail\NinjaMailerObject;
 | 
			
		||||
use App\Jobs\Util\SystemLogger;
 | 
			
		||||
use App\Libraries\MultiDB;
 | 
			
		||||
use App\Mail\RecurringInvoice\ClientContactRequestCancellationObject;
 | 
			
		||||
use Carbon\Carbon;
 | 
			
		||||
use App\Models\Client;
 | 
			
		||||
use App\Models\ClientContact;
 | 
			
		||||
use App\Models\Credit;
 | 
			
		||||
use App\Models\Invoice;
 | 
			
		||||
use App\Models\License;
 | 
			
		||||
use App\Models\Product;
 | 
			
		||||
use App\Models\SystemLog;
 | 
			
		||||
use App\Libraries\MultiDB;
 | 
			
		||||
use App\Models\PaymentHash;
 | 
			
		||||
use App\Models\PaymentType;
 | 
			
		||||
use App\Models\Product;
 | 
			
		||||
use App\Models\RecurringInvoice;
 | 
			
		||||
use Illuminate\Support\Str;
 | 
			
		||||
use App\Models\Subscription;
 | 
			
		||||
use App\Models\SystemLog;
 | 
			
		||||
use App\Models\ClientContact;
 | 
			
		||||
use App\Services\Email\Email;
 | 
			
		||||
use App\Factory\CreditFactory;
 | 
			
		||||
use App\Jobs\Mail\NinjaMailer;
 | 
			
		||||
use App\DataMapper\InvoiceItem;
 | 
			
		||||
use App\Factory\InvoiceFactory;
 | 
			
		||||
use App\Factory\PaymentFactory;
 | 
			
		||||
use App\Jobs\Util\SystemLogger;
 | 
			
		||||
use App\Utils\Traits\MakesHash;
 | 
			
		||||
use App\Models\RecurringInvoice;
 | 
			
		||||
use App\Jobs\Mail\NinjaMailerJob;
 | 
			
		||||
use App\Services\Email\EmailObject;
 | 
			
		||||
use App\Jobs\Mail\NinjaMailerObject;
 | 
			
		||||
use App\Utils\Traits\CleanLineItems;
 | 
			
		||||
use App\Repositories\CreditRepository;
 | 
			
		||||
use App\Repositories\InvoiceRepository;
 | 
			
		||||
use App\Repositories\PaymentRepository;
 | 
			
		||||
use App\Repositories\RecurringInvoiceRepository;
 | 
			
		||||
use App\Repositories\SubscriptionRepository;
 | 
			
		||||
use App\Utils\Traits\CleanLineItems;
 | 
			
		||||
use App\Utils\Traits\MakesHash;
 | 
			
		||||
use App\Utils\Traits\Notifications\UserNotifies;
 | 
			
		||||
use App\Factory\RecurringInvoiceFactory;
 | 
			
		||||
use App\Utils\Traits\SubscriptionHooker;
 | 
			
		||||
use Carbon\Carbon;
 | 
			
		||||
use App\Repositories\SubscriptionRepository;
 | 
			
		||||
use App\Repositories\RecurringInvoiceRepository;
 | 
			
		||||
use App\Utils\Traits\Notifications\UserNotifies;
 | 
			
		||||
use Illuminate\Contracts\Container\BindingResolutionException;
 | 
			
		||||
use App\Mail\RecurringInvoice\ClientContactRequestCancellationObject;
 | 
			
		||||
 | 
			
		||||
class SubscriptionService
 | 
			
		||||
{
 | 
			
		||||
@ -54,6 +58,8 @@ class SubscriptionService
 | 
			
		||||
    /** @var subscription */
 | 
			
		||||
    private $subscription;
 | 
			
		||||
 | 
			
		||||
    private const WHITE_LABEL = 4316;
 | 
			
		||||
 | 
			
		||||
    private float $credit_payments = 0;
 | 
			
		||||
 | 
			
		||||
    public function __construct(Subscription $subscription)
 | 
			
		||||
@ -75,6 +81,11 @@ class SubscriptionService
 | 
			
		||||
            return $this->handlePlanChange($payment_hash);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($payment_hash->data->billing_context->context == 'whitelabel') {
 | 
			
		||||
            return $this->handleWhiteLabelPurchase($payment_hash);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        // if we have a recurring product - then generate a recurring invoice
 | 
			
		||||
        if (strlen($this->subscription->recurring_product_ids) >=1) {
 | 
			
		||||
            if (isset($payment_hash->data->billing_context->bundle)) {
 | 
			
		||||
@ -153,6 +164,45 @@ class SubscriptionService
 | 
			
		||||
        return $response;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function handleWhiteLabelPurchase(PaymentHash $payment_hash): bool
 | 
			
		||||
    {
 | 
			
		||||
        //send license to the user.
 | 
			
		||||
        $invoice = $payment_hash->fee_invoice;
 | 
			
		||||
        $license_key = Str::uuid()->toString();
 | 
			
		||||
        $invoice->public_notes = $license_key;
 | 
			
		||||
        $invoice->save();
 | 
			
		||||
        $invoice->service()->touchPdf();
 | 
			
		||||
 | 
			
		||||
        $contact = $invoice->client->contacts()->whereNotNull('email')->first();
 | 
			
		||||
 | 
			
		||||
        $license = new License;
 | 
			
		||||
        $license->license_key = $license_key;
 | 
			
		||||
        $license->email = $contact ? $contact->email : ' ';
 | 
			
		||||
        $license->first_name = $contact ? $contact->first_name : ' ';
 | 
			
		||||
        $license->last_name = $contact ? $contact->last_name : ' ';
 | 
			
		||||
        $license->is_claimed = 1;
 | 
			
		||||
        $license->transaction_reference = $payment_hash?->payment?->transaction_reference ?: ' ';
 | 
			
		||||
        $license->product_id = self::WHITE_LABEL;
 | 
			
		||||
 | 
			
		||||
        $license->save();
 | 
			
		||||
 | 
			
		||||
        $email_object = new EmailObject;
 | 
			
		||||
        $email_object->to = $contact->email;
 | 
			
		||||
        $email_object->subject = ctrans('texts.white_label_link') . " " .ctrans('texts.payment_subject');
 | 
			
		||||
        $email_object->body = ctrans('texts.white_label_body',['license_key' => $license_key]);
 | 
			
		||||
        $email_object->client_id = $invoice->client_id;
 | 
			
		||||
        $email_object->client_contact_id = $contact->id;
 | 
			
		||||
        $email_object->invitation_key = $invoice->invitations()->first()->invitation_key;
 | 
			
		||||
        $email_object->entity_id = $invoice->id;
 | 
			
		||||
        $email_object->entity_class = Invoice::class;
 | 
			
		||||
        $email_object->user_id = $invoice->user_id;
 | 
			
		||||
 | 
			
		||||
        Email::dispatch($email_object, $invoice->company);
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Starts the process to create a trial
 | 
			
		||||
        - we create a recurring invoice, which is has its next_send_date as now() + trial_duration
 | 
			
		||||
        - we then hit the client API end point to advise the trial payload
 | 
			
		||||
 | 
			
		||||
@ -14,8 +14,8 @@ return [
 | 
			
		||||
    'require_https' => env('REQUIRE_HTTPS', true),
 | 
			
		||||
    'app_url' => rtrim(env('APP_URL', ''), '/'),
 | 
			
		||||
    'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
 | 
			
		||||
    'app_version' => '5.5.84',
 | 
			
		||||
    'app_tag' => '5.5.84',
 | 
			
		||||
    'app_version' => '5.5.85',
 | 
			
		||||
    'app_tag' => '5.5.85',
 | 
			
		||||
    'minimum_client_version' => '5.0.16',
 | 
			
		||||
    'terms_version' => '1.0.1',
 | 
			
		||||
    'api_secret' => env('API_SECRET', ''),
 | 
			
		||||
 | 
			
		||||
@ -5014,6 +5014,8 @@ $LANG = array(
 | 
			
		||||
    'no_assigned_tasks' => 'No billable tasks for this project',
 | 
			
		||||
    'authorization_failure' => 'Insufficient permissions to perform this action',
 | 
			
		||||
    'authorization_sms_failure' => 'Please verify your account to send emails.',
 | 
			
		||||
    'white_label_body' => 'Thank you for purchasing a white label license. Your license key is :license_key.',
 | 
			
		||||
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -2,4 +2,4 @@ parameters:
 | 
			
		||||
    level: 2
 | 
			
		||||
    paths:
 | 
			
		||||
        - app
 | 
			
		||||
        - tests
 | 
			
		||||
#        - tests
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
@php
 | 
			
		||||
    $primary_color = isset($settings) ? $settings->primary_color : '#4caf50';
 | 
			
		||||
    $email_alignment = isset($settings) ? $settings->email_alignment : 'center';
 | 
			
		||||
    $email_alignment = isset($settings) && $settings?->email_alignment ? $settings->email_alignment : 'center';
 | 
			
		||||
@endphp
 | 
			
		||||
 | 
			
		||||
<!DOCTYPE html
 | 
			
		||||
 | 
			
		||||
@ -213,6 +213,10 @@
 | 
			
		||||
                  errors.textContent = "You will receive an email with details on how to verify your bank account and process payment.";
 | 
			
		||||
                  errors.hidden = false;
 | 
			
		||||
                  document.getElementById('new-bank').style.visibility = 'hidden'
 | 
			
		||||
 | 
			
		||||
                    let gateway_response = document.getElementById('gateway_response');
 | 
			
		||||
                    gateway_response.value = JSON.stringify(paymentIntent);
 | 
			
		||||
                    document.getElementById('server-response').submit();
 | 
			
		||||
            }
 | 
			
		||||
          });
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
@ -100,9 +100,15 @@
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="mt-5 sm:mt-0 sm:ml-6 sm:flex-shrink-0 sm:flex sm:items-center">
 | 
			
		||||
                            <div class="inline-flex rounded-md shadow-sm" x-data="{ open: false }">
 | 
			
		||||
                            @if (substr($payment_method->token, 0, 2) === 'pm') 
 | 
			
		||||
                                <a href="{{ $payment_method->meta?->next_action }}" class="button button-primary bg-primary">
 | 
			
		||||
                                    {{ ctrans('texts.complete_verification') }}
 | 
			
		||||
                                </a>
 | 
			
		||||
                            @else
 | 
			
		||||
                                <a href="{{ route('client.payment_methods.verification', ['payment_method' => $payment_method->hashed_id, 'method' => \App\Models\GatewayType::BANK_TRANSFER]) }}" class="button button-primary bg-primary">
 | 
			
		||||
                                    {{ ctrans('texts.complete_verification') }}
 | 
			
		||||
                                </a>
 | 
			
		||||
                            @endif
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
 | 
			
		||||
@ -98,17 +98,17 @@ use App\Http\Controllers\WebCronController;
 | 
			
		||||
use App\Http\Controllers\WebhookController;
 | 
			
		||||
use Illuminate\Support\Facades\Route;
 | 
			
		||||
 | 
			
		||||
Route::group(['middleware' => ['throttle:300,1', 'api_secret_check']], function () {
 | 
			
		||||
Route::group(['middleware' => ['throttle:api', 'api_secret_check']], function () {
 | 
			
		||||
    Route::post('api/v1/signup', [AccountController::class, 'store'])->name('signup.submit');
 | 
			
		||||
    Route::post('api/v1/oauth_login', [LoginController::class, 'oauthApiLogin']);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
Route::group(['middleware' => ['throttle:50,1','api_secret_check','email_db']], function () {
 | 
			
		||||
    Route::post('api/v1/login', [LoginController::class, 'apiLogin'])->name('login.submit')->middleware('throttle:20,1');
 | 
			
		||||
Route::group(['middleware' => ['throttle:login','api_secret_check','email_db']], function () {
 | 
			
		||||
    Route::post('api/v1/login', [LoginController::class, 'apiLogin'])->name('login.submit');
 | 
			
		||||
    Route::post('api/v1/reset_password', [ForgotPasswordController::class, 'sendResetLinkEmail']);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
Route::group(['middleware' => ['throttle:300,1', 'api_db', 'token_auth', 'locale'], 'prefix' => 'api/v1', 'as' => 'api.'], function () {
 | 
			
		||||
Route::group(['middleware' => ['throttle:api', 'api_db', 'token_auth', 'locale'], 'prefix' => 'api/v1', 'as' => 'api.'], function () {
 | 
			
		||||
    Route::put('accounts/{account}', [AccountController::class, 'update'])->name('account.update');
 | 
			
		||||
    Route::resource('bank_integrations', BankIntegrationController::class); // name = (clients. index / create / show / update / destroy / edit
 | 
			
		||||
    Route::post('bank_integrations/refresh_accounts', [BankIntegrationController::class, 'refreshAccounts'])->name('bank_integrations.refresh_accounts')->middleware('throttle:30,1');
 | 
			
		||||
@ -265,7 +265,7 @@ Route::group(['middleware' => ['throttle:300,1', 'api_db', 'token_auth', 'locale
 | 
			
		||||
    Route::post('recurring_quotes/bulk', [RecurringQuoteController::class, 'bulk'])->name('recurring_quotes.bulk');
 | 
			
		||||
    Route::put('recurring_quotes/{recurring_quote}/upload', [RecurringQuoteController::class, 'upload']);
 | 
			
		||||
 | 
			
		||||
    Route::post('refresh', [LoginController::class, 'refresh'])->middleware('throttle:300,2');
 | 
			
		||||
    Route::post('refresh', [LoginController::class, 'refresh'])->middleware('throttle:refresh');
 | 
			
		||||
 | 
			
		||||
    Route::post('reports/clients', ClientReportController::class);
 | 
			
		||||
    Route::post('reports/contacts', ClientContactReportController::class);
 | 
			
		||||
@ -364,7 +364,7 @@ Route::match(['get', 'post'], 'payment_notification_webhook/{company_key}/{compa
 | 
			
		||||
    ->name('payment_notification_webhook');
 | 
			
		||||
 | 
			
		||||
Route::post('api/v1/postmark_webhook', [PostMarkController::class, 'webhook'])->middleware('throttle:1000,1');
 | 
			
		||||
Route::get('token_hash_router', [OneTimeTokenController::class, 'router'])->middleware('throttle:100,1');
 | 
			
		||||
Route::get('token_hash_router', [OneTimeTokenController::class, 'router'])->middleware('throttle:500,1');
 | 
			
		||||
Route::get('webcron', [WebCronController::class, 'index'])->middleware('throttle:100,1');
 | 
			
		||||
Route::post('api/v1/get_migration_account', [HostedMigrationController::class, 'getAccount'])->middleware('guest')->middleware('throttle:100,1');
 | 
			
		||||
Route::post('api/v1/confirm_forwarding', [HostedMigrationController::class, 'confirmForwarding'])->middleware('guest')->middleware('throttle:100,1');
 | 
			
		||||
 | 
			
		||||
@ -78,16 +78,16 @@ class AccountEmailQuotaTest extends TestCase
 | 
			
		||||
        $account->save();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        Cache::put($account->key, 3000);
 | 
			
		||||
        Cache::put("email_quota".$account->key, 3000);
 | 
			
		||||
 | 
			
		||||
        $this->assertFalse($account->isPaid());
 | 
			
		||||
        $this->assertTrue(Ninja::isNinja());
 | 
			
		||||
        $this->assertEquals(20, $account->getDailyEmailLimit());
 | 
			
		||||
 | 
			
		||||
        $this->assertEquals(3000, Cache::get($account->key));
 | 
			
		||||
        $this->assertEquals(3000, Cache::get("email_quota".$account->key));
 | 
			
		||||
        $this->assertTrue($account->emailQuotaExceeded());
 | 
			
		||||
 | 
			
		||||
        Cache::forget('123ifyouknowwhatimean');
 | 
			
		||||
        Cache::forget("email_quota".'123ifyouknowwhatimean');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testQuotaValidRule()
 | 
			
		||||
@ -104,11 +104,11 @@ class AccountEmailQuotaTest extends TestCase
 | 
			
		||||
        $account->num_users = 3;
 | 
			
		||||
        $account->save();
 | 
			
		||||
 | 
			
		||||
        Cache::increment($account->key);
 | 
			
		||||
        Cache::increment("email_quota".$account->key);
 | 
			
		||||
 | 
			
		||||
        $this->assertFalse($account->emailQuotaExceeded());
 | 
			
		||||
 | 
			
		||||
        Cache::forget('123ifyouknowwhatimean');
 | 
			
		||||
        Cache::forget("email_quota".'123ifyouknowwhatimean');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testEmailSentCount()
 | 
			
		||||
@ -132,6 +132,6 @@ class AccountEmailQuotaTest extends TestCase
 | 
			
		||||
 | 
			
		||||
        $this->assertEquals(3000, $count);
 | 
			
		||||
 | 
			
		||||
        Cache::forget('123ifyouknowwhatimean');
 | 
			
		||||
        Cache::forget("email_quota".'123ifyouknowwhatimean');
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user