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 bool $is_loading = true;
public array $errors = [];
public function mount(): void public function mount(): void
{ {
MultiDB::setDB( MultiDB::setDB(
@ -103,6 +105,7 @@ class RequiredFields extends Component
public function handleSubmit(array $data) public function handleSubmit(array $data)
{ {
$this->errors = [];
$this->is_loading = true; $this->is_loading = true;
$rff = new RFFService( $rff = new RFFService(
@ -114,9 +117,14 @@ class RequiredFields extends Component
$contact = auth()->user(); $contact = auth()->user();
/** @var \App\Models\ClientContact $contact */ /** @var \App\Models\ClientContact $contact */
$rff->handleSubmit($data, $contact, function () { $errors = $rff->handleSubmit($data, $contact, return_errors: true, callback: function () {
$this->dispatch('required-fields'); $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 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\PaymentType;
use App\Models\SystemLog; use App\Models\SystemLog;
use App\PaymentDrivers\AuthorizePaymentDriver; use App\PaymentDrivers\AuthorizePaymentDriver;
use App\PaymentDrivers\Common\LivewireMethodInterface;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use net\authorize\api\contract\v1\DeleteCustomerPaymentProfileRequest; use net\authorize\api\contract\v1\DeleteCustomerPaymentProfileRequest;
use net\authorize\api\contract\v1\DeleteCustomerProfileRequest; use net\authorize\api\contract\v1\DeleteCustomerProfileRequest;
@ -30,7 +31,7 @@ use net\authorize\api\controller\DeleteCustomerProfileController;
/** /**
* Class AuthorizeCreditCard. * Class AuthorizeCreditCard.
*/ */
class AuthorizeCreditCard class AuthorizeCreditCard implements LivewireMethodInterface
{ {
use MakesHash; use MakesHash;
@ -41,7 +42,7 @@ class AuthorizeCreditCard
$this->authorize = $authorize; $this->authorize = $authorize;
} }
public function processPaymentView($data) public function paymentData(array $data): array
{ {
$tokens = ClientGatewayToken::where('client_id', $this->authorize->client->id) $tokens = ClientGatewayToken::where('client_id', $this->authorize->client->id)
->where('company_gateway_id', $this->authorize->company_gateway->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['public_client_id'] = $this->authorize->init()->getPublicClientKey();
$data['api_login_id'] = $this->authorize->company_gateway->getConfigField('apiLoginId'); $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); return render('gateways.authorize.credit_card.pay', $data);
} }
@ -313,4 +321,9 @@ class AuthorizeCreditCard
'invoices' => $vars['invoices'], '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 * @param array $data
*/ */
public function livewirePaymentView(array $data): string; 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\Stripe;
use Stripe\StripeClient; use Stripe\StripeClient;
class StripePaymentDriver extends BaseDriver implements LivewireMethodInterface class StripePaymentDriver extends BaseDriver
{ {
use MakesHash; use MakesHash;
use Utilities; use Utilities;
@ -1041,9 +1041,4 @@ class StripePaymentDriver extends BaseDriver implements LivewireMethodInterface
return false; 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); MultiDB::setDb($this->database);
@ -105,6 +105,10 @@ class RFFService
$validator = Validator::make($data, $rules); $validator = Validator::make($data, $rules);
if ($validator->fails()) { if ($validator->fails()) {
if ($return_errors) {
return $validator->getMessageBag()->getMessages();
}
session()->flash('validation_errors', $validator->getMessageBag()->getMessages()); session()->flash('validation_errors', $validator->getMessageBag()->getMessages());
return false; return false;

45
package-lock.json generated
View File

@ -1,10 +1,11 @@
{ {
"name": "invoiceninja", "name": "@invoiceninja/invoiceninja",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"dependencies": { "dependencies": {
"@invoiceninja/simple-card": "^0.0.2",
"axios": "^0.25", "axios": "^0.25",
"card-js": "^1.0.13", "card-js": "^1.0.13",
"card-validator": "^8.1.1", "card-validator": "^8.1.1",
@ -2023,6 +2024,15 @@
"purgecss": "^3.1.3" "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": { "node_modules/@isaacs/cliui": {
"version": "8.0.2", "version": "8.0.2",
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "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", "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz",
"integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" "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": { "node_modules/@nodelib/fs.scandir": {
"version": "2.1.5", "version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@ -12763,6 +12786,15 @@
"purgecss": "^3.1.3" "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": { "@isaacs/cliui": {
"version": "8.0.2", "version": "8.0.2",
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "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", "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz",
"integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" "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": { "@nodelib/fs.scandir": {
"version": "2.1.5", "version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "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" "vue-template-compiler": "^2.6.14"
}, },
"dependencies": { "dependencies": {
"@invoiceninja/simple-card": "^0.0.2",
"axios": "^0.25", "axios": "^0.25",
"card-js": "^1.0.13", "card-js": "^1.0.13",
"card-validator": "^8.1.1", "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" "src": "resources/js/clients/payment_methods/wepay-bank-account.js"
}, },
"resources/js/clients/payments/authorize-credit-card-payment.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, "isEntry": true,
"src": "resources/js/clients/payments/authorize-credit-card-payment.js" "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) * @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) { constructor(publicKey, loginId) {
this.publicKey = publicKey; this.publicKey = publicKey;
this.loginId = loginId; 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 = () => { handleAuthorization = () => {
if (
if (cvvRequired == "1" && document.getElementById("cvv").value.length < 3) { this.cvvRequired == '1' &&
var $errors = $('#errors'); document.getElementById('cvv').value.length < 3
$errors.show().html("<p>CVV is required</p>"); ) {
const $errors = document.getElementById('errors');
if ($errors) {
$errors.innerText = 'CVV is required';
$errors.style.display = 'block';
}
document.getElementById('pay-now').disabled = false; document.getElementById('pay-now').disabled = false;
document.querySelector('#pay-now > svg').classList.add('hidden'); 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; return;
} }
var myCard = $('#my-card');
var authData = {}; var authData = {};
authData.clientKey = this.publicKey; authData.clientKey = this.publicKey;
authData.apiLoginID = this.loginId; authData.apiLoginID = this.loginId;
var cardData = {}; var cardData = {};
cardData.cardNumber = myCard.CardJs('cardNumber').replace(/[^\d]/g, ''); cardData.cardNumber = this.sc.value('number')?.replace(/[^\d]/g, '');
cardData.month = myCard.CardJs('expiryMonth').replace(/[^\d]/g, ''); cardData.month = this.sc.value('month')?.replace(/[^\d]/g, '');
cardData.year = myCard.CardJs('expiryYear').replace(/[^\d]/g, ''); cardData.year = `20${this.sc.value('year')?.replace(/[^\d]/g, '')}`;
cardData.cardCode = document.getElementById("cvv").value.replace(/[^\d]/g, ''); cardData.cardCode = this.sc.value('cvv')?.replace(/[^\d]/g, '');
var secureData = {}; var secureData = {};
secureData.authData = authData; secureData.authData = authData;
@ -58,102 +83,112 @@ class AuthorizeAuthorizeCard {
Accept.dispatchData(secureData, this.responseHandler); Accept.dispatchData(secureData, this.responseHandler);
return false; return false;
};
}
handlePayNowAction(token_hashed_id) { handlePayNowAction(token_hashed_id) {
document.getElementById('pay-now').disabled = true; document.getElementById('pay-now').disabled = true;
document.querySelector('#pay-now > svg').classList.remove('hidden'); document.querySelector('#pay-now > svg').classList.remove('hidden');
document.querySelector('#pay-now > span').classList.add('hidden'); document.querySelector('#pay-now > span').classList.add('hidden');
document.getElementById("token").value = token_hashed_id; document.getElementById('token').value = token_hashed_id;
document.getElementById("server_response").submit(); document.getElementById('server_response').submit();
} }
responseHandler = (response) => { responseHandler = (response) => {
if (response.messages.resultCode === "Error") { if (response.messages.resultCode === 'Error') {
var i = 0; var i = 0;
var $errors = $('#errors'); // get the reference of the div const $errors = document.getElementById('errors'); // get the reference of the div
$errors.show().html("<p>" + response.messages.message[i].code + ": " + response.messages.message[i].text + "</p>");
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.getElementById('pay-now').disabled = false;
document.querySelector('#pay-now > svg').classList.add('hidden'); document.querySelector('#pay-now > svg').classList.add('hidden');
document.querySelector('#pay-now > span').classList.remove('hidden'); document
} else if (response.messages.resultCode === "Ok") { .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; let storeCard = document.querySelector(
document.getElementById("dataValue").value = response.opaqueData.dataValue; 'input[name=token-billing-checkbox]:checked'
);
let storeCard = document.querySelector('input[name=token-billing-checkbox]:checked');
if (storeCard) { 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; return false;
} };
handle = () => { handle = () => {
Array Array.from(
.from(document.getElementsByClassName('toggle-payment-with-token')) document.getElementsByClassName('toggle-payment-with-token')
.forEach((element) => element.addEventListener('click', (e) => { ).forEach((element) =>
document element.addEventListener('click', (e) => {
.getElementById('save-card--container').style.display = 'none'; document.getElementById('save-card--container').style.display =
document 'none';
.getElementById('authorize--credit-card-container').style.display = 'none'; document.getElementById(
'authorize--credit-card-container'
).style.display = 'none';
document document.getElementById('token').value = e.target.dataset.token;
.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) { if (payWithCreditCardToggle) {
payWithCreditCardToggle payWithCreditCardToggle.addEventListener('click', () => {
.addEventListener('click', () => { document.getElementById('save-card--container').style.display =
document 'grid';
.getElementById('save-card--container').style.display = 'grid'; document.getElementById(
document 'authorize--credit-card-container'
.getElementById('authorize--credit-card-container').style.display = 'flex'; ).style.display = 'flex';
document document.getElementById('token').value = null;
.getElementById('token').value = null; });
});
} }
let payNowButton = document.getElementById('pay-now'); let payNowButton = document.getElementById('pay-now');
if (payNowButton) { if (payNowButton) {
payNowButton payNowButton.addEventListener('click', (e) => {
.addEventListener('click', (e) => { let token = document.getElementById('token');
let token = document.getElementById('token');
token.value token.value
? this.handlePayNowAction(token.value) ? this.handlePayNowAction(token.value)
: this.handleAuthorization(); : this.handleAuthorization();
}); });
} }
return this; return this;
} };
} }
const publicKey = document.querySelector( function boot() {
'meta[name="authorize-public-key"]' const publicKey = document.querySelector(
).content; 'meta[name="authorize-public-key"]'
).content;
const loginId = document.querySelector( const loginId = document.querySelector(
'meta[name="authorize-login-id"]' 'meta[name="authorize-login-id"]'
).content; ).content;
const cvvRequired = document.querySelector( /** @handle */
'meta[name="authnet-require-cvv"]' new AuthorizeAuthorizeCard(publicKey, loginId).handle();
).content; }
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'] }}"> wire:model="{{ $field['name'] }}">
@endif @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"> <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> </p>
@endif @endif
@endcomponent @endcomponent

View File

@ -3,6 +3,7 @@
@section('gateway_head') @section('gateway_head')
<meta name="authorize-public-key" content="{{ $public_client_id }}"> <meta name="authorize-public-key" content="{{ $public_client_id }}">
<meta name="authorize-login-id" content="{{ $api_login_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> <script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
<meta name="authnet-require-cvv" content="{{ $gateway->company_gateway->require_cvv }}"> <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" <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"> style="display: flex!important; justify-content: center!important;" id="authorize--credit-card-container">
<div class="card-js" id="my-card" data-capture-name="true"> <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="input w-full" id="cardholder_name" name="card_holders_name"
<input class="card-number my-custom-class" id="card_number" name="card-number"> placeholder="{{ ctrans('texts.name')}}">
<input class="expiry-month" name="expiry-month" id="expiration_month" autocomplete="cc-exp-month" x-autocompletetype="cc-exp-month"> <input type="text" class="input w-full" id="number" placeholder="0000 0000 0000 0000">
<input class="expiry-year" name="expiry-year" id="expiration_year" autocomplete="cc-exp-year" x-autocompletetype="cc-exp-year"> <div class="flex items-center gap-2">
<input class="cvc" name="cvc" id="cvv"> <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>
<div id="errors"></div> <div id="errors"></div>