From 2ed80ee6781a38d0210191e0fa579e45581ec11e Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 21 Jul 2023 21:01:22 +1000 Subject: [PATCH] Additions for checkout.com webhooks --- .../CheckoutCom/CheckoutWebhook.php | 117 ++++++++++++++ app/PaymentDrivers/CheckoutCom/Utilities.php | 13 +- app/PaymentDrivers/CheckoutCom/Webhook.php | 145 +++++++++++++++++- .../CheckoutComPaymentDriver.php | 16 +- app/Repositories/BaseRepository.php | 2 - 5 files changed, 275 insertions(+), 18 deletions(-) create mode 100644 app/PaymentDrivers/CheckoutCom/CheckoutWebhook.php diff --git a/app/PaymentDrivers/CheckoutCom/CheckoutWebhook.php b/app/PaymentDrivers/CheckoutCom/CheckoutWebhook.php new file mode 100644 index 000000000000..fcac5acdc3ac --- /dev/null +++ b/app/PaymentDrivers/CheckoutCom/CheckoutWebhook.php @@ -0,0 +1,117 @@ +company_key); + + $this->company_gateway = CompanyGateway::withTrashed()->find($this->company_gateway_id); + + if(!isset($this->webhook_array['type'])) + nlog("Checkout Webhook type not set"); + + match($this->webhook_array['type']){ + 'payment_approved' => $this->paymentApproved(), + }; + + } + + /** + * { + * "id":"evt_dli6ty4qo5vuxle5wklf5gwbwy","type":"payment_approved","version":"1.0.33","created_on":"2023-07-21T10:03:07.1555904Z", + * "data":{"id":"pay_oqwbsd22kvpuvd35y5fhbdawxa","action_id":"act_buviezur7zsurnsorcgfn63e44","reference":"0014","amount":584168,"auth_code":"113059","currency":"USD","customer":{"id":"cus_6n4yt4q5kf4unn36o5qpbevxhe","email":"cypress@example.com"}, + * "metadata":{"udf1":"Invoice Ninja","udf2":"ofhgiGjyQXbsbUwygURfYFT2C3E7iY7U"},"payment_type":"Regular","processed_on":"2023-07-21T10:02:57.4678165Z","processing":{"acquirer_transaction_id":"645272142084717830381","retrieval_reference_number":"183042259107"},"response_code":"10000","response_summary":"Approved","risk":{"flagged":false,"score":0},"3ds":{"version":"2.2.0","challenged":true,"challenge_indicator":"no_preference","exemption":"none","eci":"05","cavv":"AAABAVIREQAAAAAAAAAAAAAAAAA=","xid":"74afa3ac-25d3-4d95-b815-cefbdd7c8270","downgraded":false,"enrolled":"Y","authentication_response":"Y","flow_type":"challenged"},"scheme_id":"114455763095262", + * "source":{"id":"src_ghavmefpetjellmteqwj5jjcli","type":"card","billing_address":{},"expiry_month":10,"expiry_year":2025,"scheme":"VISA","last_4":"4242","fingerprint":"BD864B08D0B098DD83052A038FD2BA967DF2D48E375AAEEF54E37BC36B385E9A","bin":"424242","card_type":"CREDIT","card_category":"CONSUMER","issuer_country":"GB","product_id":"F","product_type":"Visa Classic","avs_check":"G","cvv_check":"Y"},"balances":{"total_authorized":584168,"total_voided":0,"available_to_void":584168,"total_captured":0,"available_to_capture":584168,"total_refunded":0,"available_to_refund":0},"event_links":{"payment":"https://api.sandbox.checkout.com/payments/pay_oqwbsd22kvpuvd35y5fhbdawxa","payment_actions":"https://api.sandbox.checkout.com/payments/pay_oqwbsd22kvpuvd35y5fhbdawxa/actions","capture":"https://api.sandbox.checkout.com/payments/pay_oqwbsd22kvpuvd35y5fhbdawxa/captures","void":"https://api.sandbox.checkout.com/payments/pay_oqwbsd22kvpuvd35y5fhbdawxa/voids"}},"_links":{"self":{"href":"https://api.sandbox.checkout.com/workflows/events/evt_dli6ty4qo5vuxle5wklf5gwbwy"},"subject":{"href":"https://api.sandbox.checkout.com/workflows/events/subject/pay_oqwbsd22kvpuvd35y5fhbdawxa"},"payment":{"href":"https://api.sandbox.checkout.com/payments/pay_oqwbsd22kvpuvd35y5fhbdawxa"},"payment_actions":{"href":"https://api.sandbox.checkout.com/payments/pay_oqwbsd22kvpuvd35y5fhbdawxa/actions"},"capture":{"href":"https://api.sandbox.checkout.com/payments/pay_oqwbsd22kvpuvd35y5fhbdawxa/captures"},"void":{"href":"https://api.sandbox.checkout.com/payments/pay_oqwbsd22kvpuvd35y5fhbdawxa/voids"}}} + */ + + private function paymentApproved() + { + $payment_object = $this->webhook_array['data']; + + $payment = Payment::withTrashed()->where('transaction_reference', $payment_object['id'])->first(); + + if($payment && $payment->status_id == Payment::STATUS_COMPLETED) + return; + + if($payment){ + $payment->status_id = Payment::STATUS_COMPLETED; + $payment->save(); + return; + } + + if(isset($this->webhook_array['metadata'])) { + + $metadata = $this->webhook_array['metadata']; + + $payment_hash = PaymentHash::where('hash', $metadata['udf2'])->first(); + + $driver = $this->company_gateway->driver($payment_hash->fee_invoice->client)->init()->setPaymentMethod(); + + $payment_hash->data = array_merge((array) $payment_hash->data, $this->webhook_array); + $payment_hash->save(); + $driver->setPaymentHash($payment_hash); + + $data = [ + 'payment_method' => isset($this->webhook_array['source']['id']) ? $this->webhook_array['source']['id'] : '', + 'payment_type' => PaymentType::CREDIT_CARD_OTHER, + 'amount' => $payment_hash->data->raw_value, + 'transaction_reference' => $payment_object['id'], + 'gateway_type_id' => GatewayType::CREDIT_CARD, + ]; + + $payment = $driver->createPayment($data, \App\Models\Payment::STATUS_COMPLETED); + + SystemLogger::dispatch( + ['response' => $this->webhook_array, 'data' => $data], + SystemLog::CATEGORY_GATEWAY_RESPONSE, + SystemLog::EVENT_GATEWAY_SUCCESS, + SystemLog::TYPE_CHECKOUT, + $payment_hash->fee_invoice->client, + $this->company_gateway->company, + ); + + } + + } +} diff --git a/app/PaymentDrivers/CheckoutCom/Utilities.php b/app/PaymentDrivers/CheckoutCom/Utilities.php index 764eb224c07e..ef37688c305a 100644 --- a/app/PaymentDrivers/CheckoutCom/Utilities.php +++ b/app/PaymentDrivers/CheckoutCom/Utilities.php @@ -12,12 +12,13 @@ namespace App\PaymentDrivers\CheckoutCom; -use App\Exceptions\PaymentFailed; -use App\Jobs\Util\SystemLogger; -use App\Models\GatewayType; -use App\Models\SystemLog; -use Exception; use stdClass; +use Exception; +use App\Models\SystemLog; +use App\Models\GatewayType; +use App\Jobs\Util\SystemLogger; +use App\Exceptions\PaymentFailed; +use Checkout\Payments\PaymentType; trait Utilities { @@ -60,7 +61,7 @@ trait Utilities $data = [ 'payment_method' => $_payment['source']['id'], - 'payment_type' => 12, + 'payment_type' => \App\Models\PaymentType::CREDIT_CARD_OTHER, 'amount' => $this->getParent()->payment_hash->data->raw_value, 'transaction_reference' => $_payment['id'], 'gateway_type_id' => GatewayType::CREDIT_CARD, diff --git a/app/PaymentDrivers/CheckoutCom/Webhook.php b/app/PaymentDrivers/CheckoutCom/Webhook.php index 5fe59b00d6e6..f25ce27b24a5 100644 --- a/app/PaymentDrivers/CheckoutCom/Webhook.php +++ b/app/PaymentDrivers/CheckoutCom/Webhook.php @@ -40,22 +40,143 @@ class Webhook $this->checkout->init(); } - public function checkStatus() - { - // $this->checkout->company_gateway->webhookUrl() - } +/* +'id' => 'pay_qw7rslcvacvubcvn6o5v7jp3ie', + 'requested_on' => '2023-07-21T05:49:29.0799437Z', + 'source' => + array ( + 'id' => 'src_epivptv65yxungkhyqt6nayiai', + 'type' => 'card', + 'phone' => + array ( + ), + 'expiry_month' => 10, + 'expiry_year' => 2025, + 'scheme' => 'Visa', + 'last4' => '4242', + 'fingerprint' => 'BD864B08D0B098DD83052A038FD2BA967DF2D48E375AAEEF54E37BC36B385E9A', + 'bin' => '424242', + 'card_type' => 'CREDIT', + 'card_category' => 'CONSUMER', + 'issuer_country' => 'GB', + 'product_id' => 'F', + 'product_type' => 'Visa Classic', + 'avs_check' => 'G', + 'cvv_check' => 'Y', + 'payment_account_reference' => 'V001726431013874807', + ), + 'expires_on' => '2023-08-20T05:50:08.7570835Z', + 'items' => + array ( + ), + 'amount' => 44520, + 'currency' => 'USD', + 'payment_type' => 'Regular', + 'reference' => '0024', + 'status' => 'Captured', + 'approved' => true, + '3ds' => + array ( + 'downgraded' => false, + 'enrolled' => 'Y', + 'authentication_response' => 'Y', + 'cryptogram' => 'AAABAVIREQAAAAAAAAAAAAAAAAA=', + 'xid' => 'e1331818-b517-439e-b186-e22bf4efbf4b', + 'version' => '2.2.0', + 'exemption' => 'none', + 'challenged' => true, + 'exemption_applied' => 'none', + ), + 'balances' => + array ( + 'total_authorized' => 44520, + 'total_voided' => 0, + 'available_to_void' => 0, + 'total_captured' => 44520, + 'available_to_capture' => 0, + 'total_refunded' => 0, + 'available_to_refund' => 44520, + ), + 'risk' => + array ( + 'flagged' => false, + 'score' => 0.0, + ), + 'customer' => + array ( + 'id' => 'cus_aarus35jqd5uddkcxqfd5gwiii', + 'email' => 'user@example.com', + 'name' => 'GBP', + ), + 'metadata' => + array ( + 'udf1' => 'Invoice Ninja', + 'udf2' => 'JUdUiwMNTV1qfSstvC0ZvUJSQVJ65DDC', + ), + 'processing' => + array ( + 'acquirer_transaction_id' => '767665093700479870728', + 'retrieval_reference_number' => '787770870837', + 'merchant_category_code' => '5815', + 'scheme_merchant_id' => '55500', + 'aft' => false, + 'cko_network_token_available' => false, + ), + 'eci' => '05', + 'scheme_id' => '420920321590206', + 'actions' => + array ( + 0 => + array ( + 'id' => 'act_c3suhqtbmpjejltr6krvuknikm', + 'type' => 'Capture', + 'response_code' => '10000', + 'response_summary' => 'Approved', + ), + 1 => + array ( + 'id' => 'act_q4tjzvgsr2yu3cfzgrfn342wei', + 'type' => 'Authorization', + 'response_code' => '10000', + 'response_summary' => 'Approved', + ), + ), + '_links' => + array ( + 'self' => + array ( + 'href' => 'https://api.sandbox.checkout.com/payments/pay_qw7rslcvacvubcvn6o5v7jp3ie', + ), + 'actions' => + array ( + 'href' => 'https://api.sandbox.checkout.com/payments/pay_qw7rslcvacvubcvn6o5v7jp3ie/actions', + ), + 'refund' => + array ( + 'href' => 'https://api.sandbox.checkout.com/payments/pay_qw7rslcvacvubcvn6o5v7jp3ie/refunds', + ), + ), +) +*/ + + /** + * Creates an authentication workflow for 3DS + * and also a registration mechanism for payments that have been approved. + * + * @return void + */ public function createAuthenticationWorkflow() { $signature = new WebhookSignature(); - $signature->key = "1234567890"; + $signature->key = $this->checkout->company_gateway->company->company_key; $signature->method = "HMACSHA256"; $actionRequest = new WebhookWorkflowActionRequest(); $actionRequest->url = $this->checkout->company_gateway->webhookUrl(); $actionRequest->signature = $signature; - + $eventWorkflowConditionRequest = new EventWorkflowConditionRequest(); $eventWorkflowConditionRequest->events = [ "gateway" => ["payment_approved"], @@ -82,10 +203,13 @@ class Webhook // Bad Invalid authorization } - - } + /** + * Lists all possible events in checkout and a brief description + * + * @return void + */ public function getEventTypes() { try { @@ -105,6 +229,11 @@ class Webhook } + /** + * Lists the workflows in Checkout + * + * @return void + */ public function getWorkFlows() { diff --git a/app/PaymentDrivers/CheckoutComPaymentDriver.php b/app/PaymentDrivers/CheckoutComPaymentDriver.php index 310c31a9afbc..6306ec230fa5 100644 --- a/app/PaymentDrivers/CheckoutComPaymentDriver.php +++ b/app/PaymentDrivers/CheckoutComPaymentDriver.php @@ -27,6 +27,7 @@ use App\Models\PaymentType; use App\Models\SystemLog; use App\PaymentDrivers\CheckoutCom\CreditCard; use App\PaymentDrivers\CheckoutCom\Utilities; +use App\PaymentDrivers\CheckoutCom\CheckoutWebhook; use App\Utils\Traits\SystemLogTrait; use Checkout\CheckoutApi; use Checkout\CheckoutApiException; @@ -421,8 +422,19 @@ class CheckoutComPaymentDriver extends BaseDriver public function processWebhookRequest(PaymentWebhookRequest $request) { - nlog($request->all()); - return true; + + 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()); + } + + return response()->json(['success' => true]); } public function process3dsConfirmation(Checkout3dsRequest $request) diff --git a/app/Repositories/BaseRepository.php b/app/Repositories/BaseRepository.php index 0350cb00b95a..15c93b5fe556 100644 --- a/app/Repositories/BaseRepository.php +++ b/app/Repositories/BaseRepository.php @@ -180,8 +180,6 @@ class BaseRepository unset($tmp_data['client_contacts']); } - nlog($tmp_data); - $model->fill($tmp_data); $model->custom_surcharge_tax1 = $client->company->custom_surcharge_taxes1;