Handling square webhook events

This commit is contained in:
David Bomba 2023-08-19 15:52:37 +10:00
parent b35b8210ac
commit 5fb51656a6
3 changed files with 130 additions and 36 deletions

View File

@ -222,6 +222,22 @@ class CompanyGateway extends BaseModel
$this->config = encrypt(json_encode($config)); $this->config = encrypt(json_encode($config));
} }
/**
* setConfigField
*
* @param mixed $field
* @param mixed $value
* @return void
*/
public function setConfigField($field, $value): void
{
$config = $this->getConfig();
$config->{$field} = $value;
$this->setConfig($config);
$this->save();
}
/** /**
* @return mixed * @return mixed
*/ */

View File

@ -20,9 +20,11 @@ use App\Models\PaymentType;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use App\Models\CompanyGateway; use App\Models\CompanyGateway;
use App\Jobs\Util\SystemLogger; use App\Jobs\Util\SystemLogger;
use App\Jobs\Mail\PaymentFailedMailer;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use App\PaymentDrivers\Stripe\Utilities; use App\PaymentDrivers\Stripe\Utilities;
use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\InteractsWithQueue;
use App\PaymentDrivers\SquarePaymentDriver;
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Foundation\Bus\Dispatchable;
@ -36,6 +38,10 @@ class SquareWebhook implements ShouldQueue
public CompanyGateway $company_gateway; public CompanyGateway $company_gateway;
public SquarePaymentDriver $driver;
public \Square\SquareClient $square;
private array $source_type = [ private array $source_type = [
'CARD' => PaymentType::CREDIT_CARD_OTHER, 'CARD' => PaymentType::CREDIT_CARD_OTHER,
'BANK_ACCOUNT' => PaymentType::ACH, 'BANK_ACCOUNT' => PaymentType::ACH,
@ -98,22 +104,56 @@ class SquareWebhook implements ShouldQueue
MultiDB::findAndSetDbByCompanyKey($this->company_key); MultiDB::findAndSetDbByCompanyKey($this->company_key);
$this->company_gateway = CompanyGateway::withTrashed()->find($this->company_gateway_id); $this->company_gateway = CompanyGateway::queyr()->withTrashed()->find($this->company_gateway_id);
$this->driver = $this->company_gateway->driver()->init();
$this->square = $this->driver->square;
$status = $this->webhook_array['data']['object']['payment']['status'] ?? false; $status = $this->webhook_array['data']['object']['payment']['status'] ?? false;
$payment_id = $this->webhook_array['data']['object']['payment']['id'] ?? null; $payment_id = $this->webhook_array['data']['object']['payment']['id'] ?? null;
match($status){ match($status){
'APPROVED' => $payment_status = Payment::STATUS_COMPLETED, 'APPROVED' => $payment_status = false,
'COMPLETED' => $payment_status = Payment::STATUS_COMPLETED, 'COMPLETED' => $payment_status = Payment::STATUS_COMPLETED,
'PENDING' => $payment_status = Payment::STATUS_PENDING, 'PENDING' => $payment_status = Payment::STATUS_PENDING,
'CANCELED' => $payment_status = Payment::STATUS_CANCELLED, 'CANCELED' => $payment_status = Payment::STATUS_CANCELLED,
'FAILED' => $payment_status = Payment::STATUS_FAILED, 'FAILED' => $payment_status = Payment::STATUS_FAILED,
default => $payment_status = Payment::STATUS_FAILED, default => $payment_status = false,
}; };
if(!$payment_status){
nlog("Square Webhook - Payment Status Not Found or not worthy of processing");
nlog($this->webhook_array);
}
$payment = $this->retrieveOrCreatePayment($payment_id, $payment_status); $payment = $this->retrieveOrCreatePayment($payment_id, $payment_status);
/** If the status was pending and now is reporting as Failed / Cancelled - process failure path */
if($payment->status_id == Payment::STATUS_PENDING && in_array($payment_status, [Payment::STATUS_CANCELLED, Payment::STATUS_FAILED])){
$payment->service()->deletePayment();
if ($this->driver->payment_hash) {
$error = ctrans('texts.client_payment_failure_body', [
'invoice' => implode(',', $payment->invoices->pluck('number')->toArray()),
'amount' => array_sum(array_column($this->driver->payment_hash->invoices(), 'amount')) + $this->driver->payment_hash->fee_total,
]);
} 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
);
}
elseif($payment->status_id == Payment::STATUS_PENDING && in_array($payment_status, [Payment::STATUS_COMPLETED, Payment::STATUS_COMPLETED])){
$payment->status_id = Payment::STATUS_COMPLETED;
$payment->save();
}
//toggle pending to completed. //toggle pending to completed.
} }
@ -125,8 +165,8 @@ class SquareWebhook implements ShouldQueue
if($payment) if($payment)
return $payment; return $payment;
$square = $this->company_gateway->driver()->init(); /** Handles the edge case where for some reason the payment has not yet been recorded in Invoice Ninja */
$apiResponse = $square->getPaymentsApi()->getPayment($payment_reference); $apiResponse = $this->square->getPaymentsApi()->getPayment($payment_reference);
// { // {
// "payment": { // "payment": {
@ -201,13 +241,13 @@ class SquareWebhook implements ShouldQueue
$payment_hash_id = $apiResponse->getPayment()->getReferenceId() ?? false; $payment_hash_id = $apiResponse->getPayment()->getReferenceId() ?? false;
$square_payment = $apiResponse->getPayment()->jsonSerialize(); $square_payment = $apiResponse->getPayment()->jsonSerialize();
$payment_hash = PaymentHash::where('hash',$payment_hash_id)->first(); $payment_hash = PaymentHash::where('hash', $payment_hash_id)->firstOrFail();
$payment_hash->data = array_merge((array) $payment_hash->data, (array)$square_payment); $payment_hash->data = array_merge((array) $payment_hash->data, (array)$square_payment);
$payment_hash->save(); $payment_hash->save();
$square->setPaymentHash($payment_hash); $this->driver->setPaymentHash($payment_hash);
$square->setClient($payment_hash->fee_invoice->client); $this->driver->setClient($payment_hash->fee_invoice->client);
$data = [ $data = [
'payment_type' => $this->source_type[$square_payment->source_type], 'payment_type' => $this->source_type[$square_payment->source_type],
@ -216,22 +256,23 @@ class SquareWebhook implements ShouldQueue
'gateway_type_id' => GatewayType::BANK_TRANSFER, 'gateway_type_id' => GatewayType::BANK_TRANSFER,
]; ];
$payment = $square->createPayment($data, $payment_status); $payment = $this->driver->createPayment($data, $payment_status);
SystemLogger::dispatch( SystemLogger::dispatch(
['response' => $this->webhook_array, 'data' => $square_payment], ['response' => $this->webhook_array, 'data' => $square_payment],
SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::CATEGORY_GATEWAY_RESPONSE,
SystemLog::EVENT_GATEWAY_SUCCESS, SystemLog::EVENT_GATEWAY_SUCCESS,
SystemLog::TYPE_SQUARE, SystemLog::TYPE_SQUARE,
$square->client, $this->driver->client,
$square->client->company, $this->driver->client->company,
); );
return $payment; return $payment;
} }
else{ else{
nlog("Square Webhook Payment not found: $payment_reference"); nlog("Square Webhook - Payment not found: $payment_reference");
nlog($apiResponse->getErrors());
} }
} }
} }

