1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-08 20:22:42 +01:00

Authorize.net: New payment flow (#68)

* fixes for validation errors

* authorize.net

* pass livewirePaymentView & processPaymentView thru base driver

* add paymentData to the interface

* authorize.net credit card
This commit is contained in:
Benjamin Beganović 2024-08-09 01:07:23 +02:00 committed by GitHub
parent 0de492d96f
commit 0fff78b0a0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 292 additions and 99 deletions

View File

@ -49,6 +49,8 @@ class RequiredFields extends Component
public bool $is_loading = true;
public array $errors = [];
public function mount(): void
{
MultiDB::setDB(
@ -103,6 +105,7 @@ class RequiredFields extends Component
public function handleSubmit(array $data)
{
$this->errors = [];
$this->is_loading = true;
$rff = new RFFService(
@ -114,9 +117,14 @@ class RequiredFields extends Component
$contact = auth()->user();
/** @var \App\Models\ClientContact $contact */
$rff->handleSubmit($data, $contact, function () {
$errors = $rff->handleSubmit($data, $contact, return_errors: true, callback: function () {
$this->dispatch('required-fields');
});
if (is_array($errors) && count($errors)) {
$this->errors = $errors;
$this->is_loading = false;
}
}
public function render(): \Illuminate\Contracts\View\Factory|\Illuminate\View\View

View File

@ -21,6 +21,7 @@ use App\Models\PaymentHash;
use App\Models\PaymentType;
use App\Models\SystemLog;
use App\PaymentDrivers\AuthorizePaymentDriver;
use App\PaymentDrivers\Common\LivewireMethodInterface;
use App\Utils\Traits\MakesHash;
use net\authorize\api\contract\v1\DeleteCustomerPaymentProfileRequest;
use net\authorize\api\contract\v1\DeleteCustomerProfileRequest;
@ -30,7 +31,7 @@ use net\authorize\api\controller\DeleteCustomerProfileController;
/**
* Class AuthorizeCreditCard.
*/
class AuthorizeCreditCard
class AuthorizeCreditCard implements LivewireMethodInterface
{
use MakesHash;
@ -41,7 +42,7 @@ class AuthorizeCreditCard
$this->authorize = $authorize;
}
public function processPaymentView($data)
public function paymentData(array $data): array
{
$tokens = ClientGatewayToken::where('client_id', $this->authorize->client->id)
->where('company_gateway_id', $this->authorize->company_gateway->id)
@ -54,6 +55,13 @@ class AuthorizeCreditCard
$data['public_client_id'] = $this->authorize->init()->getPublicClientKey();
$data['api_login_id'] = $this->authorize->company_gateway->getConfigField('apiLoginId');
return $data;
}
public function processPaymentView($data)
{
$data = $this->paymentData($data);
return render('gateways.authorize.credit_card.pay', $data);
}
@ -313,4 +321,9 @@ class AuthorizeCreditCard
'invoices' => $vars['invoices'],
];
}
public function livewirePaymentView(array $data): string
{
return 'gateways.authorize.credit_card.pay_livewire';
}
}

View File

@ -816,4 +816,14 @@ class BaseDriver extends AbstractPaymentDriver
{
}
public function livewirePaymentView(array $data): string
{
return $this->payment_method->livewirePaymentView($data);
}
public function processPaymentViewData(array $data): array
{
return $this->payment_method->paymentData($data);
}
}

View File

@ -19,4 +19,12 @@ interface LivewireMethodInterface
* @param array $data
*/
public function livewirePaymentView(array $data): string;
/**
* Payment data for the gateway method.
*
* @param array $data
* @return array
*/
public function paymentData(array $data): array;
}

View File

@ -62,7 +62,7 @@ use Stripe\SetupIntent;
use Stripe\Stripe;
use Stripe\StripeClient;
class StripePaymentDriver extends BaseDriver implements LivewireMethodInterface
class StripePaymentDriver extends BaseDriver
{
use MakesHash;
use Utilities;
@ -1041,9 +1041,4 @@ class StripePaymentDriver extends BaseDriver implements LivewireMethodInterface
return false;
}
public function livewirePaymentView(array $data): string
{
return $this->payment_method->livewirePaymentView($data);
}
}

