Improvements for task imports

This commit is contained in:
David Bomba 2024-07-24 12:27:09 +10:00
parent a236da0149
commit d6a4f4b4ca
19 changed files with 155 additions and 114 deletions

View File

@ -177,7 +177,6 @@ class BackupUpdate extends Command
$doc_bin = $document->getFile(); $doc_bin = $document->getFile();
} catch(\Exception $e) { } catch(\Exception $e) {
nlog("Exception:: BackupUpdate::" . $e->getMessage()); nlog("Exception:: BackupUpdate::" . $e->getMessage());
nlog($e->getMessage());
} }
if ($doc_bin) { if ($doc_bin) {

View File

@ -172,6 +172,7 @@ class BaseExport
'tax_rate3' => 'invoice.tax_rate3', 'tax_rate3' => 'invoice.tax_rate3',
'recurring_invoice' => 'invoice.recurring_id', 'recurring_invoice' => 'invoice.recurring_id',
'auto_bill' => 'invoice.auto_bill_enabled', 'auto_bill' => 'invoice.auto_bill_enabled',
'project' => 'invoice.project',
]; ];
protected array $recurring_invoice_report_keys = [ protected array $recurring_invoice_report_keys = [

View File

@ -153,9 +153,9 @@ class InvoiceExport extends BaseExport
private function decorateAdvancedFields(Invoice $invoice, array $entity): array private function decorateAdvancedFields(Invoice $invoice, array $entity): array
{ {
// if (in_array('invoice.status', $this->input['report_keys'])) { if (in_array('invoice.project', $this->input['report_keys'])) {
// $entity['invoice.status'] = $invoice->stringStatus($invoice->status_id); $entity['invoice.project'] = $invoice->project ? $invoice->project->name : '';
// } }
if (in_array('invoice.recurring_id', $this->input['report_keys'])) { if (in_array('invoice.recurring_id', $this->input['report_keys'])) {
$entity['invoice.recurring_id'] = $invoice->recurring_invoice->number ?? ''; $entity['invoice.recurring_id'] = $invoice->recurring_invoice->number ?? '';

View File

@ -92,6 +92,7 @@ class InvoiceDecorator extends Decorator implements DecoratorInterface
{ {
return $invoice->recurring_invoice ? $invoice->recurring_invoice->number : ''; return $invoice->recurring_invoice ? $invoice->recurring_invoice->number : '';
} }
public function auto_bill_enabled(Invoice $invoice) public function auto_bill_enabled(Invoice $invoice)
{ {
return $invoice->auto_bill_enabled ? ctrans('texts.yes') : ctrans('texts.no'); return $invoice->auto_bill_enabled ? ctrans('texts.yes') : ctrans('texts.no');

View File

@ -66,7 +66,7 @@ class ChartController extends BaseController
return response()->json($cs->chart_summary($request->input('start_date'), $request->input('end_date')), 200); 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() */ /** @var \App\Models\User auth()->user() */

View File

@ -19,8 +19,9 @@ use Illuminate\Contracts\Validation\ValidationRule;
*/ */
class BlackListRule implements ValidationRule class BlackListRule implements ValidationRule
{ {
/** Bad domains +/- dispoable email domains */ /** Bad domains +/- disposable email domains */
private array $blacklist = [ private array $blacklist = [
'padvn.com',
'anonaddy.me', 'anonaddy.me',
'nqmo.com', 'nqmo.com',
'wireconnected.com', 'wireconnected.com',

View File

@ -473,6 +473,8 @@ class BaseImport
$tasks = $this->groupTasks($tasks, $task_number_key); $tasks = $this->groupTasks($tasks, $task_number_key);
nlog($tasks);
foreach ($tasks as $raw_task) { foreach ($tasks as $raw_task) {
$task_data = []; $task_data = [];

View File

@ -46,6 +46,7 @@ class TaskTransformer extends BaseTransformer
'company_id' => $this->company->id, 'company_id' => $this->company->id,
'number' => $this->getString($task_data, 'task.number'), 'number' => $this->getString($task_data, 'task.number'),
'user_id' => $this->getString($task_data, 'task.user_id'), 'user_id' => $this->getString($task_data, 'task.user_id'),
'rate' => $this->getFloat($task_data, 'task.rate'),
'client_id' => $clientId, 'client_id' => $clientId,
'project_id' => $this->getProjectId($projectId, $clientId), 'project_id' => $this->getProjectId($projectId, $clientId),
'description' => $this->getString($task_data, 'task.description'), 'description' => $this->getString($task_data, 'task.description'),
@ -87,8 +88,7 @@ class TaskTransformer extends BaseTransformer
$is_billable = true; $is_billable = true;
} }
if(isset($item['task.start_date']) && if(isset($item['task.start_date'])) {
isset($item['task.end_date'])) {
$start_date = $this->resolveStartDate($item); $start_date = $this->resolveStartDate($item);
$end_date = $this->resolveEndDate($item); $end_date = $this->resolveEndDate($item);
} elseif(isset($item['task.duration'])) { } elseif(isset($item['task.duration'])) {
@ -136,7 +136,7 @@ class TaskTransformer extends BaseTransformer
private function resolveEndDate($item) 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'] : ''; $stub_end_date .= isset($item['task.end_time']) ? " ".$item['task.end_time'] : '';
try { try {

View File

@ -139,23 +139,23 @@ class Gateway extends StaticModel
case 20: case 20:
case 56: case 56:
return [ return [
GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true, 'webhooks' => ['payment_intent.succeeded', '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.succeeded', 'customer.source.updated', 'payment_intent.processing', 'payment_intent.payment_failed', 'charge.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', 'payment_intent.succeeded', 'payment_intent.partially_funded', 'payment_intent.payment_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::ALIPAY => ['refund' => false, 'token_billing' => false],
GatewayType::APPLE_PAY => ['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::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.failed', 'payment_intent.succeeded', '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.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.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.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.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.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.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.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.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.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.failed',]], GatewayType::FPX => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.refunded', 'charge.failed',]],
]; ];
case 39: case 39:
return [GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true, 'webhooks' => [' ']]]; //Checkout return [GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true, 'webhooks' => [' ']]]; //Checkout

View File

@ -129,7 +129,7 @@ class Project extends BaseModel
public function invoices(): HasMany public function invoices(): HasMany
{ {
return $this->hasMany(Invoice::class); return $this->hasMany(Invoice::class)->withTrashed();
} }
public function quotes(): HasMany public function quotes(): HasMany

View File

@ -170,6 +170,9 @@ class ACH
]; ];
$payment = $this->forte->createPayment($data, Payment::STATUS_COMPLETED); $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]);
} }
} }

View File

@ -187,6 +187,8 @@ class CreditCard
'gateway_type_id' => GatewayType::CREDIT_CARD, 'gateway_type_id' => GatewayType::CREDIT_CARD,
]; ];
$payment = $this->forte->createPayment($data, Payment::STATUS_COMPLETED); $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]);
} }
} }

