This commit is contained in:
Benjamin Beganović 2021-07-23 14:43:32 +02:00
parent 99d0259365
commit a29d4f2075
7 changed files with 263 additions and 6 deletions

View File

@ -68,6 +68,7 @@ class SystemLog extends Model
const TYPE_BRAINTREE = 307; const TYPE_BRAINTREE = 307;
const TYPE_WEPAY = 309; const TYPE_WEPAY = 309;
const TYPE_PAYFAST = 310; const TYPE_PAYFAST = 310;
const TYPE_MOLLIE = 311;
const TYPE_QUOTA_EXCEEDED = 400; const TYPE_QUOTA_EXCEEDED = 400;

View File

@ -0,0 +1,63 @@
<?php
namespace App\PaymentDrivers\Mollie;
use App\Exceptions\PaymentFailed;
use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest;
use App\PaymentDrivers\MolliePaymentDriver;
use Illuminate\Contracts\View\Factory;
use Illuminate\View\View;
use Illuminate\Contracts\Container\BindingResolutionException;
use function Symfony\Component\String\b;
class CreditCard
{
/**
* @var MolliePaymentDriver
*/
protected $mollie;
public function __construct(MolliePaymentDriver $mollie)
{
$this->mollie = $mollie;
$this->mollie->init();
}
/**
* Show the page for credit card payments.
*
* @param array $data
* @return Factory|View
*/
public function paymentView(array $data)
{
$data['gateway'] = $this->mollie;
return render('gateways.mollie.credit_card.pay', $data);
}
public function paymentResponse(PaymentResponseRequest $request)
{
try {
$payment = $this->mollie->gateway->payments->create([
"amount" => [
"currency" => "USD",
"value" => "10.00"
],
"description" => "Order #12345",
"redirectUrl" => "https://webshop.example.org/order/12345/",
"webhookUrl" => "https://webshop.example.org/mollie-webhook/",
]);
if ($payment->status === 'open') {
return redirect($payment->getCheckoutUrl());
}
} catch (\Exception $e) {
throw new PaymentFailed($e->getMessage(), $e->getCode());
}
dd($payment);
}
}

View File