View File

@ -88,7 +88,7 @@ class RFFService
}
}
public function handleSubmit(array $data, ClientContact $contact, callable $callback): bool
public function handleSubmit(array $data, ClientContact $contact, callable $callback, bool $return_errors = false): bool|array
{
MultiDB::setDb($this->database);
@ -105,6 +105,10 @@ class RFFService
$validator = Validator::make($data, $rules);
if ($validator->fails()) {
if ($return_errors) {
return $validator->getMessageBag()->getMessages();
}
session()->flash('validation_errors', $validator->getMessageBag()->getMessages());
return false;

45
package-lock.json generated
View File

@ -1,10 +1,11 @@
{
"name": "invoiceninja",
"name": "@invoiceninja/invoiceninja",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"dependencies": {
"@invoiceninja/simple-card": "^0.0.2",
"axios": "^0.25",
"card-js": "^1.0.13",
"card-validator": "^8.1.1",
@ -2023,6 +2024,15 @@
"purgecss": "^3.1.3"
}
},
"node_modules/@invoiceninja/simple-card": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/@invoiceninja/simple-card/-/simple-card-0.0.2.tgz",
"integrity": "sha512-xDZvfrumnE7Qkp5e4N8EFfEIOcQfMJXSI+o/xeVlTb1WvibulSBgWkIg7J0zZW0eIDvGKCpEv3k+NBpASlaJUw==",
"dependencies": {
"@maskito/core": "^3.0.0",
"@maskito/kit": "^3.0.0"
}
},
"node_modules/@isaacs/cliui": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
@ -2188,6 +2198,19 @@
"resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz",
"integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A=="
},
"node_modules/@maskito/core": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@maskito/core/-/core-3.0.0.tgz",
"integrity": "sha512-g7zeYPMlpMczrq4Huf+Bpdm3Emy/GO0NUXXnQnUiCjlAoKQl+86cLyP5Hbf4HGcNl/J9SoEGEA4uoW6uUc/yLw=="
},
"node_modules/@maskito/kit": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@maskito/kit/-/kit-3.0.0.tgz",
"integrity": "sha512-aXRlDBjeNox/+D7hbXtnM9INGml1QUIXhrnScrCsbqgg7550mt/ivh4PrxL7oazq/BH7HhvS4olJCF5TPEti1g==",
"peerDependencies": {
"@maskito/core": "^3.0.0"
}
},
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@ -12763,6 +12786,15 @@
"purgecss": "^3.1.3"
}
},
"@invoiceninja/simple-card": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/@invoiceninja/simple-card/-/simple-card-0.0.2.tgz",
"integrity": "sha512-xDZvfrumnE7Qkp5e4N8EFfEIOcQfMJXSI+o/xeVlTb1WvibulSBgWkIg7J0zZW0eIDvGKCpEv3k+NBpASlaJUw==",
"requires": {
"@maskito/core": "^3.0.0",
"@maskito/kit": "^3.0.0"
}
},
"@isaacs/cliui": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
@ -12887,6 +12919,17 @@
"resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz",
"integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A=="
},
"@maskito/core": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@maskito/core/-/core-3.0.0.tgz",
"integrity": "sha512-g7zeYPMlpMczrq4Huf+Bpdm3Emy/GO0NUXXnQnUiCjlAoKQl+86cLyP5Hbf4HGcNl/J9SoEGEA4uoW6uUc/yLw=="
},
"@maskito/kit": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@maskito/kit/-/kit-3.0.0.tgz",
"integrity": "sha512-aXRlDBjeNox/+D7hbXtnM9INGml1QUIXhrnScrCsbqgg7550mt/ivh4PrxL7oazq/BH7HhvS4olJCF5TPEti1g==",
"requires": {}
},
"@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",

