diff --git a/app/PaymentDrivers/BaseDriver.php b/app/PaymentDrivers/BaseDriver.php index ff1807f71965..1b83db02500a 100644 --- a/app/PaymentDrivers/BaseDriver.php +++ b/app/PaymentDrivers/BaseDriver.php @@ -231,7 +231,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..62d5676a3a70 100644 --- a/app/PaymentDrivers/CheckoutCom/Utilities.php +++ b/app/PaymentDrivers/CheckoutCom/Utilities.php @@ -81,7 +81,7 @@ trait Utilities 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 +103,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..ed38ba8abd8c 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,95 @@ 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->hash]); + + //$this->setPaymentHash($payment_hash); + + try { + $response = $this->gateway->payments()->request($payment); + + if ($response->status == 'Authorized') { + $this->confirmGatewayFee($request); + + $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') { + $this->unWindGatewayFees($payment_hash); + + PaymentFailureMailer::dispatch( + $this->client, $response->response_summary, + $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); + } } public function processWebhookRequest(PaymentWebhookRequest $request, Payment $payment = null) diff --git a/app/Services/Invoice/AutoBillInvoice.php b/app/Services/Invoice/AutoBillInvoice.php index 1718c5a7e243..3be132848d3b 100644 --- a/app/Services/Invoice/AutoBillInvoice.php +++ b/app/Services/Invoice/AutoBillInvoice.php @@ -48,11 +48,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); $this->invoice = $this->invoice->service()->addGatewayFee($gateway_token->gateway, $gateway_token->gateway_type_id, $amount)->save(); @@ -261,7 +261,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(); @@ -280,18 +280,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