1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-15 07:33:04 +01:00
invoiceninja/app/PaymentDrivers/Square/SquareWebhook.php

179 lines
6.4 KiB
PHP
Raw Normal View History

2023-08-19 02:14:46 +02:00
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
2024-04-12 06:15:41 +02:00
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
2023-08-19 02:14:46 +02:00
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\PaymentDrivers\Square;
2023-10-26 04:57:44 +02:00
use App\Jobs\Mail\PaymentFailedMailer;
use App\Jobs\Util\SystemLogger;
2023-08-19 02:14:46 +02:00
use App\Libraries\MultiDB;
2023-10-26 04:57:44 +02:00
use App\Models\CompanyGateway;
2023-08-19 02:14:46 +02:00
use App\Models\GatewayType;
2023-10-26 04:57:44 +02:00
use App\Models\Payment;
2023-08-19 02:14:46 +02:00
use App\Models\PaymentHash;
use App\Models\PaymentType;
2023-10-26 04:57:44 +02:00
use App\Models\SystemLog;
2023-08-19 07:52:37 +02:00
use App\PaymentDrivers\SquarePaymentDriver;
2023-10-26 04:57:44 +02:00
use App\PaymentDrivers\Stripe\Utilities;
use Illuminate\Bus\Queueable;
2023-08-19 02:14:46 +02:00
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
2023-10-26 04:57:44 +02:00
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
2023-08-19 02:14:46 +02:00
class SquareWebhook implements ShouldQueue
{
2024-01-14 05:05:00 +01:00
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
use Utilities;
2023-08-19 02:14:46 +02:00
public $tries = 1;
public $deleteWhenMissingModels = true;
public CompanyGateway $company_gateway;
2023-08-19 07:52:37 +02:00
public SquarePaymentDriver $driver;
public \Square\SquareClient $square;
2023-08-19 04:23:37 +02:00
private array $source_type = [
2023-10-26 04:57:44 +02:00
'CARD' => PaymentType::CREDIT_CARD_OTHER,
'BANK_ACCOUNT' => PaymentType::ACH,
'WALLET' => PaymentType::CREDIT_CARD_OTHER,
'BUY_NOW_PAY_LATER' => PaymentType::CREDIT_CARD_OTHER,
'SQUARE_ACCOUNT' => PaymentType::CREDIT_CARD_OTHER,
'CASH' => PaymentType::CASH,
2024-01-14 05:05:00 +01:00
'EXTERNAL' => PaymentType::CREDIT_CARD_OTHER
2023-08-19 04:23:37 +02:00
];
2023-08-19 02:14:46 +02:00
public function __construct(public array $webhook_array, public string $company_key, public int $company_gateway_id)
{
}
public function handle()
{
nlog("Square Webhook");
MultiDB::findAndSetDbByCompanyKey($this->company_key);
2023-08-20 11:17:21 +02:00
$this->company_gateway = CompanyGateway::query()->withTrashed()->find($this->company_gateway_id);
2023-08-19 07:52:37 +02:00
$this->driver = $this->company_gateway->driver()->init();
$this->square = $this->driver->square;
2023-08-19 02:14:46 +02:00
2023-08-19 04:23:37 +02:00
$status = $this->webhook_array['data']['object']['payment']['status'] ?? false;
$payment_id = $this->webhook_array['data']['object']['payment']['id'] ?? null;
2023-08-20 11:17:21 +02:00
$payment_status = false;
2023-10-26 04:57:44 +02:00
match($status) {
2023-08-19 07:52:37 +02:00
'APPROVED' => $payment_status = false,
2023-08-19 04:31:35 +02:00
'COMPLETED' => $payment_status = Payment::STATUS_COMPLETED,
'PENDING' => $payment_status = Payment::STATUS_PENDING,
'CANCELED' => $payment_status = Payment::STATUS_CANCELLED,
'FAILED' => $payment_status = Payment::STATUS_FAILED,
2023-08-19 07:52:37 +02:00
default => $payment_status = false,
2023-08-19 04:31:35 +02:00
};
2023-10-26 04:57:44 +02:00
if(!$payment_status) {
2023-08-19 07:52:37 +02:00
nlog("Square Webhook - Payment Status Not Found or not worthy of processing");
nlog($this->webhook_array);
}
2023-08-19 04:31:35 +02:00
$payment = $this->retrieveOrCreatePayment($payment_id, $payment_status);
2023-08-19 07:52:37 +02:00
/** If the status was pending and now is reporting as Failed / Cancelled - process failure path */
2023-10-26 04:57:44 +02:00
if($payment->status_id == Payment::STATUS_PENDING && in_array($payment_status, [Payment::STATUS_CANCELLED, Payment::STATUS_FAILED])) {
2023-08-19 07:52:37 +02:00
$payment->service()->deletePayment();
if ($this->driver->payment_hash) {
$error = ctrans('texts.client_payment_failure_body', [
'invoice' => implode(',', $payment->invoices->pluck('number')->toArray()),
2023-10-26 04:57:44 +02:00
'amount' => array_sum(array_column($this->driver->payment_hash->invoices(), 'amount')) + $this->driver->payment_hash->fee_total,
2023-08-19 07:52:37 +02:00
]);
} else {
$error = 'Payment for '.$payment->client->present()->name()." for {$payment->amount} failed";
}
PaymentFailedMailer::dispatch(
$this->driver->payment_hash,
$this->driver->client->company,
$this->driver->client,
$error
);
2023-10-26 04:57:44 +02:00
} elseif($payment->status_id == Payment::STATUS_PENDING && in_array($payment_status, [Payment::STATUS_COMPLETED, Payment::STATUS_COMPLETED])) {
2023-08-19 07:52:37 +02:00
$payment->status_id = Payment::STATUS_COMPLETED;
$payment->save();
}
2024-01-14 05:05:00 +01:00
2023-08-19 02:14:46 +02:00
}
2023-08-19 04:23:37 +02:00
2023-08-20 11:17:21 +02:00
private function retrieveOrCreatePayment(?string $payment_reference, int $payment_status): ?\App\Models\Payment
2023-08-19 04:23:37 +02:00
{
$payment = Payment::withTrashed()->where('transaction_reference', $payment_reference)->first();
2023-08-20 13:43:31 +02:00
if($payment) {
nlog("payment found, returning");
2023-08-19 04:23:37 +02:00
return $payment;
2023-08-20 13:43:31 +02:00
}
2023-08-19 04:23:37 +02:00
2023-08-19 07:52:37 +02:00
/** Handles the edge case where for some reason the payment has not yet been recorded in Invoice Ninja */
$apiResponse = $this->square->getPaymentsApi()->getPayment($payment_reference);
2023-08-19 04:23:37 +02:00
2023-08-20 13:43:31 +02:00
nlog("searching square for payment");
2023-08-19 04:23:37 +02:00
2023-10-26 04:57:44 +02:00
if($apiResponse->isSuccess()) {
2023-08-19 04:23:37 +02:00
2023-08-20 13:43:31 +02:00
nlog("Searching by payment hash");
2023-10-15 22:28:17 +02:00
$payment_hash_id = $apiResponse->getResult()->getPayment()->getReferenceId() ?? false;
$square_payment = $apiResponse->getResult()->getPayment()->jsonSerialize();
2023-08-20 13:43:31 +02:00
$payment_hash = PaymentHash::query()->where('hash', $payment_hash_id)->firstOrFail();
2023-08-19 04:23:37 +02:00
$payment_hash->data = array_merge((array) $payment_hash->data, (array)$square_payment);
$payment_hash->save();
2023-08-19 07:52:37 +02:00
$this->driver->setPaymentHash($payment_hash);
$this->driver->setClient($payment_hash->fee_invoice->client);
2023-08-19 04:23:37 +02:00
$data = [
'payment_type' => $this->source_type[$square_payment->source_type],
2023-08-27 01:22:46 +02:00
'amount' => $payment_hash->amount_with_fee(),
2023-08-19 04:23:37 +02:00
'transaction_reference' => $square_payment->id,
'gateway_type_id' => GatewayType::BANK_TRANSFER,
];
2023-08-19 07:52:37 +02:00
$payment = $this->driver->createPayment($data, $payment_status);
2024-01-14 05:05:00 +01:00
2023-08-20 13:43:31 +02:00
nlog("Creating payment");
2023-08-19 04:23:37 +02:00
SystemLogger::dispatch(
['response' => $this->webhook_array, 'data' => $square_payment],
SystemLog::CATEGORY_GATEWAY_RESPONSE,
SystemLog::EVENT_GATEWAY_SUCCESS,
SystemLog::TYPE_SQUARE,
2023-08-19 07:52:37 +02:00
$this->driver->client,
$this->driver->client->company,
2023-08-19 04:23:37 +02:00
);
return $payment;
2023-10-26 04:57:44 +02:00
} else {
2023-08-20 11:20:12 +02:00
nlog("Square Webhook - Payment not found: {$payment_reference}");
2023-08-19 07:52:37 +02:00
nlog($apiResponse->getErrors());
2023-08-20 11:17:21 +02:00
return null;
2023-08-19 04:23:37 +02:00
}
}
2023-10-26 04:57:44 +02:00
}