View File

@ -23,6 +23,7 @@
"vue-template-compiler": "^2.6.14"
},
"dependencies": {
"@invoiceninja/simple-card": "^0.0.2",
"axios": "^0.25",
"card-js": "^1.0.13",
"card-validator": "^8.1.1",

File diff suppressed because one or more lines are too long

View File

@ -1,9 +0,0 @@
var l=Object.defineProperty;var c=(d,e,t)=>e in d?l(d,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):d[e]=t;var o=(d,e,t)=>(c(d,typeof e!="symbol"?e+"":e,t),t);/**
* Invoice Ninja (https://invoiceninja.com)
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/class s{constructor(e,t){o(this,"handleAuthorization",()=>{if(m=="1"&&document.getElementById("cvv").value.length<3){var e=$("#errors");e.show().html("<p>CVV is required</p>"),document.getElementById("pay-now").disabled=!1,document.querySelector("#pay-now > svg").classList.add("hidden"),document.querySelector("#pay-now > span").classList.remove("hidden");return}var t=$("#my-card"),n={};n.clientKey=this.publicKey,n.apiLoginID=this.loginId;var a={};a.cardNumber=t.CardJs("cardNumber").replace(/[^\d]/g,""),a.month=t.CardJs("expiryMonth").replace(/[^\d]/g,""),a.year=t.CardJs("expiryYear").replace(/[^\d]/g,""),a.cardCode=document.getElementById("cvv").value.replace(/[^\d]/g,"");var r={};return r.authData=n,r.cardData=a,document.getElementById("pay-now")&&(document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden")),Accept.dispatchData(r,this.responseHandler),!1});o(this,"responseHandler",e=>{if(e.messages.resultCode==="Error"){var t=0,n=$("#errors");n.show().html("<p>"+e.messages.message[t].code+": "+e.messages.message[t].text+"</p>"),document.getElementById("pay-now").disabled=!1,document.querySelector("#pay-now > svg").classList.add("hidden"),document.querySelector("#pay-now > span").classList.remove("hidden")}else if(e.messages.resultCode==="Ok"){document.getElementById("dataDescriptor").value=e.opaqueData.dataDescriptor,document.getElementById("dataValue").value=e.opaqueData.dataValue;let a=document.querySelector("input[name=token-billing-checkbox]:checked");a&&(document.getElementById("store_card").value=a.value),document.getElementById("server_response").submit()}return!1});o(this,"handle",()=>{Array.from(document.getElementsByClassName("toggle-payment-with-token")).forEach(n=>n.addEventListener("click",a=>{document.getElementById("save-card--container").style.display="none",document.getElementById("authorize--credit-card-container").style.display="none",document.getElementById("token").value=a.target.dataset.token}));let e=document.getElementById("toggle-payment-with-credit-card");e&&e.addEventListener("click",()=>{document.getElementById("save-card--container").style.display="grid",document.getElementById("authorize--credit-card-container").style.display="flex",document.getElementById("token").value=null});let t=document.getElementById("pay-now");return t&&t.addEventListener("click",n=>{let a=document.getElementById("token");a.value?this.handlePayNowAction(a.value):this.handleAuthorization()}),this});this.publicKey=e,this.loginId=t,this.cardHolderName=document.getElementById("cardholder_name")}handlePayNowAction(e){document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),document.getElementById("token").value=e,document.getElementById("server_response").submit()}}const u=document.querySelector('meta[name="authorize-public-key"]').content,i=document.querySelector('meta[name="authorize-login-id"]').content,m=document.querySelector('meta[name="authnet-require-cvv"]').content;new s(u,i).handle();

View File

@ -67,7 +67,10 @@
"src": "resources/js/clients/payment_methods/wepay-bank-account.js"
},
"resources/js/clients/payments/authorize-credit-card-payment.js": {
"file": "assets/authorize-credit-card-payment-a217579b.js",
"file": "assets/authorize-credit-card-payment-5206050e.js",
"imports": [
"_wait-8f4ae121.js"
],
"isEntry": true,
"src": "resources/js/clients/payments/authorize-credit-card-payment.js"
},

View File

@ -5,40 +5,65 @@
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
* @license https://www.elastic.co/licensing/elastic-license
*/
class AuthorizeAuthorizeCard {
import { SimpleCard } from '@invoiceninja/simple-card';
import { wait, instant } from '../wait';
class AuthorizeAuthorizeCard {
constructor(publicKey, loginId) {
this.publicKey = publicKey;
this.loginId = loginId;
this.cardHolderName = document.getElementById("cardholder_name");
this.cardHolderName = document.getElementById('cardholder_name');
this.sc = new SimpleCard({
fields: {
card: {
number: '#number',
date: '#date',
cvv: '#cvv',
},
},
});
this.sc.mount();
this.cvvRequired = document.querySelector(
'meta[name="authnet-require-cvv"]'
).content;
}
handleAuthorization = () => {
if (cvvRequired == "1" && document.getElementById("cvv").value.length < 3) {
var $errors = $('#errors');
$errors.show().html("<p>CVV is required</p>");
if (
this.cvvRequired == '1' &&
document.getElementById('cvv').value.length < 3
) {
const $errors = document.getElementById('errors');
if ($errors) {
$errors.innerText = 'CVV is required';
$errors.style.display = 'block';
}
document.getElementById('pay-now').disabled = false;
document.querySelector('#pay-now > svg').classList.add('hidden');
document.querySelector('#pay-now > span').classList.remove('hidden');
document
.querySelector('#pay-now > span')
.classList.remove('hidden');
return;
}
var myCard = $('#my-card');
var authData = {};
authData.clientKey = this.publicKey;
authData.apiLoginID = this.loginId;
var cardData = {};
cardData.cardNumber = myCard.CardJs('cardNumber').replace(/[^\d]/g, '');
cardData.month = myCard.CardJs('expiryMonth').replace(/[^\d]/g, '');
cardData.year = myCard.CardJs('expiryYear').replace(/[^\d]/g, '');
cardData.cardCode = document.getElementById("cvv").value.replace(/[^\d]/g, '');
cardData.cardNumber = this.sc.value('number')?.replace(/[^\d]/g, '');
cardData.month = this.sc.value('month')?.replace(/[^\d]/g, '');
cardData.year = `20${this.sc.value('year')?.replace(/[^\d]/g, '')}`;
cardData.cardCode = this.sc.value('cvv')?.replace(/[^\d]/g, '');
var secureData = {};
secureData.authData = authData;
@ -58,102 +83,112 @@ class AuthorizeAuthorizeCard {
Accept.dispatchData(secureData, this.responseHandler);
return false;
}
};
handlePayNowAction(token_hashed_id) {
document.getElementById('pay-now').disabled = true;
document.querySelector('#pay-now > svg').classList.remove('hidden');
document.querySelector('#pay-now > span').classList.add('hidden');
document.getElementById("token").value = token_hashed_id;
document.getElementById("server_response").submit();
document.getElementById('token').value = token_hashed_id;
document.getElementById('server_response').submit();
}
responseHandler = (response) => {
if (response.messages.resultCode === "Error") {
if (response.messages.resultCode === 'Error') {
var i = 0;
var $errors = $('#errors'); // get the reference of the div
$errors.show().html("<p>" + response.messages.message[i].code + ": " + response.messages.message[i].text + "</p>");
const $errors = document.getElementById('errors'); // get the reference of the div
if ($errors) {
$errors.innerText = `${response.messages.message[i].code}: ${response.messages.message[i].text}`;
$errors.style.display = 'block';
}
document.getElementById('pay-now').disabled = false;
document.querySelector('#pay-now > svg').classList.add('hidden');
document.querySelector('#pay-now > span').classList.remove('hidden');
} else if (response.messages.resultCode === "Ok") {
document
.querySelector('#pay-now > span')
.classList.remove('hidden');
} else if (response.messages.resultCode === 'Ok') {
document.getElementById('dataDescriptor').value =
response.opaqueData.dataDescriptor;
document.getElementById('dataValue').value =
response.opaqueData.dataValue;
document.getElementById("dataDescriptor").value = response.opaqueData.dataDescriptor;
document.getElementById("dataValue").value = response.opaqueData.dataValue;
let storeCard = document.querySelector('input[name=token-billing-checkbox]:checked');
let storeCard = document.querySelector(
'input[name=token-billing-checkbox]:checked'
);
if (storeCard) {
document.getElementById("store_card").value = storeCard.value;
document.getElementById('store_card').value = storeCard.value;
}
document.getElementById("server_response").submit();
document.getElementById('server_response').submit();
}
return false;
}
};
handle = () => {
Array
.from(document.getElementsByClassName('toggle-payment-with-token'))
.forEach((element) => element.addEventListener('click', (e) => {
document
.getElementById('save-card--container').style.display = 'none';
document
.getElementById('authorize--credit-card-container').style.display = 'none';
Array.from(
document.getElementsByClassName('toggle-payment-with-token')
).forEach((element) =>
element.addEventListener('click', (e) => {
document.getElementById('save-card--container').style.display =
'none';
document.getElementById(
'authorize--credit-card-container'
).style.display = 'none';
document
.getElementById('token').value = e.target.dataset.token;
}));
document.getElementById('token').value = e.target.dataset.token;
})
);
let payWithCreditCardToggle = document.getElementById('toggle-payment-with-credit-card');
let payWithCreditCardToggle = document.getElementById(
'toggle-payment-with-credit-card'
);
if (payWithCreditCardToggle) {
payWithCreditCardToggle
.addEventListener('click', () => {
document
.getElementById('save-card--container').style.display = 'grid';
document
.getElementById('authorize--credit-card-container').style.display = 'flex';
payWithCreditCardToggle.addEventListener('click', () => {
document.getElementById('save-card--container').style.display =
'grid';
document.getElementById(
'authorize--credit-card-container'
).style.display = 'flex';
document
.getElementById('token').value = null;
});
document.getElementById('token').value = null;
});
}
let payNowButton = document.getElementById('pay-now');
if (payNowButton) {
payNowButton
.addEventListener('click', (e) => {
let token = document.getElementById('token');
payNowButton.addEventListener('click', (e) => {
let token = document.getElementById('token');
token.value
? this.handlePayNowAction(token.value)
: this.handleAuthorization();
});
token.value
? this.handlePayNowAction(token.value)
: this.handleAuthorization();
});
}
return this;
}
};
}
const publicKey = document.querySelector(
'meta[name="authorize-public-key"]'
).content;
function boot() {
const publicKey = document.querySelector(
'meta[name="authorize-public-key"]'
).content;
const loginId = document.querySelector(
'meta[name="authorize-login-id"]'
).content;
const loginId = document.querySelector(
'meta[name="authorize-login-id"]'
).content;
const cvvRequired = document.querySelector(
'meta[name="authnet-require-cvv"]'
).content;
/** @handle */
new AuthorizeAuthorizeCard(publicKey, loginId).handle();
}
instant() ? boot() : wait('#authorize-net-credit-card-payment').then(() => boot());
/** @handle */
new AuthorizeAuthorizeCard(publicKey, loginId).handle();

View File

@ -34,9 +34,9 @@
wire:model="{{ $field['name'] }}">
@endif
@if(session()->has('validation_errors') && array_key_exists($field['name'], session('validation_errors')))
@if(count($errors) && array_key_exists($field['name'], $errors))
<p class="mt-2 text-gray-900 border-red-300 px-2 py-1 bg-gray-100">
{{ session('validation_errors')[$field['name']][0] }}
{{ $errors[$field['name']][0] }}
</p>
@endif
@endcomponent

View File

@ -3,6 +3,7 @@
@section('gateway_head')
<meta name="authorize-public-key" content="{{ $public_client_id }}">
<meta name="authorize-login-id" content="{{ $api_login_id }}">
<meta name="instant-payment" content="yes" />
<script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
<meta name="authnet-require-cvv" content="{{ $gateway->company_gateway->require_cvv }}">

View File

@ -0,0 +1,67 @@
<div class="rounded-lg border bg-card text-card-foreground shadow-sm overflow-hidden py-5 bg-white sm:gap-4"
id="authorize-net-credit-card-payment">
<meta name="authorize-public-key" content="{{ $public_client_id }}">
<meta name="authorize-login-id" content="{{ $api_login_id }}">
<meta name="authnet-require-cvv" content="{{ $gateway->company_gateway->require_cvv }}">
<form action="{{ route('client.payments.response') }}" method="post" id="server_response">
@csrf
<input type="hidden" name="payment_hash" value="{{ $payment_hash }}">
<input type="hidden" name="company_gateway_id" value="{{ $gateway->company_gateway->id }}">
<input type="hidden" name="payment_method_id" value="1">
<input type="hidden" name="gateway_response" id="gateway_response">
<input type="hidden" name="dataValue" id="dataValue"/>
<input type="hidden" name="dataDescriptor" id="dataDescriptor"/>
<input type="hidden" name="token" id="token"/>
<input type="hidden" name="store_card" id="store_card"/>
<input type="hidden" name="amount_with_fee" id="amount_with_fee" value="{{ $total['amount_with_fee'] }}"/>
</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', ['title' => ctrans('texts.pay_with')])
@if(count($tokens) > 0)
@foreach($tokens as $token)
<label class="mr-4">
<input
type="radio"
data-token="{{ $token->hashed_id }}"
name="payment-type"
class="form-radio cursor-pointer toggle-payment-with-token"/>
<span class="ml-1 cursor-pointer">**** {{ $token->meta?->last4 }}</span>
</label>
@endforeach
@endisset
<label>
<input
type="radio"
id="toggle-payment-with-credit-card"
class="form-radio cursor-pointer"
name="payment-type"
checked/>
<span class="ml-1 cursor-pointer">{{ __('texts.new_card') }}</span>
</label>
@endcomponent
@include('portal.ninja2020.gateways.includes.save_card')
@include('portal.ninja2020.gateways.authorize.includes.credit_card')
@include('portal.ninja2020.gateways.includes.pay_now')
</div>
@assets
@if($gateway->company_gateway->getConfigField('testMode'))
<script src="https://jstest.authorize.net/v1/Accept.js" charset="utf-8"></script>
@else
<script src="https://js.authorize.net/v1/Accept.js" charset="utf-8"></script>
@endif
@vite('resources/js/clients/payments/authorize-credit-card-payment.js')
@endassets

View File

@ -1,11 +1,13 @@
<div class="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6"
style="display: flex!important; justify-content: center!important;" id="authorize--credit-card-container">
<div class="card-js" id="my-card" data-capture-name="true">
<input class="name" id="cardholder_name" name="card-holders-name" placeholder="{{ ctrans('texts.name')}}">
<input class="card-number my-custom-class" id="card_number" name="card-number">
<input class="expiry-month" name="expiry-month" id="expiration_month" autocomplete="cc-exp-month" x-autocompletetype="cc-exp-month">
<input class="expiry-year" name="expiry-year" id="expiration_year" autocomplete="cc-exp-year" x-autocompletetype="cc-exp-year">
<input class="cvc" name="cvc" id="cvv">
<div class="card-js" id="my-card" data-capture-name="true">
<input class="input w-full" id="cardholder_name" name="card_holders_name"
placeholder="{{ ctrans('texts.name')}}">
<input type="text" class="input w-full" id="number" placeholder="0000 0000 0000 0000">
<div class="flex items-center gap-2">
<input type="text" class="input w-1/2" id="date" placeholder="MM/YY">
<input type="text" class="input w-1/2" id="cvv" placeholder="000">
</div>
</div>
<div id="errors"></div>