View File

@ -25,6 +25,7 @@ use App\Models\ClientGatewayToken;
use Square\Models\WebhookSubscription; use Square\Models\WebhookSubscription;
use App\PaymentDrivers\Square\CreditCard; use App\PaymentDrivers\Square\CreditCard;
use App\PaymentDrivers\Square\SquareWebhook; use App\PaymentDrivers\Square\SquareWebhook;
use Square\Models\ListWebhookSubscriptionsRequest;
use Square\Models\CreateWebhookSubscriptionRequest; use Square\Models\CreateWebhookSubscriptionRequest;
use App\Http\Requests\Payments\PaymentWebhookRequest; use App\Http\Requests\Payments\PaymentWebhookRequest;
use Square\Models\Builders\RefundPaymentRequestBuilder; use Square\Models\Builders\RefundPaymentRequestBuilder;
@ -285,15 +286,64 @@ class SquarePaymentDriver extends BaseDriver
return false; return false;
} }
public function createWebhooks() public function checkWebhooks(): bool
{ {
$this->init(); $this->init();
$api_response = $this->square->getWebhookSubscriptionsApi()->listWebhookSubscriptions();
if ($api_response->isSuccess()) {
//array of WebhookSubscription objects
foreach($api_response->getResult()->getSubscriptions() as $subscription)
{
if($subscription->getName() == 'Invoice_Ninja_Webhook_Subscription')
return true;
}
} else {
$errors = $api_response->getErrors();
nlog($errors);
}
return false;
}
// {
// "subscription": {
// "id": "wbhk_b35f6b3145074cf9ad513610786c19d5",
// "name": "Example Webhook Subscription",
// "enabled": true,
// "event_types": [
// "payment.created",
// "order.updated",
// "invoice.created"
// ],
// "notification_url": "https://example-webhook-url.com",
// "api_version": "2021-12-15",
// "signature_key": "1k9bIJKCeTmSQwyagtNRLg",
// "created_at": "2022-08-17 23:29:48 +0000 UTC",
// "updated_at": "2022-08-17 23:29:48 +0000 UTC"
// }
// }
public function createWebhooks(): void
{
if($this->checkWebhooks())
return;
$this->init();
$event_types = ['payment.created', 'payment.updated']; $event_types = ['payment.created', 'payment.updated'];
$subscription = new WebhookSubscription(); $subscription = new WebhookSubscription();
$subscription->setName('Invoice_Ninja_Webhook_Subscription'); $subscription->setName('Invoice_Ninja_Webhook_Subscription');
$subscription->setEventTypes($event_types); $subscription->setEventTypes($event_types);
$subscription->setNotificationUrl($this->company_gateway->webhookUrl());
$subscription->setNotificationUrl('https://invoicing.co');
// $subscription->setNotificationUrl($this->company_gateway->webhookUrl());
// $subscription->setApiVersion('2021-12-15'); // $subscription->setApiVersion('2021-12-15');
$body = new CreateWebhookSubscriptionRequest($subscription); $body = new CreateWebhookSubscriptionRequest($subscription);
@ -301,28 +351,15 @@ class SquarePaymentDriver extends BaseDriver
$api_response = $this->square->getWebhookSubscriptionsApi()->createWebhookSubscription($body); $api_response = $this->square->getWebhookSubscriptionsApi()->createWebhookSubscription($body);
// {
// "subscription": {
// "id": "wbhk_b35f6b3145074cf9ad513610786c19d5",
// "name": "Example Webhook Subscription",
// "enabled": true,
// "event_types": [
// "payment.created",
// "order.updated",
// "invoice.created"
// ],
// "notification_url": "https://example-webhook-url.com",
// "api_version": "2021-12-15",
// "signature_key": "1k9bIJKCeTmSQwyagtNRLg",
// "created_at": "2022-08-17 23:29:48 +0000 UTC",
// "updated_at": "2022-08-17 23:29:48 +0000 UTC"
// }
// }
if ($api_response->isSuccess()) { if ($api_response->isSuccess()) {
$result = $api_response->getResult(); $subscription = $api_response->getResult()->getSubscription();
$signatureKey = $subscription->getSignatureKey();
$this->company_gateway->setConfigField('signatureKey', $signatureKey);
} else { } else {
$errors = $api_response->getErrors(); $errors = $api_response->getErrors();
nlog($errors);
} }
} }