mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04: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')
|
->with('contact.client')
|
||||||
->firstOrFail();
|
->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]);
|
return $this->render('generic.not_available', ['account' => $invitation->company->account, 'company' => $invitation->company]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ class UpdateCompanyRequest extends Request
|
|||||||
* are saveable
|
* are saveable
|
||||||
*
|
*
|
||||||
* @param object $settings
|
* @param object $settings
|
||||||
* @return stdClass $settings
|
* @return \stdClass $settings
|
||||||
*/
|
*/
|
||||||
private function filterSaveableSettings($settings)
|
private function filterSaveableSettings($settings)
|
||||||
{
|
{
|
||||||
|
@ -73,6 +73,11 @@ class UpdateUserRequest extends Request
|
|||||||
$input['oauth_user_id'] = '';
|
$input['oauth_user_id'] = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (array_key_exists('oauth_user_token', $input) && $input['oauth_user_token'] == '***') {
|
||||||
|
unset($input['oauth_user_token']);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
$this->replace($input);
|
$this->replace($input);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
18
app/Http/Requests/Vendor/StoreVendorRequest.php
vendored
18
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.
|
* Determine if the user is authorized to make this request.
|
||||||
*
|
*
|
||||||
* @return bool
|
|
||||||
* @method static \Illuminate\Contracts\Auth\Authenticatable|null user()
|
|
||||||
*/
|
*/
|
||||||
public function authorize() : bool
|
public function authorize() : bool
|
||||||
{
|
{
|
||||||
/** @var \App\User|null $user */
|
return auth()->user()->can('create', Vendor::class);
|
||||||
$user = auth()->user();
|
|
||||||
|
|
||||||
return $user->can('create', Vendor::class);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function rules()
|
public function rules()
|
||||||
{
|
{
|
||||||
/** @var \App\User|null $user */
|
$rules = [];
|
||||||
$user = auth()->user();
|
|
||||||
|
|
||||||
$rules['contacts.*.email'] = 'bail|nullable|distinct|sometimes|email';
|
$rules['contacts.*.email'] = 'bail|nullable|distinct|sometimes|email';
|
||||||
|
|
||||||
if (isset($this->number)) {
|
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';
|
$rules['currency_id'] = 'bail|required|exists:currencies,id';
|
||||||
@ -63,13 +57,11 @@ class StoreVendorRequest extends Request
|
|||||||
|
|
||||||
public function prepareForValidation()
|
public function prepareForValidation()
|
||||||
{
|
{
|
||||||
/** @var \App\User|null $user */
|
|
||||||
$user = auth()->user();
|
|
||||||
|
|
||||||
$input = $this->all();
|
$input = $this->all();
|
||||||
|
|
||||||
if (!array_key_exists('currency_id', $input) || empty($input['currency_id'])) {
|
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);
|
$input = $this->decodePrimaryKeys($input);
|
||||||
|
@ -24,11 +24,8 @@ class UpdateReminders implements ShouldQueue
|
|||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
public Company $company;
|
public function __construct(public Company $company)
|
||||||
|
|
||||||
public function __construct(Company $company)
|
|
||||||
{
|
{
|
||||||
$this->company = $company;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -22,7 +22,7 @@ use Illuminate\Queue\InteractsWithQueue;
|
|||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Turbo124\Beacon\Facades\LightLogs;
|
use Turbo124\Beacon\Facades\LightLogs;
|
||||||
|
use Illuminate\Support\Facades\Redis;
|
||||||
class AdjustEmailQuota implements ShouldQueue
|
class AdjustEmailQuota implements ShouldQueue
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
@ -61,18 +61,47 @@ class AdjustEmailQuota implements ShouldQueue
|
|||||||
Account::query()->cursor()->each(function ($account) {
|
Account::query()->cursor()->each(function ($account) {
|
||||||
nlog("resetting email quota for {$account->key}");
|
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) {
|
if ($email_count > 0) {
|
||||||
try {
|
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) {
|
} catch(\Exception $e) {
|
||||||
nlog($e->getMessage());
|
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.
|
* Create a new job instance.
|
||||||
*
|
*
|
||||||
* @param int invoice_id
|
|
||||||
* @param string $db
|
|
||||||
*/
|
*/
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
|
@ -339,7 +339,8 @@ class Account extends BaseModel
|
|||||||
return false;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -352,7 +353,7 @@ class Account extends BaseModel
|
|||||||
return false;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -526,12 +527,12 @@ class Account extends BaseModel
|
|||||||
|
|
||||||
public function emailQuotaExceeded() :bool
|
public function emailQuotaExceeded() :bool
|
||||||
{
|
{
|
||||||
if (is_null(Cache::get($this->key))) {
|
if (is_null(Cache::get("email_quota".$this->key))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
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}"))) {
|
if (is_null(Cache::get("throttle_notified:{$this->key}"))) {
|
||||||
App::forgetInstance('translator');
|
App::forgetInstance('translator');
|
||||||
$t = app('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',
|
'accepted_terms_version',
|
||||||
'oauth_user_id',
|
'oauth_user_id',
|
||||||
'oauth_provider_id',
|
'oauth_provider_id',
|
||||||
'oauth_user_token',
|
// 'oauth_user_token',
|
||||||
'oauth_user_refresh_token',
|
// 'oauth_user_refresh_token',
|
||||||
'custom_value1',
|
'custom_value1',
|
||||||
'custom_value2',
|
'custom_value2',
|
||||||
'custom_value3',
|
'custom_value3',
|
||||||
|
@ -356,6 +356,20 @@ class ACH
|
|||||||
$response = json_decode($request->gateway_response);
|
$response = json_decode($request->gateway_response);
|
||||||
$bank_account_response = json_decode($request->bank_account_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 = $bank_account_response->payment_method->us_bank_account;
|
||||||
$method->id = $response->payment_method;
|
$method->id = $response->payment_method;
|
||||||
$method->state = 'authorized';
|
$method->state = 'authorized';
|
||||||
@ -547,6 +561,10 @@ class ACH
|
|||||||
$payment_meta->type = GatewayType::BANK_TRANSFER;
|
$payment_meta->type = GatewayType::BANK_TRANSFER;
|
||||||
$payment_meta->state = $state;
|
$payment_meta->state = $state;
|
||||||
|
|
||||||
|
if(property_exists($method, 'next_action')) {
|
||||||
|
$payment_meta->next_action = $method->next_action;
|
||||||
|
}
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'payment_meta' => $payment_meta,
|
'payment_meta' => $payment_meta,
|
||||||
'token' => $method->id,
|
'token' => $method->id,
|
||||||
|
@ -78,8 +78,19 @@ class PaymentIntentProcessingWebhook implements ShouldQueue
|
|||||||
|
|
||||||
$this->payment_completed = true;
|
$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) {
|
if ($this->payment_completed) {
|
||||||
return;
|
return;
|
||||||
|
@ -90,15 +90,21 @@ class UpdatePaymentMethods
|
|||||||
);
|
);
|
||||||
|
|
||||||
foreach ($bank_methods->data as $method) {
|
foreach ($bank_methods->data as $method) {
|
||||||
$token_exists = ClientGatewayToken::where([
|
$token = ClientGatewayToken::where([
|
||||||
'gateway_customer_reference' => $customer->id,
|
'gateway_customer_reference' => $customer->id,
|
||||||
'token' => $method->id,
|
'token' => $method->id,
|
||||||
'client_id' => $client->id,
|
'client_id' => $client->id,
|
||||||
'company_id' => $client->company_id,
|
'company_id' => $client->company_id,
|
||||||
])->exists();
|
])->first();
|
||||||
|
|
||||||
/* Already exists return */
|
/* Already exists return */
|
||||||
if ($token_exists) {
|
if ($token) {
|
||||||
|
|
||||||
|
$meta = $token->meta;
|
||||||
|
$meta->state = 'authorized';
|
||||||
|
$token->meta = $meta;
|
||||||
|
$token->save();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,16 +11,20 @@
|
|||||||
|
|
||||||
namespace App\Providers;
|
namespace App\Providers;
|
||||||
|
|
||||||
|
use App\Utils\Ninja;
|
||||||
use App\Models\Scheduler;
|
use App\Models\Scheduler;
|
||||||
use App\Utils\Traits\MakesHash;
|
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\Database\Eloquent\ModelNotFoundException as ModelNotFoundException;
|
||||||
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
|
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
|
||||||
use Illuminate\Support\Facades\Route;
|
|
||||||
|
|
||||||
class RouteServiceProvider extends ServiceProvider
|
class RouteServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
use MakesHash;
|
use MakesHash;
|
||||||
|
|
||||||
|
private int $default_rate_limit = 5000;
|
||||||
/**
|
/**
|
||||||
* Define your route model bindings, pattern filters, etc.
|
* Define your route model bindings, pattern filters, etc.
|
||||||
*
|
*
|
||||||
@ -40,6 +44,37 @@ class RouteServiceProvider extends ServiceProvider
|
|||||||
->company()
|
->company()
|
||||||
->where('id', $this->decodePrimaryKey($value))->firstOrFail();
|
->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;
|
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;
|
return $this;
|
||||||
}
|
}
|
||||||
|
@ -11,38 +11,42 @@
|
|||||||
|
|
||||||
namespace App\Services\Subscription;
|
namespace App\Services\Subscription;
|
||||||
|
|
||||||
use App\DataMapper\InvoiceItem;
|
use Carbon\Carbon;
|
||||||
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 App\Models\Client;
|
use App\Models\Client;
|
||||||
use App\Models\ClientContact;
|
|
||||||
use App\Models\Credit;
|
use App\Models\Credit;
|
||||||
use App\Models\Invoice;
|
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\PaymentHash;
|
||||||
use App\Models\PaymentType;
|
use App\Models\PaymentType;
|
||||||
use App\Models\Product;
|
use Illuminate\Support\Str;
|
||||||
use App\Models\RecurringInvoice;
|
|
||||||
use App\Models\Subscription;
|
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\CreditRepository;
|
||||||
use App\Repositories\InvoiceRepository;
|
use App\Repositories\InvoiceRepository;
|
||||||
use App\Repositories\PaymentRepository;
|
use App\Repositories\PaymentRepository;
|
||||||
use App\Repositories\RecurringInvoiceRepository;
|
use App\Factory\RecurringInvoiceFactory;
|
||||||
use App\Repositories\SubscriptionRepository;
|
|
||||||
use App\Utils\Traits\CleanLineItems;
|
|
||||||
use App\Utils\Traits\MakesHash;
|
|
||||||
use App\Utils\Traits\Notifications\UserNotifies;
|
|
||||||
use App\Utils\Traits\SubscriptionHooker;
|
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 Illuminate\Contracts\Container\BindingResolutionException;
|
||||||
|
use App\Mail\RecurringInvoice\ClientContactRequestCancellationObject;
|
||||||
|
|
||||||
class SubscriptionService
|
class SubscriptionService
|
||||||
{
|
{
|
||||||
@ -54,6 +58,8 @@ class SubscriptionService
|
|||||||
/** @var subscription */
|
/** @var subscription */
|
||||||
private $subscription;
|
private $subscription;
|
||||||
|
|
||||||
|
private const WHITE_LABEL = 4316;
|
||||||
|
|
||||||
private float $credit_payments = 0;
|
private float $credit_payments = 0;
|
||||||
|
|
||||||
public function __construct(Subscription $subscription)
|
public function __construct(Subscription $subscription)
|
||||||
@ -75,6 +81,11 @@ class SubscriptionService
|
|||||||
return $this->handlePlanChange($payment_hash);
|
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 we have a recurring product - then generate a recurring invoice
|
||||||
if (strlen($this->subscription->recurring_product_ids) >=1) {
|
if (strlen($this->subscription->recurring_product_ids) >=1) {
|
||||||
if (isset($payment_hash->data->billing_context->bundle)) {
|
if (isset($payment_hash->data->billing_context->bundle)) {
|
||||||
@ -153,6 +164,45 @@ class SubscriptionService
|
|||||||
return $response;
|
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
|
/* Starts the process to create a trial
|
||||||
- we create a recurring invoice, which is has its next_send_date as now() + trial_duration
|
- 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
|
- we then hit the client API end point to advise the trial payload
|
||||||
|
@ -14,8 +14,8 @@ return [
|
|||||||
'require_https' => env('REQUIRE_HTTPS', true),
|
'require_https' => env('REQUIRE_HTTPS', true),
|
||||||
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
||||||
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
|
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
|
||||||
'app_version' => '5.5.84',
|
'app_version' => '5.5.85',
|
||||||
'app_tag' => '5.5.84',
|
'app_tag' => '5.5.85',
|
||||||
'minimum_client_version' => '5.0.16',
|
'minimum_client_version' => '5.0.16',
|
||||||
'terms_version' => '1.0.1',
|
'terms_version' => '1.0.1',
|
||||||
'api_secret' => env('API_SECRET', ''),
|
'api_secret' => env('API_SECRET', ''),
|
||||||
|
@ -5014,6 +5014,8 @@ $LANG = array(
|
|||||||
'no_assigned_tasks' => 'No billable tasks for this project',
|
'no_assigned_tasks' => 'No billable tasks for this project',
|
||||||
'authorization_failure' => 'Insufficient permissions to perform this action',
|
'authorization_failure' => 'Insufficient permissions to perform this action',
|
||||||
'authorization_sms_failure' => 'Please verify your account to send emails.',
|
'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
|
level: 2
|
||||||
paths:
|
paths:
|
||||||
- app
|
- app
|
||||||
- tests
|
# - tests
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
@php
|
@php
|
||||||
$primary_color = isset($settings) ? $settings->primary_color : '#4caf50';
|
$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
|
@endphp
|
||||||
|
|
||||||
<!DOCTYPE html
|
<!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.textContent = "You will receive an email with details on how to verify your bank account and process payment.";
|
||||||
errors.hidden = false;
|
errors.hidden = false;
|
||||||
document.getElementById('new-bank').style.visibility = 'hidden'
|
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>
|
||||||
<div class="mt-5 sm:mt-0 sm:ml-6 sm:flex-shrink-0 sm:flex sm:items-center">
|
<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 }">
|
<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">
|
<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') }}
|
{{ ctrans('texts.complete_verification') }}
|
||||||
</a>
|
</a>
|
||||||
|
@endif
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -98,17 +98,17 @@ use App\Http\Controllers\WebCronController;
|
|||||||
use App\Http\Controllers\WebhookController;
|
use App\Http\Controllers\WebhookController;
|
||||||
use Illuminate\Support\Facades\Route;
|
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/signup', [AccountController::class, 'store'])->name('signup.submit');
|
||||||
Route::post('api/v1/oauth_login', [LoginController::class, 'oauthApiLogin']);
|
Route::post('api/v1/oauth_login', [LoginController::class, 'oauthApiLogin']);
|
||||||
});
|
});
|
||||||
|
|
||||||
Route::group(['middleware' => ['throttle:50,1','api_secret_check','email_db']], function () {
|
Route::group(['middleware' => ['throttle:login','api_secret_check','email_db']], function () {
|
||||||
Route::post('api/v1/login', [LoginController::class, 'apiLogin'])->name('login.submit')->middleware('throttle:20,1');
|
Route::post('api/v1/login', [LoginController::class, 'apiLogin'])->name('login.submit');
|
||||||
Route::post('api/v1/reset_password', [ForgotPasswordController::class, 'sendResetLinkEmail']);
|
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::put('accounts/{account}', [AccountController::class, 'update'])->name('account.update');
|
||||||
Route::resource('bank_integrations', BankIntegrationController::class); // name = (clients. index / create / show / update / destroy / edit
|
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');
|
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::post('recurring_quotes/bulk', [RecurringQuoteController::class, 'bulk'])->name('recurring_quotes.bulk');
|
||||||
Route::put('recurring_quotes/{recurring_quote}/upload', [RecurringQuoteController::class, 'upload']);
|
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/clients', ClientReportController::class);
|
||||||
Route::post('reports/contacts', ClientContactReportController::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');
|
->name('payment_notification_webhook');
|
||||||
|
|
||||||
Route::post('api/v1/postmark_webhook', [PostMarkController::class, 'webhook'])->middleware('throttle:1000,1');
|
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::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/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');
|
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();
|
$account->save();
|
||||||
|
|
||||||
|
|
||||||
Cache::put($account->key, 3000);
|
Cache::put("email_quota".$account->key, 3000);
|
||||||
|
|
||||||
$this->assertFalse($account->isPaid());
|
$this->assertFalse($account->isPaid());
|
||||||
$this->assertTrue(Ninja::isNinja());
|
$this->assertTrue(Ninja::isNinja());
|
||||||
$this->assertEquals(20, $account->getDailyEmailLimit());
|
$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());
|
$this->assertTrue($account->emailQuotaExceeded());
|
||||||
|
|
||||||
Cache::forget('123ifyouknowwhatimean');
|
Cache::forget("email_quota".'123ifyouknowwhatimean');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testQuotaValidRule()
|
public function testQuotaValidRule()
|
||||||
@ -104,11 +104,11 @@ class AccountEmailQuotaTest extends TestCase
|
|||||||
$account->num_users = 3;
|
$account->num_users = 3;
|
||||||
$account->save();
|
$account->save();
|
||||||
|
|
||||||
Cache::increment($account->key);
|
Cache::increment("email_quota".$account->key);
|
||||||
|
|
||||||
$this->assertFalse($account->emailQuotaExceeded());
|
$this->assertFalse($account->emailQuotaExceeded());
|
||||||
|
|
||||||
Cache::forget('123ifyouknowwhatimean');
|
Cache::forget("email_quota".'123ifyouknowwhatimean');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testEmailSentCount()
|
public function testEmailSentCount()
|
||||||
@ -132,6 +132,6 @@ class AccountEmailQuotaTest extends TestCase
|
|||||||
|
|
||||||
$this->assertEquals(3000, $count);
|
$this->assertEquals(3000, $count);
|
||||||
|
|
||||||
Cache::forget('123ifyouknowwhatimean');
|
Cache::forget("email_quota".'123ifyouknowwhatimean');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user