From d7277b7c6db032200c582922cbd2af0ae9f8aa6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Wed, 27 Jan 2021 15:58:19 +0100 Subject: [PATCH 1/3] wip --- app/PaymentDrivers/BaseDriver.php | 2 +- app/PaymentDrivers/CheckoutCom/Utilities.php | 12 +++- .../CheckoutComPaymentDriver.php | 65 +++++++++++++++++-- 3 files changed, 69 insertions(+), 10 deletions(-) diff --git a/app/PaymentDrivers/BaseDriver.php b/app/PaymentDrivers/BaseDriver.php index 16f2c3442cb5..5a6e4b751744 100644 --- a/app/PaymentDrivers/BaseDriver.php +++ b/app/PaymentDrivers/BaseDriver.php @@ -230,7 +230,7 @@ class BaseDriver extends AbstractPaymentDriver $payment->service()->updateInvoicePayment($this->payment_hash); - if ($this->client->getSetting('client_online_payment_notification')) + if ($this->client->getSetting('client_online_payment_notification')) $payment->service()->sendEmail(); event(new PaymentWasCreated($payment, $payment->company, Ninja::eventVars())); diff --git a/app/PaymentDrivers/CheckoutCom/Utilities.php b/app/PaymentDrivers/CheckoutCom/Utilities.php index cc7427f60267..2ac8e27b8ce4 100644 --- a/app/PaymentDrivers/CheckoutCom/Utilities.php +++ b/app/PaymentDrivers/CheckoutCom/Utilities.php @@ -55,7 +55,7 @@ trait Utilities return round($amount * 100); } - private function processSuccessfulPayment(Payment $_payment) + private function processSuccessfulPayment(Payment $_payment, $return_payment = false) { if ($this->getParent()->payment_hash->data->store_card) { $this->storePaymentMethod($_payment); @@ -78,10 +78,14 @@ trait Utilities $this->getParent()->client ); + if ($return_payment) { + return $payment; + } + return redirect()->route('client.payments.show', ['payment' => $this->getParent()->encodePrimaryKey($payment->id)]); } - public function processUnsuccessfulPayment(Payment $_payment) + public function processUnsuccessfulPayment(Payment $_payment, $throw_exception = true) { PaymentFailureMailer::dispatch( $this->getParent()->client, @@ -103,7 +107,9 @@ trait Utilities $this->getParent()->client ); - throw new PaymentFailed($_payment->status, $_payment->http_code); + if ($throw_exception) { + throw new PaymentFailed($_payment->status, $_payment->http_code); + } } private function processPendingPayment(Payment $_payment) diff --git a/app/PaymentDrivers/CheckoutComPaymentDriver.php b/app/PaymentDrivers/CheckoutComPaymentDriver.php index 1a58002f58b8..0e51959dd395 100644 --- a/app/PaymentDrivers/CheckoutComPaymentDriver.php +++ b/app/PaymentDrivers/CheckoutComPaymentDriver.php @@ -12,18 +12,24 @@ namespace App\PaymentDrivers; +use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest; use App\Http\Requests\Payments\PaymentWebhookRequest; +use App\Jobs\Mail\PaymentFailureMailer; +use App\Jobs\Util\SystemLogger; use App\Models\ClientGatewayToken; use App\Models\Company; use App\Models\GatewayType; +use App\Models\Invoice; use App\Models\Payment; use App\Models\PaymentHash; +use App\Models\PaymentType; use App\Models\SystemLog; use App\PaymentDrivers\CheckoutCom\CreditCard; use App\PaymentDrivers\CheckoutCom\Utilities; use App\Utils\Traits\SystemLogTrait; use Checkout\CheckoutApi; use Checkout\Library\Exceptions\CheckoutHttpException; +use Checkout\Models\Payments\IdSource; use Checkout\Models\Payments\Refund; use Exception; @@ -96,8 +102,8 @@ class CheckoutComPaymentDriver extends BaseDriver public function init() { $config = [ - 'secret' => $this->company_gateway->getConfigField('secretApiKey'), - 'public' => $this->company_gateway->getConfigField('publicApiKey'), + 'secret' => $this->company_gateway->getConfigField('secretApiKey'), + 'public' => $this->company_gateway->getConfigField('publicApiKey'), 'sandbox' => $this->company_gateway->getConfigField('testMode'), ]; @@ -108,7 +114,7 @@ class CheckoutComPaymentDriver extends BaseDriver /** * Process different view depending on payment type - * @param int $gateway_type_id The gateway type + * @param int $gateway_type_id The gateway type * @return string The view string */ public function viewForType($gateway_type_id) @@ -132,7 +138,7 @@ class CheckoutComPaymentDriver extends BaseDriver /** * Payment View * - * @param array $data Payment data array + * @param array $data Payment data array * @return view The payment view */ public function processPaymentView(array $data) @@ -143,7 +149,7 @@ class CheckoutComPaymentDriver extends BaseDriver /** * Process the payment response * - * @param Request $request The payment request + * @param Request $request The payment request * @return view The payment response view */ public function processPaymentResponse($request) @@ -188,7 +194,54 @@ class CheckoutComPaymentDriver extends BaseDriver public function tokenBilling(ClientGatewayToken $cgt, PaymentHash $payment_hash) { - // .. + $amount = array_sum(array_column($payment_hash->invoices(), 'amount')) + $payment_hash->fee_total; + $invoice = Invoice::whereIn('id', $this->transformKeys(array_column($payment_hash->invoices(), 'invoice_id')))->first(); + + if ($invoice) { + $description = "Invoice {$invoice->number} for {$amount} for client {$this->client->present()->name()}"; + } else { + $description = "Payment with no invoice for amount {$amount} for client {$this->client->present()->name()}"; + } + + $this->init(); + + $method = new IdSource($cgt->token); + + $payment = new \Checkout\Models\Payments\Payment($method, $this->client->getCurrencyCode()); + $payment->amount = $this->convertToCheckoutAmount($amount, $this->client->getCurrencyCode()); + $payment->reference = $cgt->meta->last4 . '-' . now(); + + $request = new PaymentResponseRequest(); + $request->setMethod('POST'); + $request->request->add(['payment_hash' => $payment_hash]); + + try { + $response = $this->gateway->payments()->request($payment); + + if ($response->status == 'Authorized') { + $this->confirmGatewayFee($request); + + return $this->processSuccessfulPayment($response, true); + } + + if ($response->status == 'Declined') { + $this->unWindGatewayFees($payment_hash); + + PaymentFailureMailer::dispatch( + $this->client, $response->response_summary, + $this->client->company, + $this->payment_hash->data->value + ); + + $this->processUnsuccessfulPayment($response, false); + + return false; + } + } catch (\Exception | CheckoutHttpException $e) { + $this->unWindGatewayFees($payment_hash); + + // .. + } } public function processWebhookRequest(PaymentWebhookRequest $request, Payment $payment = null) From 8bbfbbb9e1191cfbd572eb53b9e7ca989b29145e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Thu, 28 Jan 2021 16:13:32 +0100 Subject: [PATCH 2/3] checkout token billing --- app/PaymentDrivers/CheckoutCom/Utilities.php | 6 +- .../CheckoutComPaymentDriver.php | 58 +++++++++++++++++-- 2 files changed, 54 insertions(+), 10 deletions(-) diff --git a/app/PaymentDrivers/CheckoutCom/Utilities.php b/app/PaymentDrivers/CheckoutCom/Utilities.php index 2ac8e27b8ce4..62d5676a3a70 100644 --- a/app/PaymentDrivers/CheckoutCom/Utilities.php +++ b/app/PaymentDrivers/CheckoutCom/Utilities.php @@ -55,7 +55,7 @@ trait Utilities return round($amount * 100); } - private function processSuccessfulPayment(Payment $_payment, $return_payment = false) + private function processSuccessfulPayment(Payment $_payment) { if ($this->getParent()->payment_hash->data->store_card) { $this->storePaymentMethod($_payment); @@ -78,10 +78,6 @@ trait Utilities $this->getParent()->client ); - if ($return_payment) { - return $payment; - } - return redirect()->route('client.payments.show', ['payment' => $this->getParent()->encodePrimaryKey($payment->id)]); } diff --git a/app/PaymentDrivers/CheckoutComPaymentDriver.php b/app/PaymentDrivers/CheckoutComPaymentDriver.php index 0e51959dd395..09b75625590b 100644 --- a/app/PaymentDrivers/CheckoutComPaymentDriver.php +++ b/app/PaymentDrivers/CheckoutComPaymentDriver.php @@ -213,7 +213,9 @@ class CheckoutComPaymentDriver extends BaseDriver $request = new PaymentResponseRequest(); $request->setMethod('POST'); - $request->request->add(['payment_hash' => $payment_hash]); + $request->request->add(['payment_hash' => $payment_hash->hash]); + + $this->setPaymentHash($payment_hash); try { $response = $this->gateway->payments()->request($payment); @@ -221,7 +223,24 @@ class CheckoutComPaymentDriver extends BaseDriver if ($response->status == 'Authorized') { $this->confirmGatewayFee($request); - return $this->processSuccessfulPayment($response, true); + $data = [ + 'payment_method' => $response->source['id'], + 'payment_type' => PaymentType::parseCardType(strtolower($response->source['scheme'])), + 'amount' => $amount, + 'transaction_reference' => $response->id, + ]; + + $payment = $this->createPayment($data, \App\Models\Payment::STATUS_COMPLETED); + + SystemLogger::dispatch( + ['response' => $response, 'data' => $data], + SystemLog::CATEGORY_GATEWAY_RESPONSE, + SystemLog::EVENT_GATEWAY_SUCCESS, + SystemLog::TYPE_CHECKOUT, + $this->client + ); + + return $payment; } if ($response->status == 'Declined') { @@ -230,17 +249,46 @@ class CheckoutComPaymentDriver extends BaseDriver PaymentFailureMailer::dispatch( $this->client, $response->response_summary, $this->client->company, - $this->payment_hash->data->value + $amount ); - $this->processUnsuccessfulPayment($response, false); + PaymentFailureMailer::dispatch( + $this->client, + $response, + $this->client->company, + $amount + ); + + $message = [ + 'server_response' => $response, + 'data' => $payment_hash->data, + ]; + + SystemLogger::dispatch( + $message, + SystemLog::CATEGORY_GATEWAY_RESPONSE, + SystemLog::EVENT_GATEWAY_FAILURE, + SystemLog::TYPE_CHECKOUT, + $this->client + ); return false; } } catch (\Exception | CheckoutHttpException $e) { $this->unWindGatewayFees($payment_hash); + $message = $e instanceof CheckoutHttpException + ? $e->getBody() + : $e->getMessage(); - // .. + $data = [ + 'status' => '', + 'error_type' => '', + 'error_code' => $e->getCode(), + 'param' => '', + 'message' => $message, + ]; + + SystemLogger::dispatch($data, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_CHECKOUT, $this->client); } } From dbade2f497b9c6978204c71f681681427c6237d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Fri, 29 Jan 2021 12:00:30 +0100 Subject: [PATCH 3/3] wip --- .../CheckoutComPaymentDriver.php | 9 +------- app/Services/Invoice/AutoBillInvoice.php | 21 ++++++++++--------- 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/app/PaymentDrivers/CheckoutComPaymentDriver.php b/app/PaymentDrivers/CheckoutComPaymentDriver.php index 09b75625590b..ed38ba8abd8c 100644 --- a/app/PaymentDrivers/CheckoutComPaymentDriver.php +++ b/app/PaymentDrivers/CheckoutComPaymentDriver.php @@ -215,7 +215,7 @@ class CheckoutComPaymentDriver extends BaseDriver $request->setMethod('POST'); $request->request->add(['payment_hash' => $payment_hash->hash]); - $this->setPaymentHash($payment_hash); + //$this->setPaymentHash($payment_hash); try { $response = $this->gateway->payments()->request($payment); @@ -252,13 +252,6 @@ class CheckoutComPaymentDriver extends BaseDriver $amount ); - PaymentFailureMailer::dispatch( - $this->client, - $response, - $this->client->company, - $amount - ); - $message = [ 'server_response' => $response, 'data' => $payment_hash->data, diff --git a/app/Services/Invoice/AutoBillInvoice.php b/app/Services/Invoice/AutoBillInvoice.php index 7641f6caad30..be038abb5ec7 100644 --- a/app/Services/Invoice/AutoBillInvoice.php +++ b/app/Services/Invoice/AutoBillInvoice.php @@ -49,11 +49,11 @@ class AutoBillInvoice extends AbstractService $this->invoice = $this->invoice->service()->markSent()->save(); /* Mark the invoice as paid if there is no balance */ - if ((int)$this->invoice->balance == 0) + if ((int)$this->invoice->balance == 0) return $this->invoice->service()->markPaid()->save(); - + //if the credits cover the payments, we stop here, build the payment with credits and exit early - if ($this->client->getSetting('use_credits_payment') != 'off') + if ($this->client->getSetting('use_credits_payment') != 'off') $this->applyCreditPayment(); /* Determine $amount */ @@ -70,9 +70,9 @@ class AutoBillInvoice extends AbstractService $gateway_token = $this->getGateway($amount); /* Bail out if no payment methods available */ - if (! $gateway_token || ! $gateway_token->gateway->driver($this->client)->token_billing) + if (! $gateway_token || ! $gateway_token->gateway->driver($this->client)->token_billing) return $this->invoice; - + /* $gateway fee */ $fee = $gateway_token->gateway->calcGatewayFee($amount, $gateway_token->gateway_type_id, $this->invoice->uses_inclusive_taxes); @@ -86,6 +86,7 @@ class AutoBillInvoice extends AbstractService $payment = $gateway_token->gateway ->driver($this->client) + ->setPaymentHash($payment_hash) ->tokenBilling($gateway_token, $payment_hash); return $this->invoice; @@ -257,7 +258,7 @@ class AutoBillInvoice extends AbstractService * @param float $amount The amount to charge * @return ClientGatewayToken The client gateway token */ - // private function + // private function // { // $gateway_tokens = $this->client->gateway_tokens()->orderBy('is_default', 'DESC')->get(); @@ -276,18 +277,18 @@ class AutoBillInvoice extends AbstractService $gateway_tokens = $this->client->gateway_tokens; $filtered_gateways = $gateway_tokens->filter(function ($gateway_token) use($amount) { - + $company_gateway = $gateway_token->gateway; //check if fees and limits are set - if (isset($company_gateway->fees_and_limits) && property_exists($company_gateway->fees_and_limits, $gateway_token->gateway_type_id)) + if (isset($company_gateway->fees_and_limits) && property_exists($company_gateway->fees_and_limits, $gateway_token->gateway_type_id)) { //if valid we keep this gateway_token - if ($this->invoice->client->validGatewayForAmount($company_gateway->fees_and_limits->{$gateway_token->gateway_type_id}, $amount)) + if ($this->invoice->client->validGatewayForAmount($company_gateway->fees_and_limits->{$gateway_token->gateway_type_id}, $amount)) return true; else return false; - + } return true; //if no fees_and_limits set then we automatically must add this gateway