mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-05-24 02:14:21 -04:00
wip
This commit is contained in:
parent
99d0259365
commit
a29d4f2075
@ -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;
|
||||||
|
63
app/PaymentDrivers/Mollie/CreditCard.php
Normal file
63
app/PaymentDrivers/Mollie/CreditCard.php
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
@ -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
2
public/css/app.css
vendored
File diff suppressed because one or more lines are too long
@ -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",
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
Loading…
x
Reference in New Issue
Block a user