diff --git a/app/Console/Commands/BackupUpdate.php b/app/Console/Commands/BackupUpdate.php
index 33b421ea384d..b2a16bff2502 100644
--- a/app/Console/Commands/BackupUpdate.php
+++ b/app/Console/Commands/BackupUpdate.php
@@ -177,7 +177,6 @@ class BackupUpdate extends Command
$doc_bin = $document->getFile();
} catch(\Exception $e) {
nlog("Exception:: BackupUpdate::" . $e->getMessage());
- nlog($e->getMessage());
}
if ($doc_bin) {
diff --git a/app/Export/CSV/BaseExport.php b/app/Export/CSV/BaseExport.php
index 374e7783c3fe..9dbf186b90b1 100644
--- a/app/Export/CSV/BaseExport.php
+++ b/app/Export/CSV/BaseExport.php
@@ -172,6 +172,7 @@ class BaseExport
'tax_rate3' => 'invoice.tax_rate3',
'recurring_invoice' => 'invoice.recurring_id',
'auto_bill' => 'invoice.auto_bill_enabled',
+ 'project' => 'invoice.project',
];
protected array $recurring_invoice_report_keys = [
diff --git a/app/Export/CSV/InvoiceExport.php b/app/Export/CSV/InvoiceExport.php
index d87c49ff943c..39ece67a28a9 100644
--- a/app/Export/CSV/InvoiceExport.php
+++ b/app/Export/CSV/InvoiceExport.php
@@ -153,9 +153,9 @@ class InvoiceExport extends BaseExport
private function decorateAdvancedFields(Invoice $invoice, array $entity): array
{
- // if (in_array('invoice.status', $this->input['report_keys'])) {
- // $entity['invoice.status'] = $invoice->stringStatus($invoice->status_id);
- // }
+ if (in_array('invoice.project', $this->input['report_keys'])) {
+ $entity['invoice.project'] = $invoice->project ? $invoice->project->name : '';
+ }
if (in_array('invoice.recurring_id', $this->input['report_keys'])) {
$entity['invoice.recurring_id'] = $invoice->recurring_invoice->number ?? '';
diff --git a/app/Export/Decorators/InvoiceDecorator.php b/app/Export/Decorators/InvoiceDecorator.php
index 35985decba66..b6579b7f546d 100644
--- a/app/Export/Decorators/InvoiceDecorator.php
+++ b/app/Export/Decorators/InvoiceDecorator.php
@@ -92,6 +92,7 @@ class InvoiceDecorator extends Decorator implements DecoratorInterface
{
return $invoice->recurring_invoice ? $invoice->recurring_invoice->number : '';
}
+
public function auto_bill_enabled(Invoice $invoice)
{
return $invoice->auto_bill_enabled ? ctrans('texts.yes') : ctrans('texts.no');
diff --git a/app/Http/Controllers/ChartController.php b/app/Http/Controllers/ChartController.php
index 394e762d9749..07fc7fc238e4 100644
--- a/app/Http/Controllers/ChartController.php
+++ b/app/Http/Controllers/ChartController.php
@@ -66,7 +66,7 @@ class ChartController extends BaseController
return response()->json($cs->chart_summary($request->input('start_date'), $request->input('end_date')), 200);
}
- public function calculatedField(ShowCalculatedFieldRequest $request)
+ public function calculatedFields(ShowCalculatedFieldRequest $request)
{
/** @var \App\Models\User auth()->user() */
diff --git a/app/Http/ValidationRules/Account/BlackListRule.php b/app/Http/ValidationRules/Account/BlackListRule.php
index 1d65de052fa6..0d5e5a13a83f 100644
--- a/app/Http/ValidationRules/Account/BlackListRule.php
+++ b/app/Http/ValidationRules/Account/BlackListRule.php
@@ -19,8 +19,9 @@ use Illuminate\Contracts\Validation\ValidationRule;
*/
class BlackListRule implements ValidationRule
{
- /** Bad domains +/- dispoable email domains */
+ /** Bad domains +/- disposable email domains */
private array $blacklist = [
+ 'padvn.com',
'anonaddy.me',
'nqmo.com',
'wireconnected.com',
diff --git a/app/Import/Providers/BaseImport.php b/app/Import/Providers/BaseImport.php
index b2897352cc4a..0366970037f6 100644
--- a/app/Import/Providers/BaseImport.php
+++ b/app/Import/Providers/BaseImport.php
@@ -473,6 +473,8 @@ class BaseImport
$tasks = $this->groupTasks($tasks, $task_number_key);
+ nlog($tasks);
+
foreach ($tasks as $raw_task) {
$task_data = [];
diff --git a/app/Import/Transformer/Csv/TaskTransformer.php b/app/Import/Transformer/Csv/TaskTransformer.php
index edd8737131cb..54636349f050 100644
--- a/app/Import/Transformer/Csv/TaskTransformer.php
+++ b/app/Import/Transformer/Csv/TaskTransformer.php
@@ -46,6 +46,7 @@ class TaskTransformer extends BaseTransformer
'company_id' => $this->company->id,
'number' => $this->getString($task_data, 'task.number'),
'user_id' => $this->getString($task_data, 'task.user_id'),
+ 'rate' => $this->getFloat($task_data, 'task.rate'),
'client_id' => $clientId,
'project_id' => $this->getProjectId($projectId, $clientId),
'description' => $this->getString($task_data, 'task.description'),
@@ -87,8 +88,7 @@ class TaskTransformer extends BaseTransformer
$is_billable = true;
}
- if(isset($item['task.start_date']) &&
- isset($item['task.end_date'])) {
+ if(isset($item['task.start_date'])) {
$start_date = $this->resolveStartDate($item);
$end_date = $this->resolveEndDate($item);
} elseif(isset($item['task.duration'])) {
@@ -136,7 +136,7 @@ class TaskTransformer extends BaseTransformer
private function resolveEndDate($item)
{
- $stub_end_date = $item['task.end_date'];
+ $stub_end_date = isset($item['task.end_date']) ? $item['task.end_date'] : $item['task.start_date'];
$stub_end_date .= isset($item['task.end_time']) ? " ".$item['task.end_time'] : '';
try {
diff --git a/app/Models/Gateway.php b/app/Models/Gateway.php
index 46ae2abc56f6..2fcdfe7ee4d0 100644
--- a/app/Models/Gateway.php
+++ b/app/Models/Gateway.php
@@ -139,23 +139,23 @@ class Gateway extends StaticModel
case 20:
case 56:
return [
- GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true, 'webhooks' => ['payment_intent.succeeded', 'payment_intent.payment_failed']],
- GatewayType::BANK_TRANSFER => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'customer.source.updated', 'payment_intent.processing', 'payment_intent.payment_failed', 'charge.failed']],
- GatewayType::DIRECT_DEBIT => ['refund' => false, 'token_billing' => false, 'webhooks' => ['payment_intent.processing', 'payment_intent.succeeded', 'payment_intent.partially_funded', 'payment_intent.payment_failed']],
+ GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true, 'webhooks' => ['payment_intent.succeeded', 'charge.refunded', 'payment_intent.payment_failed']],
+ GatewayType::BANK_TRANSFER => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.refunded','charge.succeeded', 'customer.source.updated', 'payment_intent.processing', 'payment_intent.payment_failed', 'charge.failed']],
+ GatewayType::DIRECT_DEBIT => ['refund' => false, 'token_billing' => false, 'webhooks' => ['payment_intent.processing', 'charge.refunded', 'payment_intent.succeeded', 'payment_intent.partially_funded', 'payment_intent.payment_failed']],
GatewayType::ALIPAY => ['refund' => false, 'token_billing' => false],
GatewayType::APPLE_PAY => ['refund' => false, 'token_billing' => false],
- GatewayType::BACS => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.failed', 'payment_intent.processing', 'payment_intent.succeeded', 'mandate.updated', 'payment_intent.payment_failed']],
- GatewayType::SOFORT => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
- GatewayType::KLARNA => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
- GatewayType::SEPA => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
- GatewayType::PRZELEWY24 => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
- GatewayType::GIROPAY => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
- GatewayType::EPS => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
- GatewayType::BANCONTACT => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
- GatewayType::BECS => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
- GatewayType::IDEAL => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
- GatewayType::ACSS => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
- GatewayType::FPX => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.failed',]],
+ GatewayType::BACS => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.refunded', 'charge.failed', 'payment_intent.processing', 'payment_intent.succeeded', 'mandate.updated', 'payment_intent.payment_failed']],
+ GatewayType::SOFORT => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.refunded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
+ GatewayType::KLARNA => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.refunded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
+ GatewayType::SEPA => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.refunded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
+ GatewayType::PRZELEWY24 => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.refunded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
+ GatewayType::GIROPAY => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.refunded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
+ GatewayType::EPS => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.refunded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
+ GatewayType::BANCONTACT => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.refunded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
+ GatewayType::BECS => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.refunded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
+ GatewayType::IDEAL => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.refunded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
+ GatewayType::ACSS => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.refunded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
+ GatewayType::FPX => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.refunded', 'charge.failed',]],
];
case 39:
return [GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true, 'webhooks' => [' ']]]; //Checkout
diff --git a/app/Models/Project.php b/app/Models/Project.php
index d341a3fb8569..c786dcec0836 100644
--- a/app/Models/Project.php
+++ b/app/Models/Project.php
@@ -129,7 +129,7 @@ class Project extends BaseModel
public function invoices(): HasMany
{
- return $this->hasMany(Invoice::class);
+ return $this->hasMany(Invoice::class)->withTrashed();
}
public function quotes(): HasMany
diff --git a/app/PaymentDrivers/Forte/ACH.php b/app/PaymentDrivers/Forte/ACH.php
index aef043d50a9e..8ea313e77200 100644
--- a/app/PaymentDrivers/Forte/ACH.php
+++ b/app/PaymentDrivers/Forte/ACH.php
@@ -170,6 +170,9 @@ class ACH
];
$payment = $this->forte->createPayment($data, Payment::STATUS_COMPLETED);
- return redirect('client/invoices')->withSuccess('Invoice paid.');
+ // return redirect('client/invoices')->withSuccess('Invoice paid.');
+
+ return redirect()->route('client.payments.show', ['payment' => $payment->hashed_id]);
+
}
}
diff --git a/app/PaymentDrivers/Forte/CreditCard.php b/app/PaymentDrivers/Forte/CreditCard.php
index 67c404190137..5a317f4ec8a7 100644
--- a/app/PaymentDrivers/Forte/CreditCard.php
+++ b/app/PaymentDrivers/Forte/CreditCard.php
@@ -187,6 +187,8 @@ class CreditCard
'gateway_type_id' => GatewayType::CREDIT_CARD,
];
$payment = $this->forte->createPayment($data, Payment::STATUS_COMPLETED);
- return redirect('client/invoices')->withSuccess('Invoice paid.');
+ // return redirect('client/invoices')->withSuccess('Invoice paid.');
+ return redirect()->route('client.payments.show', ['payment' => $payment->hashed_id]);
+
}
}
diff --git a/app/PaymentDrivers/Stripe/Jobs/ChargeRefunded.php b/app/PaymentDrivers/Stripe/Jobs/ChargeRefunded.php
index 36f766dd7303..405f696742e4 100644
--- a/app/PaymentDrivers/Stripe/Jobs/ChargeRefunded.php
+++ b/app/PaymentDrivers/Stripe/Jobs/ChargeRefunded.php
@@ -11,18 +11,22 @@
namespace App\PaymentDrivers\Stripe\Jobs;
-use App\Libraries\MultiDB;
use App\Models\Company;
-use App\Models\CompanyGateway;
use App\Models\Payment;
+use App\Libraries\MultiDB;
use App\Models\PaymentHash;
-use App\PaymentDrivers\Stripe\Utilities;
+use App\Services\Email\Email;
use Illuminate\Bus\Queueable;
+use App\Models\CompanyGateway;
+use App\Services\Email\EmailObject;
+use Illuminate\Support\Facades\App;
+use Illuminate\Mail\Mailables\Address;
+use Illuminate\Queue\SerializesModels;
+use App\PaymentDrivers\Stripe\Utilities;
+use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
-use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\Middleware\WithoutOverlapping;
-use Illuminate\Queue\SerializesModels;
class ChargeRefunded implements ShouldQueue
{
@@ -36,19 +40,10 @@ class ChargeRefunded implements ShouldQueue
public $deleteWhenMissingModels = true;
- public $stripe_request;
-
- public $company_key;
-
- private $company_gateway_id;
-
public $payment_completed = false;
- public function __construct($stripe_request, $company_key, $company_gateway_id)
+ public function __construct(public array $stripe_request, private string $company_key)
{
- $this->stripe_request = $stripe_request;
- $this->company_key = $company_key;
- $this->company_gateway_id = $company_gateway_id;
}
public function handle()
@@ -64,8 +59,8 @@ class ChargeRefunded implements ShouldQueue
$payment_hash_key = $source['metadata']['payment_hash'] ?? null;
- $company_gateway = CompanyGateway::query()->find($this->company_gateway_id);
$payment_hash = PaymentHash::query()->where('hash', $payment_hash_key)->first();
+ $company_gateway = $payment_hash->payment->company_gateway;
$stripe_driver = $company_gateway->driver()->init();
@@ -79,7 +74,7 @@ class ChargeRefunded implements ShouldQueue
->first();
//don't touch if already refunded
- if(!$payment || in_array($payment->status_id, [Payment::STATUS_PARTIALLY_REFUNDED, Payment::STATUS_REFUNDED])) {
+ if(!$payment || $payment->status_id == Payment::STATUS_REFUNDED || $payment->is_deleted){
return;
}
@@ -94,8 +89,19 @@ class ChargeRefunded implements ShouldQueue
return;
}
- if($payment->status_id == Payment::STATUS_COMPLETED) {
+ usleep(rand(200000,300000));
+ $payment = $payment->fresh();
+ if($payment->status_id == Payment::STATUS_PARTIALLY_REFUNDED){
+ //determine the delta in the refunded amount - how much has already been refunded and only apply the delta.
+
+ if(floatval($payment->refunded) >= floatval($amount_refunded))
+ return;
+
+ $amount_refunded -= $payment->refunded;
+
+ }
+
$invoice_collection = $payment->paymentables
->where('paymentable_type', 'invoices')
->map(function ($pivot) {
@@ -117,9 +123,24 @@ class ChargeRefunded implements ShouldQueue
];
});
- } elseif($invoice_collection->sum('amount') != $amount_refunded) {
- //too many edges cases at this point, return early
+ }
+ elseif($invoice_collection->sum('amount') != $amount_refunded) {
+
+ $refund_text = "A partial refund was processed for Payment #{$payment_hash->payment->number}.
This payment is associated with multiple invoices, so you will need to manually apply the refund to the correct invoice/s.";
+
+ App::setLocale($payment_hash->payment->company->getLocale());
+
+ $mo = new EmailObject();
+ $mo->subject = "Refund processed in Stripe for multiple invoices, action required.";
+ $mo->body = $refund_text;
+ $mo->text_body = $refund_text;
+ $mo->company_key = $payment_hash->payment->company->company_key;
+ $mo->html_template = 'email.template.generic';
+ $mo->to = [new Address($payment_hash->payment->company->owner()->email, $payment_hash->payment->company->owner()->present()->name())];
+
+ Email::dispatch($mo, $payment_hash->payment->company);
return;
+
}
$invoices = $invoice_collection->toArray();
@@ -131,20 +152,21 @@ class ChargeRefunded implements ShouldQueue
'date' => now()->format('Y-m-d'),
'gateway_refund' => false,
'email_receipt' => false,
+ 'via_webhook' => true,
];
nlog($data);
$payment->refund($data);
- $payment->private_notes .= 'Refunded via Stripe';
- return;
- }
+ $payment->private_notes .= 'Refunded via Stripe ';
+
+ $payment->saveQuietly();
}
public function middleware()
{
- return [new WithoutOverlapping($this->company_gateway_id)];
+ return [new WithoutOverlapping($this->company_key)];
}
}
diff --git a/app/PaymentDrivers/StripePaymentDriver.php b/app/PaymentDrivers/StripePaymentDriver.php
index 69920e31e6b5..d91e39fc3763 100644
--- a/app/PaymentDrivers/StripePaymentDriver.php
+++ b/app/PaymentDrivers/StripePaymentDriver.php
@@ -12,54 +12,55 @@
namespace App\PaymentDrivers;
-use App\Exceptions\PaymentFailed;
-use App\Exceptions\StripeConnectFailure;
-use App\Http\Requests\Payments\PaymentWebhookRequest;
-use App\Http\Requests\Request;
-use App\Jobs\Util\SystemLogger;
-use App\Models\Client;
-use App\Models\ClientGatewayToken;
-use App\Models\GatewayType;
-use App\Models\Payment;
-use App\Models\PaymentHash;
-use App\Models\SystemLog;
-use App\PaymentDrivers\Stripe\ACH;
-use App\PaymentDrivers\Stripe\ACSS;
-use App\PaymentDrivers\Stripe\Alipay;
-use App\PaymentDrivers\Stripe\BACS;
-use App\PaymentDrivers\Stripe\Bancontact;
-use App\PaymentDrivers\Stripe\BankTransfer;
-use App\PaymentDrivers\Stripe\BECS;
-use App\PaymentDrivers\Stripe\BrowserPay;
-use App\PaymentDrivers\Stripe\Charge;
-use App\PaymentDrivers\Stripe\Connect\Verify;
-use App\PaymentDrivers\Stripe\CreditCard;
-use App\PaymentDrivers\Stripe\EPS;
-use App\PaymentDrivers\Stripe\FPX;
-use App\PaymentDrivers\Stripe\GIROPAY;
-use App\PaymentDrivers\Stripe\iDeal;
-use App\PaymentDrivers\Stripe\ImportCustomers;
-use App\PaymentDrivers\Stripe\Jobs\PaymentIntentFailureWebhook;
-use App\PaymentDrivers\Stripe\Jobs\PaymentIntentPartiallyFundedWebhook;
-use App\PaymentDrivers\Stripe\Jobs\PaymentIntentProcessingWebhook;
-use App\PaymentDrivers\Stripe\Jobs\PaymentIntentWebhook;
-use App\PaymentDrivers\Stripe\Klarna;
-use App\PaymentDrivers\Stripe\PRZELEWY24;
-use App\PaymentDrivers\Stripe\SEPA;
-use App\PaymentDrivers\Stripe\SOFORT;
-use App\PaymentDrivers\Stripe\Utilities;
-use App\Utils\Traits\MakesHash;
use Exception;
-use Illuminate\Http\RedirectResponse;
-use Laracasts\Presenter\Exceptions\PresenterException;
+use Stripe\Stripe;
use Stripe\Account;
use Stripe\Customer;
-use Stripe\Exception\ApiErrorException;
+use App\Models\Client;
+use App\Models\Payment;
+use Stripe\SetupIntent;
+use Stripe\StripeClient;
+use App\Models\SystemLog;
use Stripe\PaymentIntent;
use Stripe\PaymentMethod;
-use Stripe\SetupIntent;
-use Stripe\Stripe;
-use Stripe\StripeClient;
+use App\Models\GatewayType;
+use App\Models\PaymentHash;
+use App\Http\Requests\Request;
+use App\Jobs\Util\SystemLogger;
+use App\Utils\Traits\MakesHash;
+use App\Exceptions\PaymentFailed;
+use App\Models\ClientGatewayToken;
+use App\PaymentDrivers\Stripe\ACH;
+use App\PaymentDrivers\Stripe\EPS;
+use App\PaymentDrivers\Stripe\FPX;
+use App\PaymentDrivers\Stripe\ACSS;
+use App\PaymentDrivers\Stripe\BACS;
+use App\PaymentDrivers\Stripe\BECS;
+use App\PaymentDrivers\Stripe\SEPA;
+use App\PaymentDrivers\Stripe\iDeal;
+use App\PaymentDrivers\Stripe\Alipay;
+use App\PaymentDrivers\Stripe\Charge;
+use App\PaymentDrivers\Stripe\Klarna;
+use App\PaymentDrivers\Stripe\SOFORT;
+use Illuminate\Http\RedirectResponse;
+use App\PaymentDrivers\Stripe\GIROPAY;
+use Stripe\Exception\ApiErrorException;
+use App\Exceptions\StripeConnectFailure;
+use App\PaymentDrivers\Stripe\Utilities;
+use App\PaymentDrivers\Stripe\Bancontact;
+use App\PaymentDrivers\Stripe\BrowserPay;
+use App\PaymentDrivers\Stripe\CreditCard;
+use App\PaymentDrivers\Stripe\PRZELEWY24;
+use App\PaymentDrivers\Stripe\BankTransfer;
+use App\PaymentDrivers\Stripe\Connect\Verify;
+use App\PaymentDrivers\Stripe\ImportCustomers;
+use App\PaymentDrivers\Stripe\Jobs\ChargeRefunded;
+use App\Http\Requests\Payments\PaymentWebhookRequest;
+use Laracasts\Presenter\Exceptions\PresenterException;
+use App\PaymentDrivers\Stripe\Jobs\PaymentIntentWebhook;
+use App\PaymentDrivers\Stripe\Jobs\PaymentIntentFailureWebhook;
+use App\PaymentDrivers\Stripe\Jobs\PaymentIntentProcessingWebhook;
+use App\PaymentDrivers\Stripe\Jobs\PaymentIntentPartiallyFundedWebhook;
class StripePaymentDriver extends BaseDriver
{
@@ -670,31 +671,39 @@ class StripePaymentDriver extends BaseDriver
public function processWebhookRequest(PaymentWebhookRequest $request)
{
+ nlog($request->all());
+
if ($request->type === 'customer.source.updated') {
$ach = new ACH($this);
$ach->updateBankAccount($request->all());
}
if ($request->type === 'payment_intent.processing') {
- PaymentIntentProcessingWebhook::dispatch($request->data, $request->company_key, $this->company_gateway->id)->delay(now()->addSeconds(rand(10, 12)));
+ PaymentIntentProcessingWebhook::dispatch($request->data, $request->company_key, $this->company_gateway->id)->delay(now()->addSeconds(5));
return response()->json([], 200);
}
//payment_intent.succeeded - this will confirm or cancel the payment
if ($request->type === 'payment_intent.succeeded') {
- PaymentIntentWebhook::dispatch($request->data, $request->company_key, $this->company_gateway->id)->delay(now()->addSeconds(rand(10, 15)));
+ PaymentIntentWebhook::dispatch($request->data, $request->company_key, $this->company_gateway->id)->delay(now()->addSeconds(5));
return response()->json([], 200);
}
if ($request->type === 'payment_intent.partially_funded') {
- PaymentIntentPartiallyFundedWebhook::dispatch($request->data, $request->company_key, $this->company_gateway->id)->delay(now()->addSeconds(rand(10, 15)));
+ PaymentIntentPartiallyFundedWebhook::dispatch($request->data, $request->company_key, $this->company_gateway->id)->delay(now()->addSeconds(5));
return response()->json([], 200);
}
if (in_array($request->type, ['payment_intent.payment_failed', 'charge.failed'])) {
- PaymentIntentFailureWebhook::dispatch($request->data, $request->company_key, $this->company_gateway->id)->delay(now()->addSeconds(rand(5, 10)));
+ PaymentIntentFailureWebhook::dispatch($request->data, $request->company_key, $this->company_gateway->id)->delay(now()->addSeconds(2));
+
+ return response()->json([], 200);
+ }
+
+ if ($request->type === 'charge.refunded' && $request->data['object']['status'] == 'succeeded') {
+ ChargeRefunded::dispatch($request->data, $request->company_key)->delay(now()->addSeconds(5));
return response()->json([], 200);
}
@@ -702,7 +711,6 @@ class StripePaymentDriver extends BaseDriver
if ($request->type === 'charge.succeeded') {
foreach ($request->data as $transaction) {
-
$payment = Payment::query()
->where('company_id', $this->company_gateway->company_id)
->where(function ($query) use ($transaction) {
diff --git a/app/Services/Payment/RefundPayment.php b/app/Services/Payment/RefundPayment.php
index a17baa67051e..ed7b51deed20 100644
--- a/app/Services/Payment/RefundPayment.php
+++ b/app/Services/Payment/RefundPayment.php
@@ -44,7 +44,6 @@ class RefundPayment
->setStatus() //sets status of payment
->updatePaymentables() //update the paymentable items
->adjustInvoices()
- ->finalize()
->save();
if (array_key_exists('email_receipt', $this->refund_data) && $this->refund_data['email_receipt'] == 'true') {
@@ -52,10 +51,11 @@ class RefundPayment
EmailRefundPayment::dispatch($this->payment, $this->payment->company, $contact);
}
- $notes = ctrans('texts.refunded') . " : {$this->total_refund} - " . ctrans('texts.gateway_refund') . " : ";
- $notes .= $this->refund_data['gateway_refund'] !== false ? ctrans('texts.yes') : ctrans('texts.no');
-
+ $is_gateway_refund = ($this->refund_data['gateway_refund'] !== false || $this->refund_failed || (isset($this->refund_data['via_webhook']) && $this->refund_data['via_webhook'] !== false)) ? ctrans('texts.yes') : ctrans('texts.no');
+ $notes = ctrans('texts.refunded') . " : {$this->total_refund} - " . ctrans('texts.gateway_refund') . " : " . $is_gateway_refund;
+
$this->createActivity($notes);
+ $this->finalize();
return $this->payment;
}
@@ -178,7 +178,7 @@ class RefundPayment
*/
private function setStatus()
{
- if ($this->total_refund == $this->payment->amount) {
+ if ($this->total_refund == $this->payment->amount || floatval($this->payment->amount) == floatval($this->payment->refunded)) {
$this->payment->status_id = Payment::STATUS_REFUNDED;
} else {
$this->payment->status_id = Payment::STATUS_PARTIALLY_REFUNDED;
diff --git a/app/Services/Template/TemplateService.php b/app/Services/Template/TemplateService.php
index 5a033c2f5383..13adb454d7f1 100644
--- a/app/Services/Template/TemplateService.php
+++ b/app/Services/Template/TemplateService.php
@@ -1023,7 +1023,8 @@ class TemplateService
'vat_number' => $project->client->vat_number ?? '',
'currency' => $project->client->currency()->code ?? 'USD',
] : [],
- 'user' => $this->userInfo($project->user)
+ 'user' => $this->userInfo($project->user),
+ 'invoices' => $this->processInvoices($project->invoices)
];
}
diff --git a/composer.lock b/composer.lock
index 541f0c04527b..4d331a5d006e 100644
--- a/composer.lock
+++ b/composer.lock
@@ -5407,16 +5407,16 @@
},
{
"name": "league/commonmark",
- "version": "2.4.2",
+ "version": "2.5.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/commonmark.git",
- "reference": "91c24291965bd6d7c46c46a12ba7492f83b1cadf"
+ "reference": "0026475f5c9a104410ae824cb5a4d63fa3bdb1df"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/91c24291965bd6d7c46c46a12ba7492f83b1cadf",
- "reference": "91c24291965bd6d7c46c46a12ba7492f83b1cadf",
+ "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/0026475f5c9a104410ae824cb5a4d63fa3bdb1df",
+ "reference": "0026475f5c9a104410ae824cb5a4d63fa3bdb1df",
"shasum": ""
},
"require": {
@@ -5429,8 +5429,8 @@
},
"require-dev": {
"cebe/markdown": "^1.0",
- "commonmark/cmark": "0.30.3",
- "commonmark/commonmark.js": "0.30.0",
+ "commonmark/cmark": "0.31.0",
+ "commonmark/commonmark.js": "0.31.0",
"composer/package-versions-deprecated": "^1.8",
"embed/embed": "^4.4",
"erusev/parsedown": "^1.0",
@@ -5452,7 +5452,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "2.5-dev"
+ "dev-main": "2.6-dev"
}
},
"autoload": {
@@ -5509,7 +5509,7 @@
"type": "tidelift"
}
],
- "time": "2024-02-02T11:59:32+00:00"
+ "time": "2024-07-22T18:18:14+00:00"
},
{
"name": "league/config",
diff --git a/lang/en/texts.php b/lang/en/texts.php
index e2bad2efdae3..f00d22cdcf4c 100644
--- a/lang/en/texts.php
+++ b/lang/en/texts.php
@@ -5124,7 +5124,7 @@ $lang = array(
'all_contacts' => 'All Contacts',
'insert_below' => 'Insert Below',
'nordigen_handler_subtitle' => 'Bank account authentication. Selecting your institution to complete the request with your account credentials.',
- 'nordigen_handler_error_heading_unknown' => 'An error has occured',
+ 'nordigen_handler_error_heading_unknown' => 'An error has occurred',
'nordigen_handler_error_contents_unknown' => 'An unknown error has occurred! Reason:',
'nordigen_handler_error_heading_token_invalid' => 'Invalid Token',
'nordigen_handler_error_contents_token_invalid' => 'The provided token was invalid. Contact support for help, if this issue persists.',
diff --git a/routes/api.php b/routes/api.php
index 0b0ffa772b15..2a5a2350f191 100644
--- a/routes/api.php
+++ b/routes/api.php
@@ -164,6 +164,7 @@ Route::group(['middleware' => ['throttle:api', 'api_db', 'token_auth', 'locale']
Route::post('charts/totals_v2', [ChartController::class, 'totalsV2'])->name('chart.totals_v2');
Route::post('charts/chart_summary_v2', [ChartController::class, 'chart_summaryV2'])->name('chart.chart_summary_v2');
+ Route::post('charts/calculated_fields', [ChartController::class, 'calculatedFields'])->name('chart.calculated_fields');
Route::post('claim_license', [LicenseController::class, 'index'])->name('license.index');