View File

@ -11,18 +11,22 @@
namespace App\PaymentDrivers\Stripe\Jobs; namespace App\PaymentDrivers\Stripe\Jobs;
use App\Libraries\MultiDB;
use App\Models\Company; use App\Models\Company;
use App\Models\CompanyGateway;
use App\Models\Payment; use App\Models\Payment;
use App\Libraries\MultiDB;
use App\Models\PaymentHash; use App\Models\PaymentHash;
use App\PaymentDrivers\Stripe\Utilities; use App\Services\Email\Email;
use Illuminate\Bus\Queueable; 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\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\Middleware\WithoutOverlapping; use Illuminate\Queue\Middleware\WithoutOverlapping;
use Illuminate\Queue\SerializesModels;
class ChargeRefunded implements ShouldQueue class ChargeRefunded implements ShouldQueue
{ {
@ -36,19 +40,10 @@ class ChargeRefunded implements ShouldQueue
public $deleteWhenMissingModels = true; public $deleteWhenMissingModels = true;
public $stripe_request;
public $company_key;
private $company_gateway_id;
public $payment_completed = false; 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() public function handle()
@ -64,8 +59,8 @@ class ChargeRefunded implements ShouldQueue
$payment_hash_key = $source['metadata']['payment_hash'] ?? null; $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(); $payment_hash = PaymentHash::query()->where('hash', $payment_hash_key)->first();
$company_gateway = $payment_hash->payment->company_gateway;
$stripe_driver = $company_gateway->driver()->init(); $stripe_driver = $company_gateway->driver()->init();
@ -79,7 +74,7 @@ class ChargeRefunded implements ShouldQueue
->first(); ->first();
//don't touch if already refunded //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; return;
} }
@ -94,8 +89,19 @@ class ChargeRefunded implements ShouldQueue
return; 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 $invoice_collection = $payment->paymentables
->where('paymentable_type', 'invoices') ->where('paymentable_type', 'invoices')
->map(function ($pivot) { ->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}. <br><br> 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; return;
} }
$invoices = $invoice_collection->toArray(); $invoices = $invoice_collection->toArray();
@ -131,20 +152,21 @@ class ChargeRefunded implements ShouldQueue
'date' => now()->format('Y-m-d'), 'date' => now()->format('Y-m-d'),
'gateway_refund' => false, 'gateway_refund' => false,
'email_receipt' => false, 'email_receipt' => false,
'via_webhook' => true,
]; ];
nlog($data); nlog($data);
$payment->refund($data); $payment->refund($data);
$payment->private_notes .= 'Refunded via Stripe'; $payment->private_notes .= 'Refunded via Stripe ';
return;
} $payment->saveQuietly();
} }
public function middleware() public function middleware()
{ {
return [new WithoutOverlapping($this->company_gateway_id)]; return [new WithoutOverlapping($this->company_key)];
} }
} }

