diff --git a/app/PaymentDrivers/CheckoutCom/CreditCard.php b/app/PaymentDrivers/CheckoutCom/CreditCard.php index 0a76f483750a..4859e2f1cb9f 100644 --- a/app/PaymentDrivers/CheckoutCom/CreditCard.php +++ b/app/PaymentDrivers/CheckoutCom/CreditCard.php @@ -14,9 +14,12 @@ namespace App\PaymentDrivers\CheckoutCom; use App\Exceptions\PaymentFailed; use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest; +use App\Http\Requests\Request; use App\Jobs\Mail\PaymentFailureMailer; use App\Models\ClientGatewayToken; +use App\Models\GatewayType; use App\PaymentDrivers\CheckoutComPaymentDriver; +use App\PaymentDrivers\Common\MethodInterface; use App\Utils\Traits\MakesHash; use Checkout\Library\Exceptions\CheckoutHttpException; use Checkout\Models\Payments\IdSource; @@ -25,7 +28,7 @@ use Checkout\Models\Payments\TokenSource; use Illuminate\Contracts\View\Factory; use Illuminate\View\View; -class CreditCard +class CreditCard implements MethodInterface { use Utilities; use MakesHash; @@ -38,6 +41,8 @@ class CreditCard public function __construct(CheckoutComPaymentDriver $checkout) { $this->checkout = $checkout; + + $this->checkout->init(); } /** @@ -54,15 +59,50 @@ class CreditCard } /** - * Checkout.com supports doesn't support direct authorization of the credit card. - * Token can be saved after the first (successful) purchase. + * Handle authorization for credit card. * - * @param mixed $data - * @return void + * @param Request $request + * @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse */ - public function authorizeResponse($data) + public function authorizeResponse(Request $request) { - return; + $gateway_response = \json_decode($request->gateway_response); + + $method = new TokenSource( + $gateway_response->token + ); + + $payment = new Payment($method, 'USD'); + $payment->amount = 100; // $1 + $payment->reference = '$1 payment for authorization.'; + $payment->capture = false; + + try { + $response = $this->checkout->gateway->payments()->request($payment); + + if ($response->approved && $response->status === 'Authorized') { + $payment_meta = new \stdClass; + $payment_meta->exp_month = (string) $response->source['expiry_month']; + $payment_meta->exp_year = (string) $response->source['expiry_year']; + $payment_meta->brand = (string) $response->source['scheme']; + $payment_meta->last4 = (string) $response->source['last4']; + $payment_meta->type = (int) GatewayType::CREDIT_CARD; + + $data = [ + 'payment_meta' => $payment_meta, + 'token' => $response->source['id'], + 'payment_method_id' => GatewayType::CREDIT_CARD, + ]; + + $payment_method = $this->checkout->storeGatewayToken($data); + + return redirect()->route('client.payment_methods.show', $payment_method->hashed_id); + } + } catch (CheckoutHttpException $exception) { + throw new PaymentFailed( + $exception->getMessage() + ); + } } public function paymentView($data) @@ -80,8 +120,6 @@ class CreditCard public function paymentResponse(PaymentResponseRequest $request) { - $this->checkout->init(); - $state = [ 'server_response' => json_decode($request->gateway_response), 'value' => $request->value, @@ -133,7 +171,6 @@ class CreditCard private function completePayment($method, PaymentResponseRequest $request) { - $payment = new Payment($method, $this->checkout->payment_hash->data->currency); $payment->amount = $this->checkout->payment_hash->data->value; $payment->reference = $this->checkout->getDescription(); @@ -161,7 +198,6 @@ class CreditCard $response = $this->checkout->gateway->payments()->request($payment); if ($response->status == 'Authorized') { - return $this->processSuccessfulPayment($response); } @@ -181,7 +217,6 @@ class CreditCard return $this->processUnsuccessfulPayment($response); } } catch (CheckoutHttpException $e) { - $this->checkout->unWindGatewayFees($this->checkout->payment_hash); return $this->checkout->processInternallyFailedPayment($this->checkout, $e); } diff --git a/public/js/clients/payment_methods/authorize-checkout-card.js b/public/js/clients/payment_methods/authorize-checkout-card.js new file mode 100644 index 000000000000..42b35a2db4d4 --- /dev/null +++ b/public/js/clients/payment_methods/authorize-checkout-card.js @@ -0,0 +1,2 @@ +/*! For license information please see authorize-checkout-card.js.LICENSE.txt */ +!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/",n(n.s=29)}({29:function(e,t,n){e.exports=n("kduS")},kduS:function(e,t){function n(e,t){for(var n=0;n { + this.button.disabled = !Frames.isCardValid(); + } + ); + + Frames.addEventHandler(Frames.Events.CARD_TOKENIZED, (event) => { + document.querySelector( + 'input[name="gateway_response"]' + ).value = JSON.stringify(event); + + document.getElementById('server_response').submit(); + }); + + document + .querySelector('#authorization-form') + .addEventListener('submit', (event) => { + this.button.disabled = true; + + event.preventDefault(); + Frames.submitCard(); + }); + } +} + +new CheckoutCreditCardAuthorization().handle(); diff --git a/resources/views/portal/ninja2020/gateways/checkout/credit_card/authorize.blade.php b/resources/views/portal/ninja2020/gateways/checkout/credit_card/authorize.blade.php index 9bf42c2b22a2..eb0f26e4d241 100644 --- a/resources/views/portal/ninja2020/gateways/checkout/credit_card/authorize.blade.php +++ b/resources/views/portal/ninja2020/gateways/checkout/credit_card/authorize.blade.php @@ -1,7 +1,44 @@ @extends('portal.ninja2020.layout.payments', ['gateway_title' => 'Credit card', 'card_title' => 'Credit card']) +@section('gateway_head') + + + @include('portal.ninja2020.gateways.checkout.credit_card.includes.styles') + + +@endsection + @section('gateway_content') - @component('portal.ninja2020.components.general.card-element-single', ['title' => 'Credit card', 'show_title' => false]) - {{ __('texts.checkout_authorize_label') }} +
+ @csrf + + + +
+ + @component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.method')]) + {{ ctrans('texts.credit_card') }} + @endcomponent + + @component('portal.ninja2020.components.general.card-element-single') +
+
+
+
+ +
+ + +
+

+
+
@endcomponent @endsection + +@section('gateway_footer') + +@endsection diff --git a/resources/views/portal/ninja2020/gateways/checkout/credit_card/includes/styles.blade.php b/resources/views/portal/ninja2020/gateways/checkout/credit_card/includes/styles.blade.php new file mode 100644 index 000000000000..d3a529340b49 --- /dev/null +++ b/resources/views/portal/ninja2020/gateways/checkout/credit_card/includes/styles.blade.php @@ -0,0 +1,101 @@ + diff --git a/resources/views/portal/ninja2020/gateways/checkout/credit_card/pay.blade.php b/resources/views/portal/ninja2020/gateways/checkout/credit_card/pay.blade.php index 3a2a67fc400c..4ac991261daa 100644 --- a/resources/views/portal/ninja2020/gateways/checkout/credit_card/pay.blade.php +++ b/resources/views/portal/ninja2020/gateways/checkout/credit_card/pay.blade.php @@ -7,101 +7,7 @@ - + @include('portal.ninja2020.gateways.checkout.credit_card.includes.styles') @endsection diff --git a/tests/Browser/ClientPortal/Gateways/CheckoutCom/CreditCardTest.php b/tests/Browser/ClientPortal/Gateways/CheckoutCom/CreditCardTest.php index 691779f30241..b4a242719b56 100644 --- a/tests/Browser/ClientPortal/Gateways/CheckoutCom/CreditCardTest.php +++ b/tests/Browser/ClientPortal/Gateways/CheckoutCom/CreditCardTest.php @@ -38,17 +38,6 @@ class CreditCardTest extends DuskTestCase }); } - public function testAddingPaymentMethodShouldntBePossible() - { - $this->browse(function (Browser $browser) { - $browser - ->visitRoute('client.payment_methods.index') - ->press('Add Payment Method') - ->clickLink('Credit Card') - ->assertSee('Checkout.com can be can saved as payment method for future use, once you complete your first transaction. Don\'t forget to check "Store credit card details" during payment process.'); - }); - } - public function testPayWithNewCard() { $this->browse(function (Browser $browser) { @@ -117,4 +106,22 @@ class CreditCardTest extends DuskTestCase ->assertSee('Payment method has been successfully removed.'); }); } + + public function testAddingCreditCardStandalone() + { + $this->browse(function (Browser $browser) { + $browser + ->visitRoute('client.payment_methods.index') + ->press('Add Payment Method') + ->clickLink('Credit Card') + ->withinFrame('iframe', function (Browser $browser) { + $browser + ->type('cardnumber', '4242424242424242') + ->type('exp-date', '04/22') + ->type('cvc', '100'); + }) + ->press('#pay-button') + ->waitForText('Details of payment method', 60); + }); + } } diff --git a/webpack.mix.js b/webpack.mix.js index 391a151007e9..34b60abfe3bb 100644 --- a/webpack.mix.js +++ b/webpack.mix.js @@ -114,6 +114,10 @@ mix.js("resources/js/app.js", "public/js") "resources/js/clients/payments/stripe-sepa.js", "public/js/clients/payments/stripe-sepa.js" ) + .js( + "resources/js/clients/payment_methods/authorize-checkout-card.js", + "public/js/clients/payment_methods/authorize-checkout-card.js" + ) mix.copyDirectory('node_modules/card-js/card-js.min.css', 'public/css/card-js.min.css');