mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-10 05:02:36 +01:00
Updates for ach payments
This commit is contained in:
parent
ad203ac228
commit
b12abf9103
@ -117,7 +117,7 @@ trait ChartQueries
|
||||
GROUP BY invoices.date
|
||||
HAVING currency_id = :currency_id
|
||||
"), [
|
||||
'company_currency' => $this->company->settings->currency_id,
|
||||
'company_currency' => (int)$this->company->settings->currency_id,
|
||||
'currency_id' => $currency_id,
|
||||
'company_id' => $this->company->id,
|
||||
'start_date' => $start_date,
|
||||
|
@ -15,24 +15,24 @@
|
||||
|
||||
@section('gateway_content')
|
||||
<div class="alert alert-failure mb-4" hidden id="errors"></div>
|
||||
|
||||
<form action="{{ route('client.payments.response') }}" method="post" id="server-response">
|
||||
@csrf
|
||||
<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="source" value="">
|
||||
<input type="hidden" name="amount" value="{{ $amount }}">
|
||||
<input type="hidden" name="currency" value="{{ $currency }}">
|
||||
<input type="hidden" name="customer" value="{{ $customer->id }}">
|
||||
<input type="hidden" name="payment_hash" value="{{ $payment_hash }}">
|
||||
<input type="hidden" name="client_secret" value="{{ $client_secret }}">
|
||||
<input type="hidden" name="gateway_response" id="gateway_response" value="">
|
||||
</form>
|
||||
|
||||
@if(count($tokens) > 0)
|
||||
|
||||
@include('portal.ninja2020.gateways.includes.payment_details')
|
||||
|
||||
<form action="{{ route('client.payments.response') }}" method="post" id="server-response">
|
||||
@csrf
|
||||
<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="source" value="">
|
||||
<input type="hidden" name="amount" value="{{ $amount }}">
|
||||
<input type="hidden" name="currency" value="{{ $currency }}">
|
||||
<input type="hidden" name="customer" value="{{ $customer->id }}">
|
||||
<input type="hidden" name="payment_hash" value="{{ $payment_hash }}">
|
||||
<input type="hidden" name="client_secret" value="{{ $client_secret }}">
|
||||
<input type="hidden" name="gateway_response" id="gateway_response" value="">
|
||||
</form>
|
||||
|
||||
@component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.pay_with')])
|
||||
@if(count($tokens) > 0)
|
||||
@foreach($tokens as $token)
|
||||
@ -52,6 +52,11 @@
|
||||
|
||||
@else
|
||||
|
||||
@component('portal.ninja2020.components.general.card-element-single')
|
||||
<input type="checkbox" class="form-checkbox mr-1" id="accept-terms" required>
|
||||
<label for="accept-terms" class="cursor-pointer">{{ ctrans('texts.ach_authorization', ['company' => auth()->user()->company->present()->name, 'email' => auth()->guard('contact')->user()->client->company->settings->email]) }}</label>
|
||||
@endcomponent
|
||||
|
||||
@component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.account_holder_name')])
|
||||
<input class="input w-full" id="account-holder-name-field" type="text" placeholder="{{ ctrans('texts.name') }}" value="{{ $gateway->client->present()->first_name() }} {{ $gateway->client->present()->last_name(); }}"required>
|
||||
@endcomponent
|
||||
@ -71,87 +76,40 @@
|
||||
<span>{{ $slot ?? ctrans('texts.new_bank_account') }}</span>
|
||||
</button>
|
||||
</dd>
|
||||
|
||||
<div class="mt-5 sm:mt-0 sm:ml-6 sm:flex-shrink-0 sm:flex sm:items-center">
|
||||
<div class="inline-flex rounded-md shadow-sm" x-data="{ open: false }">
|
||||
<button class="button button-danger" @click="open = true" id="open-delete-popup" style="display: none;">
|
||||
{{ ctrans('texts.remove_payment_method') }}
|
||||
</button>
|
||||
|
||||
<div x-show="open" class="fixed inset-x-0 bottom-0 px-4 pb-4 sm:inset-0 sm:flex sm:items-center sm:justify-center" style="display: none;">
|
||||
<div x-show="open" x-transition:enter="ease-out duration-300" x-transition:enter-start="opacity-0"
|
||||
x-transition:enter-end="opacity-100" x-transition:leave="ease-in duration-200"
|
||||
x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0"
|
||||
class="fixed inset-0 transition-opacity">
|
||||
<div class="absolute inset-0 bg-gray-500 opacity-75"></div>
|
||||
</div>
|
||||
|
||||
<div x-show="open" x-transition:enter="ease-out duration-300"
|
||||
x-transition:enter-start="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
x-transition:enter-end="opacity-100 translate-y-0 sm:scale-100" x-transition:leave="ease-in duration-200"
|
||||
x-transition:leave-start="opacity-100 translate-y-0 sm:scale-100"
|
||||
x-transition:leave-end="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
class="px-4 pt-5 pb-4 overflow-hidden transition-all transform bg-white rounded-lg shadow-xl sm:max-w-lg sm:w-full sm:p-6">
|
||||
<div class="sm:flex sm:items-start">
|
||||
<div
|
||||
class="flex items-center justify-center flex-shrink-0 w-12 h-12 mx-auto bg-red-100 rounded-full sm:mx-0 sm:h-10 sm:w-10">
|
||||
<svg class="w-6 h-6 text-red-600" stroke="currentColor" fill="none" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
|
||||
<h3 class="text-lg font-medium leading-6 text-gray-900" translate>
|
||||
{{ ctrans('texts.confirmation') }}
|
||||
</h3>
|
||||
<div class="mt-2">
|
||||
<p class="text-sm leading-5 text-gray-500">
|
||||
|
||||
{{ ctrans('texts.ach_authorization', ['company' => auth()->user()->company->present()->name, 'email' => auth()->guard('contact')->user()->client->company->settings->email]) }}
|
||||
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
|
||||
<div class="flex w-full rounded-md shadow-sm sm:ml-3 sm:w-auto">
|
||||
<button type="submit" onclick="confirmPayment(() => this.disabled = true, 0); return true;" class="button button-danger button-block" dusk="confirm-payment-removal">
|
||||
{{ ctrans('texts.confirm') }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex w-full mt-3 rounded-md shadow-sm sm:mt-0 sm:w-auto">
|
||||
|
||||
<button @click="open = false" type="button" class="button button-secondary button-block">
|
||||
{{ ctrans('texts.cancel') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@endsection
|
||||
|
||||
|
||||
|
||||
|
||||
@push('footer')
|
||||
<script src="https://js.stripe.com/v3/"></script>
|
||||
|
||||
<script>
|
||||
|
||||
let payNow = document.getElementById('pay-now');
|
||||
|
||||
let stripePaymentIntent = '';
|
||||
|
||||
if(payNow)
|
||||
{
|
||||
|
||||
Array
|
||||
.from(document.getElementsByClassName('toggle-payment-with-token'))
|
||||
.forEach((element) => element.addEventListener('click', (element) => {
|
||||
document.querySelector('input[name=source]').value = element.target.dataset.token;
|
||||
}));
|
||||
payNow.addEventListener('click', function () {
|
||||
let payNowButton = document.getElementById('pay-now');
|
||||
payNowButton.disabled = true;
|
||||
payNowButton.querySelector('svg').classList.remove('hidden');
|
||||
payNowButton.querySelector('span').classList.add('hidden');
|
||||
document.getElementById('server-response').submit();
|
||||
});
|
||||
}
|
||||
document.getElementById('new-bank').addEventListener('click', (ev) => {
|
||||
if (!document.getElementById('accept-terms').checked) {
|
||||
errors.textContent = "You must accept the mandate terms prior to making payment.";
|
||||
errors.hidden = false;
|
||||
return;
|
||||
}
|
||||
errors.hidden = true;
|
||||
let stripe;
|
||||
|
||||
let response;
|
||||
|
||||
let publishableKey = document.querySelector('meta[name="stripe-publishable-key"]').content
|
||||
let stripeConnect = document.querySelector('meta[name="stripe-account-id"]')?.content
|
||||
|
||||
@ -161,46 +119,14 @@
|
||||
else {
|
||||
stripe = Stripe(publishableKey);
|
||||
}
|
||||
|
||||
|
||||
if(payNow)
|
||||
{
|
||||
|
||||
Array
|
||||
.from(document.getElementsByClassName('toggle-payment-with-token'))
|
||||
.forEach((element) => element.addEventListener('click', (element) => {
|
||||
document.querySelector('input[name=source]').value = element.target.dataset.token;
|
||||
}));
|
||||
|
||||
payNow.addEventListener('click', function () {
|
||||
|
||||
let payNowButton = document.getElementById('pay-now');
|
||||
payNowButton.disabled = true;
|
||||
payNowButton.querySelector('svg').classList.remove('hidden');
|
||||
payNowButton.querySelector('span').classList.add('hidden');
|
||||
|
||||
document.getElementById('server-response').submit();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
document.getElementById('new-bank').addEventListener('click', (ev) => {
|
||||
|
||||
errors.hidden = true;
|
||||
|
||||
let newBankButton = document.getElementById('new-bank');
|
||||
newBankButton.disabled = true;
|
||||
newBankButton.querySelector('svg').classList.remove('hidden');
|
||||
newBankButton.querySelector('span').classList.add('hidden');
|
||||
|
||||
|
||||
ev.preventDefault();
|
||||
const accountHolderNameField = document.getElementById('account-holder-name-field');
|
||||
const emailField = document.getElementById('email-field');
|
||||
const clientSecret = document.querySelector('meta[name="client_secret"]')?.content;
|
||||
|
||||
// Calling this method will open the instant verification dialog.
|
||||
stripe.collectBankAccountForPayment({
|
||||
clientSecret: clientSecret,
|
||||
@ -230,68 +156,39 @@
|
||||
// manually-entered. Display payment method details and mandate text
|
||||
// to the customer and confirm the intent once they accept
|
||||
// the mandate.
|
||||
stripePaymentIntent = paymentIntent;
|
||||
|
||||
showModal(paymentIntent);
|
||||
|
||||
confirmPayment(stripe, clientSecret);
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
function showModal(paymentIntent)
|
||||
{
|
||||
document.getElementById('open-delete-popup').click();
|
||||
}
|
||||
|
||||
function confirmPayment(){
|
||||
|
||||
const clientSecret = document.querySelector('meta[name="client_secret"]')?.content;
|
||||
|
||||
function confirmPayment(stripe, clientSecret){
|
||||
stripe.confirmUsBankAccountPayment(clientSecret)
|
||||
.then(({paymentIntent, error}) => {
|
||||
|
||||
console.log(paymentIntent);
|
||||
|
||||
if (error) {
|
||||
console.error(error.message);
|
||||
// The payment failed for some reason.
|
||||
} else if (paymentIntent.status === "requires_payment_method") {
|
||||
// Confirmation failed. Attempt again with a different payment method.
|
||||
|
||||
errors.textContent = error.message;
|
||||
errors.hidden = false;
|
||||
resetButtons();
|
||||
|
||||
} else if (paymentIntent.status === "processing") {
|
||||
// Confirmation succeeded! The account will be debited.
|
||||
// Display a message to customer.
|
||||
|
||||
let gateway_response = document.getElementById('gateway_response');
|
||||
gateway_response.value = JSON.stringify(
|
||||
paymentIntent
|
||||
);
|
||||
document.getElementById('server-response').submit();
|
||||
|
||||
} else if (paymentIntent.next_action?.type === "verify_with_microdeposits") {
|
||||
// The account needs to be verified via microdeposits.
|
||||
// Display a message to consumer with next steps (consumer waits for
|
||||
// microdeposits, then enters a statement descriptor code on a page sent to them via email).
|
||||
}
|
||||
}).finally((promise) => {
|
||||
|
||||
console.log(promise);
|
||||
console.log("and we are finished")
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
resetButtons();
|
||||
|
||||
finalize();
|
||||
|
||||
}
|
||||
|
||||
function finalize()
|
||||
{
|
||||
|
||||
document.getElementById('server-response').submit();
|
||||
|
||||
// resetButtons();
|
||||
}
|
||||
|
||||
function resetButtons()
|
||||
@ -300,8 +197,6 @@
|
||||
newBankButton.disabled = false;
|
||||
newBankButton.querySelector('svg').classList.add('hidden');
|
||||
newBankButton.querySelector('span').classList.remove('hidden');
|
||||
|
||||
}
|
||||
|
||||
</script>
|
||||
@endpush
|
||||
@endpush
|
Loading…
Reference in New Issue
Block a user