View File

@ -12,54 +12,55 @@
namespace App\PaymentDrivers; 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 Exception;
use Illuminate\Http\RedirectResponse; use Stripe\Stripe;
use Laracasts\Presenter\Exceptions\PresenterException;
use Stripe\Account; use Stripe\Account;
use Stripe\Customer; 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\PaymentIntent;
use Stripe\PaymentMethod; use Stripe\PaymentMethod;
use Stripe\SetupIntent; use App\Models\GatewayType;
use Stripe\Stripe; use App\Models\PaymentHash;
use Stripe\StripeClient; 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 class StripePaymentDriver extends BaseDriver
{ {
@ -670,31 +671,39 @@ class StripePaymentDriver extends BaseDriver
public function processWebhookRequest(PaymentWebhookRequest $request) public function processWebhookRequest(PaymentWebhookRequest $request)
{ {
nlog($request->all());
if ($request->type === 'customer.source.updated') { if ($request->type === 'customer.source.updated') {
$ach = new ACH($this); $ach = new ACH($this);
$ach->updateBankAccount($request->all()); $ach->updateBankAccount($request->all());
} }
if ($request->type === 'payment_intent.processing') { 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); return response()->json([], 200);
} }
//payment_intent.succeeded - this will confirm or cancel the payment //payment_intent.succeeded - this will confirm or cancel the payment
if ($request->type === 'payment_intent.succeeded') { 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); return response()->json([], 200);
} }
if ($request->type === 'payment_intent.partially_funded') { 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); return response()->json([], 200);
} }
if (in_array($request->type, ['payment_intent.payment_failed', 'charge.failed'])) { 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); return response()->json([], 200);
} }
@ -702,7 +711,6 @@ class StripePaymentDriver extends BaseDriver
if ($request->type === 'charge.succeeded') { if ($request->type === 'charge.succeeded') {
foreach ($request->data as $transaction) { foreach ($request->data as $transaction) {
$payment = Payment::query() $payment = Payment::query()
->where('company_id', $this->company_gateway->company_id) ->where('company_id', $this->company_gateway->company_id)
->where(function ($query) use ($transaction) { ->where(function ($query) use ($transaction) {

View File

@ -44,7 +44,6 @@ class RefundPayment
->setStatus() //sets status of payment ->setStatus() //sets status of payment
->updatePaymentables() //update the paymentable items ->updatePaymentables() //update the paymentable items
->adjustInvoices() ->adjustInvoices()
->finalize()
->save(); ->save();
if (array_key_exists('email_receipt', $this->refund_data) && $this->refund_data['email_receipt'] == 'true') { 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); EmailRefundPayment::dispatch($this->payment, $this->payment->company, $contact);
} }
$notes = ctrans('texts.refunded') . " : {$this->total_refund} - " . ctrans('texts.gateway_refund') . " : "; $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 .= $this->refund_data['gateway_refund'] !== 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->createActivity($notes);
$this->finalize();
return $this->payment; return $this->payment;
} }
@ -178,7 +178,7 @@ class RefundPayment
*/ */
private function setStatus() 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; $this->payment->status_id = Payment::STATUS_REFUNDED;
} else { } else {
$this->payment->status_id = Payment::STATUS_PARTIALLY_REFUNDED; $this->payment->status_id = Payment::STATUS_PARTIALLY_REFUNDED;

View File

@ -1023,7 +1023,8 @@ class TemplateService
'vat_number' => $project->client->vat_number ?? '', 'vat_number' => $project->client->vat_number ?? '',
'currency' => $project->client->currency()->code ?? 'USD', 'currency' => $project->client->currency()->code ?? 'USD',
] : [], ] : [],
'user' => $this->userInfo($project->user) 'user' => $this->userInfo($project->user),
'invoices' => $this->processInvoices($project->invoices)
]; ];
} }