@ -18,7 +18,9 @@ use App\Models\GatewayType;
use App\Models\Payment; use App\Models\Payment;
use App\Models\PaymentHash; use App\Models\PaymentHash;
use App\Models\SystemLog; use App\Models\SystemLog;
use App\PaymentDrivers\Mollie\CreditCard;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use Mollie\Api\MollieApiClient;
class MolliePaymentDriver extends BaseDriver class MolliePaymentDriver extends BaseDriver
{ {
@ -40,7 +42,7 @@ class MolliePaymentDriver extends BaseDriver
public $can_authorise_credit_card = true; public $can_authorise_credit_card = true;
/** /**
* @var mixed * @var MollieApiClient
*/ */
public $gateway; public $gateway;
@ -56,14 +58,19 @@ class MolliePaymentDriver extends BaseDriver
GatewayType::CREDIT_CARD => CreditCard::class, GatewayType::CREDIT_CARD => CreditCard::class,
]; ];
const SYSTEM_LOG_TYPE = SystemLog::TYPE_STRIPE; const SYSTEM_LOG_TYPE = SystemLog::TYPE_MOLLIE;
public function init() public function init(): self
{ {
$this->gateway = new MollieApiClient();
$this->gateway->setApiKey(
$this->company_gateway->getConfigField('apiKey'),
);
return $this; return $this;
} }
/* Returns an array of gateway types for the payment gateway */
public function gatewayTypes(): array public function gatewayTypes(): array
{ {
$types = []; $types = [];
@ -76,7 +83,9 @@ class MolliePaymentDriver extends BaseDriver
public function setPaymentMethod($payment_method_id) public function setPaymentMethod($payment_method_id)
{ {
$class = self::$methods[$payment_method_id]; $class = self::$methods[$payment_method_id];
$this->payment_method = new $class($this); $this->payment_method = new $class($this);
return $this; return $this;
} }

2
public/css/app.css vendored

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,6 @@
{ {
"/js/app.js": "/js/app.js?id=696e8203d5e8e7cf5ff5", "/js/app.js": "/js/app.js?id=696e8203d5e8e7cf5ff5",
"/css/app.css": "/css/app.css?id=f4c07fdabcbe50c9f4be", "/css/app.css": "/css/app.css?id=3ab7ce803b68a6e66464",
"/js/clients/invoices/action-selectors.js": "/js/clients/invoices/action-selectors.js?id=a09bb529b8e1826f13b4", "/js/clients/invoices/action-selectors.js": "/js/clients/invoices/action-selectors.js?id=a09bb529b8e1826f13b4",
"/js/clients/invoices/payment.js": "/js/clients/invoices/payment.js?id=8ce8955ba775ea5f47d1", "/js/clients/invoices/payment.js": "/js/clients/invoices/payment.js?id=8ce8955ba775ea5f47d1",
"/js/clients/linkify-urls.js": "/js/clients/linkify-urls.js?id=0dc8c34010d09195d2f7", "/js/clients/linkify-urls.js": "/js/clients/linkify-urls.js?id=0dc8c34010d09195d2f7",

View File

@ -4286,6 +4286,7 @@ $LANG = array(
'user_created_user' => ':user created :created_user at :time', 'user_created_user' => ':user created :created_user at :time',
'company_deleted' => 'Company deleted', 'company_deleted' => 'Company deleted',
'company_deleted_body' => 'Company [ :company ] was deleted by :user', 'company_deleted_body' => 'Company [ :company ] was deleted by :user',
'expiry_date' => 'Expiry date',
); );
return $LANG; return $LANG;

View File

@ -0,0 +1,183 @@
@extends('portal.ninja2020.layout.payments', ['gateway_title' => ctrans('texts.credit_card'), 'card_title' =>
ctrans('texts.credit_card')])
@section('gateway_head')
<script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
<script src="{{ asset('js/clients/payments/card-js.min.js') }}"></script>
<link href="{{ asset('css/card-js.min.css') }}" rel="stylesheet" type="text/css">
@endsection
@section('gateway_content')
<form action="{{ route('client.payments.response') }}" method="post" id="server-response">
@csrf
<input type="hidden" name="gateway_response">
<input type="hidden" name="store_card">
<input type="hidden" name="payment_hash" value="{{ $payment_hash }}">
<input type="hidden" name="company_gateway_id" value="{{ $gateway->getCompanyGatewayId() }}">
<input type="hidden" name="payment_method_id" value="{{ $payment_method_id }}">
<input type="hidden" name="token">
</form>
<div class="alert alert-failure mb-4" hidden id="errors"></div>
@component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.payment_type')])
{{ ctrans('texts.credit_card') }}
@endcomponent
@include('portal.ninja2020.gateways.includes.payment_details')
@component('portal.ninja2020.components.general.card-element-single')
<div class="flex flex-col">
<label for="card-number">
<span class="text-xs text-gray-900 uppercase">{{ ctrans('texts.card_number') }}</span>
<div class="input w-full" type="text" id="card-number"></div>
<div class="text-xs text-red-500 mt-1 block" id="card-number-error"></div>
</label>
<label for="card-holder" class="block mt-2">
<span class="text-xs text-gray-900 uppercase">{{ ctrans('texts.name') }}</span>
<div class="input w-full" type="text" id="card-holder"></div>
<div class="text-xs text-red-500 mt-1 block" id="card-holder-error"></div>
</label>
<div class="grid grid-cols-12 gap-4 mt-2">
<label for="expiry-date" class="col-span-4">
<span class="text-xs text-gray-900 uppercase">{{ ctrans('texts.expiry_date') }}</span>
<div class="input w-full" type="text" id="expiry-date"></div>
<div class="text-xs text-red-500 mt-1 block" id="expiry-date-error"></div>
</label>
<label for="cvv" class="col-span-8">
<span class="text-xs text-gray-900 uppercase">{{ ctrans('texts.cvv') }}</span>
<div class="input w-full border" type="text" id="cvv"></div>
<div class="text-xs text-red-500 mt-1 block" id="cvv-error"></div>
</label>
</div>
</div>
@endcomponent
@include('portal.ninja2020.gateways.includes.pay_now')
@endsection
@section('gateway_footer')
<script src="https://js.mollie.com/v1/mollie.js"></script>
<script>
class _Mollie {
constructor() {
this.mollie = Mollie('pfl_sFDNzkEdyw', {
testmode: true,
locale: 'en_US',
});
}
createCardHolderInput() {
let cardHolder = this.mollie.createComponent("cardHolder");
cardHolder.mount("#card-holder");
let cardHolderError = document.getElementById("card-holder-error");
cardHolder.addEventListener("change", function(event) {
if (event.error && event.touched) {
cardHolderError.textContent = event.error;
} else {
cardHolderError.textContent = "";
}
});
return this;
}
createCardNumberInput() {
let cardNumber = this.mollie.createComponent("cardNumber");
cardNumber.mount("#card-number");
let cardNumberError = document.getElementById("card-number-error");
cardNumber.addEventListener("change", function(event) {
if (event.error && event.touched) {
cardNumberError.textContent = event.error;
} else {
cardNumberError.textContent = "";
}
});
return this;
}
createExpiryDateInput() {
let expiryDate = this.mollie.createComponent("expiryDate");
expiryDate.mount("#expiry-date");
let expiryDateError = document.getElementById("expiry-date-error");
expiryDate.addEventListener("change", function(event) {
if (event.error && event.touched) {
expiryDateError.textContent = event.error;
} else {
expiryDateError.textContent = "";
}
});
return this;
}
createCvvInput() {
let verificationCode = this.mollie.createComponent("verificationCode");
verificationCode.mount("#cvv");
let verificationCodeError = document.getElementById(
"cvv-error"
);
verificationCode.addEventListener("change", function(event) {
if (event.error && event.touched) {
verificationCodeError.textContent = event.error;
} else {
verificationCodeError.textContent = "";
}
});
return this;
}
handlePayNowButton() {
document.getElementById('pay-now').disabled = true;
this.mollie.createToken().then(function(result) {
let token = result.token;
let error = result.error;
if (error) {
document.getElementById('pay-now').disabled = false;
let errorsContainer = document.getElementById('errors');
errorsContainer.innerText = error.message;
errorsContainer.hidden = false;
return;
}
document.querySelector('input[name=token]').value = token;
document.getElementById('server-response').submit();
});
}
handle() {
this
.createCardHolderInput()
.createCardNumberInput()
.createExpiryDateInput()
.createCvvInput();
document
.getElementById('pay-now')
.addEventListener('click', () => this.handlePayNowButton());
}
}
new _Mollie().handle();
</script>
@endsection