Upgrades for Square

This commit is contained in:
David Bomba 2023-08-19 12:23:37 +10:00
parent 5bb40840d9
commit cb9c3a0a77
6 changed files with 249 additions and 15 deletions

View File

@ -17,7 +17,7 @@ use Illuminate\Database\Eloquent\Model;
* App\Models\PaymentHash
*
* @property int $id
* @property string $hash
* @property string $hash 32 char length AlphaNum
* @property float $fee_total
* @property int|null $fee_invoice_id
* @property \stdClass $data
@ -41,6 +41,7 @@ class PaymentHash extends Model
/**
* @class \App\Models\PaymentHash $this
* @property \App\Models\PaymentHash $data
* @property \App\Modes\PaymentHash $hash 32 char length AlphaNum
* @class \stdClass $data
* @property string $raw_value
*/

View File

@ -263,6 +263,8 @@ class BaseDriver extends AbstractPaymentDriver
public function setClient(Client $client)
{
$this->client = $client;
return $this;
}
/************************** Helper methods *************************************/

View File

@ -12,6 +12,7 @@
namespace App\PaymentDrivers\Square;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\SystemLog;
use Illuminate\View\View;
@ -107,6 +108,14 @@ class CreditCard implements MethodInterface
$token = $cgt->token;
}
$invoice = Invoice::query()->whereIn('id', $this->transformKeys(array_column($this->square_driver->payment_hash->invoices(), 'invoice_id')))->withTrashed()->first();
if ($invoice) {
$description = "Invoice {$invoice->number} for {$amount} for client {$this->square_driver->client->present()->name()}";
} else {
$description = "Payment with no invoice for amount {$amount} for client {$this->square_driver->client->present()->name()}";
}
$amount_money = new \Square\Models\Money();
$amount_money->setAmount($amount);
$amount_money->setCurrency($this->square_driver->client->currency()->code);
@ -116,8 +125,9 @@ class CreditCard implements MethodInterface
$body->setAutocomplete(true);
$body->setLocationId($this->square_driver->company_gateway->getConfigField('locationId'));
$body->setReferenceId(Str::random(16));
$body->setReferenceId($this->square_driver->payment_hash->hash);
$body->setNote($description);
if ($request->shouldUseToken()) {
$body->setCustomerId($cgt->gateway_customer_reference);
}elseif ($request->has('verificationToken') && $request->input('verificationToken')) {

View File

@ -36,10 +36,62 @@ class SquareWebhook implements ShouldQueue
public CompanyGateway $company_gateway;
private array $source_type = [
'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,
'EXTERNAL' =>PaymentType::CREDIT_CARD_OTHER
];
public function __construct(public array $webhook_array, public string $company_key, public int $company_gateway_id)
{
}
/**
* {
* "merchant_id": "6SSW7HV8K2ST5",
* "type": "payment.created",
* "event_id": "13b867cf-db3d-4b1c-90b6-2f32a9d78124",
* "created_at": "2020-02-06T21:27:30.792Z",
* "data": {
* "type": "payment",
* "id": "KkAkhdMsgzn59SM8A89WgKwekxLZY",
* "object": {
* "payment": {
* "id": "hYy9pRFVxpDsO1FB05SunFWUe9JZY",
* "created_at": "2020-11-22T21:16:51.086Z",
* "updated_at": "2020-11-22T21:16:51.198Z",
* "amount_money": {
* "amount": 100,
* "currency": "USD"
* },
* "status": "APPROVED",
* "delay_duration": "PT168H",
* "source_type": "CARD",
* "card_details": {
* "status": "AUTHORIZED",
* "card": {
* "card_brand": "MASTERCARD",
* "last_4": "9029",
* "exp_month": 11,
* "exp_year": 2022,
* "fingerprint": "sq-1-Tvruf3vPQxlvI6n0IcKYfBukrcv6IqWr8UyBdViWXU2yzGn5VMJvrsHMKpINMhPmVg",
* "card_type": "CREDIT",
* "prepaid_type": "NOT_PREPAID",
* "bin": "540988"
* },
* "entry_method": "KEYED",
* "cvv_status": "CVV_ACCEPTED",
* "avs_status": "AVS_ACCEPTED",
* "statement_description": "SQ *DEFAULT TEST ACCOUNT",
* "card_payment_timeline": {
* "authorized_at": "2020-11-22T21:16:51.198Z"
*
*/
public function handle()
{
nlog("Square Webhook");
@ -48,6 +100,25 @@ class SquareWebhook implements ShouldQueue
$this->company_gateway = CompanyGateway::withTrashed()->find($this->company_gateway_id);
$status = $this->webhook_array['data']['object']['payment']['status'] ?? false;
$payment_id = $this->webhook_array['data']['object']['payment']['id'] ?? null;
$payment = $this->retrieveOrCreatePayment($payment_id);
// APPROVED, PENDING, COMPLETED, CANCELED, or FAILED
if(in_array($status, ['APPROVED', 'COMPLETED'])){
}
elseif(in_array($status, ['PENDING'])){
}
elseif(in_array($status, ['CANCELED', 'FAILED'])){
}
else{
nlog("Square Webhook status not handled: $status");
}
// if(!isset($this->webhook_array['type']))
// nlog("Checkout Webhook type not set");
@ -56,4 +127,122 @@ class SquareWebhook implements ShouldQueue
// };
}
private function retrieveOrCreatePayment(?string $payment_reference): \App\Models\Payment
{
$payment = Payment::withTrashed()->where('transaction_reference', $payment_reference)->first();
if($payment)
return $payment;
$square = $this->company_gateway->driver()->init();
$apiResponse = $square->getPaymentsApi()->getPayment($payment_reference);
// {
// "payment": {
// "id": "bP9mAsEMYPUGjjGNaNO5ZDVyLhSZY",
// "created_at": "2021-10-13T19:34:33.524Z",
// "updated_at": "2021-10-13T19:34:34.339Z",
// "amount_money": {
// "amount": 555,
// "currency": "USD"
// },
// "status": "COMPLETED",
// "delay_duration": "PT168H",
// "source_type": "CARD",
// "card_details": {
// "status": "CAPTURED",
// "card": {
// "card_brand": "VISA",
// "last_4": "1111",
// "exp_month": 11,
// "exp_year": 2022,
// "fingerprint": "sq-1-Hxim77tbdcbGejOejnoAklBVJed2YFLTmirfl8Q5XZzObTc8qY_U8RkwzoNL8dCEcQ",
// "card_type": "DEBIT",
// "prepaid_type": "NOT_PREPAID",
// "bin": "411111"
// },
// "entry_method": "KEYED",
// "cvv_status": "CVV_ACCEPTED",
// "avs_status": "AVS_ACCEPTED",
// "auth_result_code": "2Nkw7q",
// "statement_description": "SQ *EXAMPLE TEST GOSQ.C",
// "card_payment_timeline": {
// "authorized_at": "2021-10-13T19:34:33.680Z",
// "captured_at": "2021-10-13T19:34:34.340Z"
// }
// },
// "location_id": "L88917AVBK2S5",
// "order_id": "d7eKah653Z579f3gVtjlxpSlmUcZY",
// "processing_fee": [
// {
// "effective_at": "2021-10-13T21:34:35.000Z",
// "type": "INITIAL",
// "amount_money": {
// "amount": 34,
// "currency": "USD"
// }
// }
// ],
// "note": "Test Note",
// "total_money": {
// "amount": 555,
// "currency": "USD"
// },
// "approved_money": {
// "amount": 555,
// "currency": "USD"
// },
// "employee_id": "TMoK_ogh6rH1o4dV",
// "receipt_number": "bP9m",
// "receipt_url": "https://squareup.com/receipt/preview/bP9mAsEMYPUGjjGNaNO5ZDVyLhSZY",
// "delay_action": "CANCEL",
// "delayed_until": "2021-10-20T19:34:33.524Z",
// "team_member_id": "TMoK_ogh6rH1o4dV",
// "application_details": {
// "square_product": "VIRTUAL_TERMINAL",
// "application_id": "sq0ids-Pw67AZAlLVB7hsRmwlJPuA"
// },
// "version_token": "56pRkL3slrzet2iQrTp9n0bdJVYTB9YEWdTNjQfZOPV6o"
// }
// }
if($apiResponse->isSuccess()){
$payment_hash_id = $apiResponse->getPayment()->getReferenceId() ?? false;
$square_payment = $apiResponse->getPayment()->jsonSerialize();
$payment_hash = PaymentHash::where('hash',$payment_hash_id)->first();
$payment_hash->data = array_merge((array) $payment_hash->data, (array)$square_payment);
$payment_hash->save();
$square->setPaymentHash($payment_hash);
$square->setClient($payment_hash->fee_invoice->client);
$data = [
'payment_type' => $this->source_type[$square_payment->source_type],
'amount' => $payment_hash->amount_with_fee,
'transaction_reference' => $square_payment->id,
'gateway_type_id' => GatewayType::BANK_TRANSFER,
];
$payment = $square->createPayment($data, Payment::STATUS_COMPLETED);
SystemLogger::dispatch(
['response' => $this->webhook_array, 'data' => $square_payment],
SystemLog::CATEGORY_GATEWAY_RESPONSE,
SystemLog::EVENT_GATEWAY_SUCCESS,
SystemLog::TYPE_SQUARE,
$square->client,
$square->client->company,
);
return $payment;
}
else{
nlog("Square Webhook Payment not found: $payment_reference");
}
}
}