16
composer.lock generated
View File

@ -5407,16 +5407,16 @@
}, },
{ {
"name": "league/commonmark", "name": "league/commonmark",
"version": "2.4.2", "version": "2.5.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/thephpleague/commonmark.git", "url": "https://github.com/thephpleague/commonmark.git",
"reference": "91c24291965bd6d7c46c46a12ba7492f83b1cadf" "reference": "0026475f5c9a104410ae824cb5a4d63fa3bdb1df"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/91c24291965bd6d7c46c46a12ba7492f83b1cadf", "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/0026475f5c9a104410ae824cb5a4d63fa3bdb1df",
"reference": "91c24291965bd6d7c46c46a12ba7492f83b1cadf", "reference": "0026475f5c9a104410ae824cb5a4d63fa3bdb1df",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -5429,8 +5429,8 @@
}, },
"require-dev": { "require-dev": {
"cebe/markdown": "^1.0", "cebe/markdown": "^1.0",
"commonmark/cmark": "0.30.3", "commonmark/cmark": "0.31.0",
"commonmark/commonmark.js": "0.30.0", "commonmark/commonmark.js": "0.31.0",
"composer/package-versions-deprecated": "^1.8", "composer/package-versions-deprecated": "^1.8",
"embed/embed": "^4.4", "embed/embed": "^4.4",
"erusev/parsedown": "^1.0", "erusev/parsedown": "^1.0",
@ -5452,7 +5452,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "2.5-dev" "dev-main": "2.6-dev"
} }
}, },
"autoload": { "autoload": {
@ -5509,7 +5509,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2024-02-02T11:59:32+00:00" "time": "2024-07-22T18:18:14+00:00"
}, },
{ {
"name": "league/config", "name": "league/config",

View File

@ -5124,7 +5124,7 @@ $lang = array(
'all_contacts' => 'All Contacts', 'all_contacts' => 'All Contacts',
'insert_below' => 'Insert Below', 'insert_below' => 'Insert Below',
'nordigen_handler_subtitle' => 'Bank account authentication. Selecting your institution to complete the request with your account credentials.', '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_contents_unknown' => 'An unknown error has occurred! Reason:',
'nordigen_handler_error_heading_token_invalid' => 'Invalid Token', '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.', 'nordigen_handler_error_contents_token_invalid' => 'The provided token was invalid. Contact support for help, if this issue persists.',

View File

@ -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/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/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'); Route::post('claim_license', [LicenseController::class, 'index'])->name('license.index');