View File

@ -20,8 +20,12 @@ use App\Models\PaymentType;
use Square\Http\ApiResponse;
use App\Jobs\Util\SystemLogger;
use App\Utils\Traits\MakesHash;
use Square\Utils\WebhooksHelper;
use App\Models\ClientGatewayToken;
use Square\Models\WebhookSubscription;
use App\PaymentDrivers\Square\CreditCard;
use App\PaymentDrivers\Square\SquareWebhook;
use Square\Models\CreateWebhookSubscriptionRequest;
use App\Http\Requests\Payments\PaymentWebhookRequest;
use Square\Models\Builders\RefundPaymentRequestBuilder;
@ -230,6 +234,8 @@ class SquarePaymentDriver extends BaseDriver
$body = new \Square\Models\CreatePaymentRequest($cgt->token, \Illuminate\Support\Str::random(32), $amount_money);
$body->setCustomerId($cgt->gateway_customer_reference);
$body->setAmountMoney($amount_money);
$body->setReferenceId($payment_hash->hash);
$body->setNote(substr($description,0,500));
/** @var ApiResponse */
$response = $this->square->getPaymentsApi()->createPayment($body);
@ -284,17 +290,35 @@ class SquarePaymentDriver extends BaseDriver
$this->init();
$event_types = ['payment.created', 'payment.updated'];
$subscription = new \Square\Models\WebhookSubscription();
$subscription->setName('Invoice Ninja Webhook Subscription');
$subscription = new WebhookSubscription();
$subscription->setName('Invoice_Ninja_Webhook_Subscription');
$subscription->setEventTypes($event_types);
$subscription->setNotificationUrl($this->company_gateway->webhookUrl());
// $subscription->setApiVersion('2021-12-15');
$body = new \Square\Models\CreateWebhookSubscriptionRequest($subscription);
$body = new CreateWebhookSubscriptionRequest($subscription);
$body->setIdempotencyKey(\Illuminate\Support\Str::uuid());
$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()) {
$result = $api_response->getResult();
} else {
@ -303,21 +327,29 @@ class SquarePaymentDriver extends BaseDriver
}
public function processWebhookRequest(PaymentWebhookRequest $request, Payment $payment = null)
public function processWebhookRequest(PaymentWebhookRequest $request)
{
$signature_key = $this->company_gateway->getConfigField('signatureKey');
$notification_url = $this->company_gateway->webhookUrl();
// header('Content-Type: text/plain');
// $webhook_payload = file_get_contents('php://input');
// if($request->header('cko-signature') == hash_hmac('sha256', $webhook_payload, $this->company_gateway->company->company_key)) {
// CheckoutWebhook::dispatch($request->all(), $request->company_key, $this->company_gateway->id)->delay(10);
// } else {
// nlog("Hash Mismatch = {$request->header('cko-signature')} ".hash_hmac('sha256', $webhook_payload, $this->company_gateway->company->company_key));
// nlog($request->all());
// }
$body = '';
$handle = fopen('php://input', 'r');
while(!feof($handle)) {
$body .= fread($handle, 1024);
}
// return response()->json(['success' => true]);
if (WebhooksHelper::isValidWebhookEventSignature($body, $request->header('x-square-hmacsha256-signature'), $signature_key, $notification_url)) {
SquareWebhook::dispatch($request->all(), $request->company_key, $this->company_gateway->id)->delay(5);
} else {
nlog("Square Hash Mismatch");
nlog($request->all());
}
return response()->json(['success' => true]);
}

View File

@ -80,7 +80,7 @@ class PaymentLibrariesSeeder extends Seeder
['id' => 53, 'name' => 'PagSeguro', 'provider' => 'PagSeguro', 'key' => 'ef498756b54db63c143af0ec433da803', 'fields' => '{"email":"","token":"","sandbox":false}'],
['id' => 54, 'name' => 'PAYMILL', 'provider' => 'Paymill', 'key' => 'ca52f618a39367a4c944098ebf977e1c', 'fields' => '{"apiKey":""}'],
['id' => 55, 'name' => 'Custom', 'provider' => 'Custom', 'is_offsite' => true, 'sort_order' => 21, 'key' => '54faab2ab6e3223dbe848b1686490baa', 'fields' => '{"name":"","text":""}'],
['id' => 57, 'name' => 'Square', 'provider' => 'Square', 'is_offsite' => false, 'sort_order' => 21, 'key' => '65faab2ab6e3223dbe848b1686490baz', 'fields' => '{"accessToken":"","applicationId":"","locationId":"","testMode":false}'],
['id' => 57, 'name' => 'Square', 'provider' => 'Square', 'is_offsite' => false, 'sort_order' => 21, 'key' => '65faab2ab6e3223dbe848b1686490baz', 'fields' => '{"accessToken":"","applicationId":"","locationId":"","signatureKey":"","testMode":false}'],
['id' => 58, 'name' => 'Razorpay', 'provider' => 'Razorpay', 'is_offsite' => false, 'sort_order' => 21, 'key' => 'hxd6gwg3ekb9tb3v9lptgx1mqyg69zu9', 'fields' => '{"apiKey":"","apiSecret":""}'],
['id' => 59, 'name' => 'Forte', 'provider' => 'Forte', 'is_offsite' => false, 'sort_order' => 21, 'key' => 'kivcvjexxvdiyqtj3mju5d6yhpeht2xs', 'fields' => '{"testMode":false,"apiLoginId":"","apiAccessId":"","secureKey":"","authOrganizationId":"","organizationId":"","locationId":""}'],
['id' => 60, 'name' => 'PayPal REST', 'provider' => 'PayPal_Rest', 'key' => '80af24a6a691230bbec33e930ab40665', 'fields' => '{"clientId":"","secret":"","signature":"","testMode":false}'],