diff --git a/app/Http/Controllers/ClientPortal/PaymentMethodController.php b/app/Http/Controllers/ClientPortal/PaymentMethodController.php index f281816b65e2..1a640aa0fcda 100644 --- a/app/Http/Controllers/ClientPortal/PaymentMethodController.php +++ b/app/Http/Controllers/ClientPortal/PaymentMethodController.php @@ -15,6 +15,7 @@ namespace App\Http\Controllers\ClientPortal; use App\Events\Payment\Methods\MethodDeleted; use App\Http\Controllers\Controller; use App\Http\Requests\ClientPortal\PaymentMethod\CreatePaymentMethodRequest; +use App\Http\Requests\ClientPortal\PaymentMethod\VerifyPaymentMethodRequest; use App\Http\Requests\Request; use App\Models\ClientGatewayToken; use App\Models\GatewayType; @@ -56,7 +57,6 @@ class PaymentMethodController extends Controller $data['gateway'] = $gateway; $data['client'] = auth()->user()->client; - return $gateway ->driver(auth()->user()->client) ->setPaymentMethod($request->query('method')) @@ -147,6 +147,9 @@ class PaymentMethodController extends Controller if (request()->query('method') == GatewayType::CREDIT_CARD) { return auth()->user()->client->getCreditCardGateway(); } + if (request()->query('method') == GatewayType::BACS) { + return auth()->user()->client->getBACSGateway(); + } if (in_array(request()->query('method'), [GatewayType::BANK_TRANSFER, GatewayType::DIRECT_DEBIT, GatewayType::SEPA])) { return auth()->user()->client->getBankTransferGateway(); diff --git a/app/Models/Client.php b/app/Models/Client.php index b31965025f91..2259a449adfb 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -587,6 +587,29 @@ class Client extends BaseModel implements HasLocalePreference return null; } + public function getBACSGateway() :?CompanyGateway + { + $pms = $this->service()->getPaymentMethods(-1); + + foreach ($pms as $pm) { + if ($pm['gateway_type_id'] == GatewayType::BACS) { + $cg = CompanyGateway::find($pm['company_gateway_id']); + + if ($cg && ! property_exists($cg->fees_and_limits, GatewayType::BACS)) { + $fees_and_limits = $cg->fees_and_limits; + $fees_and_limits->{GatewayType::BACS} = new FeesAndLimits; + $cg->fees_and_limits = $fees_and_limits; + $cg->save(); + } + + if ($cg && $cg->fees_and_limits->{GatewayType::BACS}->is_enabled) { + return $cg; + } + } + } + + return null; + } //todo refactor this - it is only searching for existing tokens public function getBankTransferGateway() :?CompanyGateway diff --git a/app/Models/Gateway.php b/app/Models/Gateway.php index be06d4a9372c..2ee165a9b3b0 100644 --- a/app/Models/Gateway.php +++ b/app/Models/Gateway.php @@ -144,6 +144,7 @@ class Gateway extends StaticModel GatewayType::DIRECT_DEBIT => ['refund' => false, 'token_billing' => false, 'webhooks' => ['payment_intent.processing','payment_intent.succeeded','payment_intent.partially_funded']], GatewayType::ALIPAY => ['refund' => false, 'token_billing' => false], GatewayType::APPLE_PAY => ['refund' => false, 'token_billing' => false], + GatewayType::BACS => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded']], GatewayType::SOFORT => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'payment_intent.succeeded']], GatewayType::KLARNA => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'payment_intent.succeeded']], GatewayType::SEPA => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'payment_intent.succeeded']], @@ -190,6 +191,7 @@ class Gateway extends StaticModel GatewayType::EPS => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'payment_intent.succeeded']], GatewayType::BANCONTACT => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'payment_intent.succeeded']], GatewayType::BECS => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'payment_intent.succeeded']], + GatewayType::BACS => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'payment_intent.succeeded']], GatewayType::IDEAL => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'payment_intent.succeeded']], GatewayType::ACSS => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'payment_intent.succeeded']], GatewayType::FPX => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded']], diff --git a/app/Models/GatewayType.php b/app/Models/GatewayType.php index 70d91d5a5a8c..c359fdbee48c 100644 --- a/app/Models/GatewayType.php +++ b/app/Models/GatewayType.php @@ -80,6 +80,8 @@ class GatewayType extends StaticModel const KLARNA = 23; + const BACS = 24; + public function gateway() { return $this->belongsTo(Gateway::class); @@ -127,6 +129,8 @@ class GatewayType extends StaticModel return ctrans('texts.eps'); case self::BECS: return ctrans('texts.becs'); + case self::BACS: + return ctrans('texts.bacs'); case self::ACSS: return ctrans('texts.acss'); case self::DIRECT_DEBIT: diff --git a/app/Models/PaymentType.php b/app/Models/PaymentType.php index 9170b3d53d21..a9529e0963ee 100644 --- a/app/Models/PaymentType.php +++ b/app/Models/PaymentType.php @@ -73,6 +73,7 @@ class PaymentType extends StaticModel const FPX = 46; const KLARNA = 47; const Interac_E_Transfer = 48; + const BACS = 49; public array $type_names = [ self::CREDIT => 'payment_type_Credit', diff --git a/app/PaymentDrivers/Stripe/BACS.php b/app/PaymentDrivers/Stripe/BACS.php new file mode 100644 index 000000000000..e772bd6e13f9 --- /dev/null +++ b/app/PaymentDrivers/Stripe/BACS.php @@ -0,0 +1,191 @@ +stripe = $stripe; + } + + public function authorizeView(array $data) + { + $customer = $this->stripe->findOrCreateCustomer(); + $data['session'] = Session::create([ + 'payment_method_types' => ['bacs_debit'], + 'mode' => 'setup', + 'customer' => $customer->id, + 'success_url' => str_replace("%7B", "{", str_replace("%7D", "}", $this->buildAuthorizeUrl())), + 'cancel_url' => route('client.payment_methods.index'), + ]); + return render('gateways.stripe.bacs.authorize', $data); + } + private function buildAuthorizeUrl(): string + { + return route('client.payment_methods.confirm', [ + 'method' => GatewayType::BACS, + 'session_id' => "{CHECKOUT_SESSION_ID}", + ]); + } + + public function authorizeResponse($request) + { + $this->stripe->init(); + if ($request->session_id) { + $session = $this->stripe->stripe->checkout->sessions->retrieve($request->session_id, ['expand' => ['setup_intent']]); + + $customer = $this->stripe->findOrCreateCustomer(); + $this->stripe->attach($session->setup_intent->payment_method, $customer); + $payment_method = $this->stripe->getStripePaymentMethod($session->setup_intent->payment_method); + $this->storePaymentMethod($payment_method, $customer); + } + return redirect()->route('client.payment_methods.index'); + } + public function paymentView(array $data) + { + $data['gateway'] = $this->stripe; + $data['amount'] = $data['total']['amount_with_fee']; + $data['payment_hash'] = $this->stripe->payment_hash->hash; + + return render('gateways.stripe.bacs.pay', $data); + } + public function paymentResponse(PaymentResponseRequest $request) + + { + $this->stripe->init(); + $invoice_numbers = collect($this->stripe->payment_hash->invoices())->pluck('invoice_number')->implode(','); + $description = ctrans('texts.stripe_payment_text', ['invoicenumber' => $invoice_numbers, 'amount' => Number::formatMoney($request->amount, $this->stripe->client), 'client' => $this->stripe->client->present()->name()]); + $payment_intent_data = [ + 'amount' => $this->stripe->convertToStripeAmount($request->amount, $this->stripe->client->currency()->precision, $this->stripe->client->currency()), + 'currency' => $this->stripe->client->getCurrencyCode(), + 'customer' => $this->stripe->findOrCreateCustomer(), + 'description' => $description, + 'payment_method_types' => ['bacs_debit'], + 'metadata' => [ + 'payment_hash' => $this->stripe->payment_hash->hash, + 'gateway_type_id' => GatewayType::BACS, + ], + 'payment_method' => $request->token, + 'confirm' => true, + ]; + $state = [ + 'payment_hash' => $this->stripe->payment_hash->hash, + 'payment_intent' => $this->stripe->createPaymentIntent($payment_intent_data), + ]; + $state = array_merge($state, $request->all()); + + $state['customer'] = $state['payment_intent']->customer; + + $this->stripe->payment_hash->data = array_merge((array) $this->stripe->payment_hash->data, $state); + $this->stripe->payment_hash->save(); + + if ($state['payment_intent']->status == 'processing') { + $this->stripe->logSuccessfulGatewayResponse(['response' => $state['payment_intent'], 'data' => $this->stripe->payment_hash], SystemLog::TYPE_STRIPE); + + return $this->processSuccessfulPayment($state['payment_intent']); + } + + return $this->processUnsuccessfulPayment("An unknown error occured."); + } + + public function processSuccessfulPayment($payment_intent) + { + UpdateCustomer::dispatch($this->stripe->company_gateway->company->company_key, $this->stripe->company_gateway->id, $this->stripe->client->id); + + $data = [ + 'payment_method' => $payment_intent['id'], + 'payment_type' => PaymentType::BACS, + 'amount' => $this->stripe->convertFromStripeAmount($payment_intent->amount, $this->stripe->client->currency()->precision, $this->stripe->client->currency()), + 'transaction_reference' => $payment_intent['id'], + 'gateway_type_id' => GatewayType::BACS, + ]; + + $payment = $this->stripe->createPayment($data, Payment::STATUS_PENDING); + + SystemLogger::dispatch( + ['response' => $payment_intent, 'data' => $data], + SystemLog::CATEGORY_GATEWAY_RESPONSE, + SystemLog::EVENT_GATEWAY_SUCCESS, + SystemLog::TYPE_STRIPE, + $this->stripe->client, + $this->stripe->client->company, + ); + + + return redirect()->route('client.payments.show', ['payment' => $this->stripe->encodePrimaryKey($payment->id)]); + } + + public function processUnsuccessfulPayment($server_response) + { + $this->stripe->sendFailureMail($server_response); + + $message = [ + 'server_response' => $server_response, + 'data' => $this->stripe->payment_hash->data, + ]; + + SystemLogger::dispatch( + $message, + SystemLog::CATEGORY_GATEWAY_RESPONSE, + SystemLog::EVENT_GATEWAY_FAILURE, + SystemLog::TYPE_STRIPE, + $this->stripe->client, + $this->stripe->client->company, + ); + + throw new PaymentFailed('Failed to process the payment.', 500); + } + + private function storePaymentMethod($method, $customer) + { + try { + $payment_meta = new \stdClass; + $payment_meta->brand = (string) $method->bacs_debit->sort_code; + $payment_meta->last4 = (string) $method->bacs_debit->last4; + $payment_meta->state = 'unauthorized'; + $payment_meta->type = GatewayType::BACS; + + $data = [ + 'payment_meta' => $payment_meta, + 'token' => $method->id, + 'payment_method_id' => GatewayType::BACS, + ]; + $clientgateway = ClientGatewayToken::query() + ->where('token', $method->id) + ->first(); + if (!$clientgateway){ + $this->stripe->storeGatewayToken($data, ['gateway_customer_reference' => $customer->id]); + } + } catch (\Exception $e) { + return $this->stripe->processInternallyFailedPayment($this->stripe, $e); + } + } +} diff --git a/app/PaymentDrivers/Stripe/Charge.php b/app/PaymentDrivers/Stripe/Charge.php index 2dea5f9e7ef1..7ce2436aa043 100644 --- a/app/PaymentDrivers/Stripe/Charge.php +++ b/app/PaymentDrivers/Stripe/Charge.php @@ -79,6 +79,9 @@ class Charge if ($cgt->gateway_type_id == GatewayType::SEPA) { $data['payment_method_types'] = ['sepa_debit']; } + if ($cgt->gateway_type_id == GatewayType::BACS) { + $data['payment_method_types'] = ['bacs_debit']; + } /* Should improve token billing with client not present */ if (!auth()->guard('contact')->check()) { @@ -135,8 +138,15 @@ class Charge if ($cgt->gateway_type_id == GatewayType::SEPA) { $payment_method_type = PaymentType::SEPA; $status = Payment::STATUS_PENDING; - } else { - if (isset($response->latest_charge)) { + + } elseif ($cgt->gateway_type_id == GatewayType::BACS){ + $payment_method_type = PaymentType::BACS; + $status = Payment::STATUS_PENDING; + } + else { + + if(isset($response->latest_charge)) { + $charge = \Stripe\Charge::retrieve($response->latest_charge, $this->stripe->stripe_connect_auth); $payment_method_type = $charge->payment_method_details->card->brand; } elseif (isset($response->charges->data[0]->payment_method_details->card->brand)) { @@ -147,9 +157,11 @@ class Charge $status = Payment::STATUS_COMPLETED; } - - if (!in_array($response?->status, ['succeeded', 'processing'])) { - $this->stripe->processInternallyFailedPayment($this->stripe, new \Exception('Auto billing failed.', 400)); + + + if(!in_array($response?->status, ['succeeded', 'processing'])){ + $this->stripe->processInternallyFailedPayment($this->stripe, new \Exception('Auto billing failed.',400)); + } $data = [ @@ -195,6 +207,8 @@ class Charge break; case PaymentType::SEPA: return PaymentType::SEPA; + case PaymentType::BACS: + return PaymentType::BACS; default: return PaymentType::CREDIT_CARD_OTHER; break; diff --git a/app/PaymentDrivers/Stripe/Jobs/PaymentIntentWebhook.php b/app/PaymentDrivers/Stripe/Jobs/PaymentIntentWebhook.php index 92b7f6b81419..5f04159130f8 100644 --- a/app/PaymentDrivers/Stripe/Jobs/PaymentIntentWebhook.php +++ b/app/PaymentDrivers/Stripe/Jobs/PaymentIntentWebhook.php @@ -89,6 +89,7 @@ class PaymentIntentWebhook implements ShouldQueue $charge_id = false; + if (isset($this->stripe_request['object']['charges']) && optional($this->stripe_request['object']['charges']['data'][0])['id']) { $charge_id = $this->stripe_request['object']['charges']['data'][0]['id']; } // API VERSION 2018 @@ -119,8 +120,9 @@ class PaymentIntentWebhook implements ShouldQueue ->where('transaction_reference', $charge['id']) ->first(); - //return early - if ($payment && $payment->status_id == Payment::STATUS_COMPLETED) { + //return early + if($payment && $payment->status_id == Payment::STATUS_COMPLETED){ + nlog(" payment found and status correct - returning "); return; } elseif ($payment) { @@ -186,7 +188,11 @@ class PaymentIntentWebhook implements ShouldQueue } $this->updateAchPayment($payment_hash, $client, $meta); + } elseif(isset($pi['payment_method_types']) && in_array('bacs_debit', $pi['payment_method_types'])){ + return; } + + } private function updateAchPayment($payment_hash, $client, $meta) @@ -206,7 +212,7 @@ class PaymentIntentWebhook implements ShouldQueue 'transaction_reference' => $meta['transaction_reference'], 'gateway_type_id' => GatewayType::BANK_TRANSFER, ]; - + $payment = $driver->createPayment($data, Payment::STATUS_COMPLETED); SystemLogger::dispatch( @@ -254,13 +260,14 @@ class PaymentIntentWebhook implements ShouldQueue } $driver->storeGatewayToken($data, $additional_data); + } catch(\Exception $e) { + nlog("failed to import payment methods"); nlog($e->getMessage()); } } - private function updateCreditCardPayment($payment_hash, $client, $meta) { $company_gateway = CompanyGateway::find($this->company_gateway_id); @@ -278,7 +285,7 @@ class PaymentIntentWebhook implements ShouldQueue 'transaction_reference' => $meta['transaction_reference'], 'gateway_type_id' => GatewayType::CREDIT_CARD, ]; - + $payment = $driver->createPayment($data, Payment::STATUS_COMPLETED); SystemLogger::dispatch( @@ -290,4 +297,5 @@ class PaymentIntentWebhook implements ShouldQueue $client->company, ); } + } diff --git a/app/PaymentDrivers/Stripe/Jobs/StripeWebhook.php b/app/PaymentDrivers/Stripe/Jobs/StripeWebhook.php index 13a25a8bce1c..3dfc94805e9d 100644 --- a/app/PaymentDrivers/Stripe/Jobs/StripeWebhook.php +++ b/app/PaymentDrivers/Stripe/Jobs/StripeWebhook.php @@ -41,6 +41,9 @@ class StripeWebhook implements ShouldQueue 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed', + 'mandate.updated', + 'checkout.session.completed', + 'payment_method.automatically_updated' ]; public function __construct(string $company_key, int $company_gateway_id) diff --git a/app/PaymentDrivers/StripePaymentDriver.php b/app/PaymentDrivers/StripePaymentDriver.php index 9f331d5bfee2..9038b366e868 100644 --- a/app/PaymentDrivers/StripePaymentDriver.php +++ b/app/PaymentDrivers/StripePaymentDriver.php @@ -26,6 +26,7 @@ use App\Models\GatewayType; use App\Models\PaymentHash; use App\Http\Requests\Request; use App\Jobs\Util\SystemLogger; +use App\Models\Client; use App\Utils\Traits\MakesHash; use App\Exceptions\PaymentFailed; use App\Models\ClientGatewayToken; @@ -33,6 +34,9 @@ use App\PaymentDrivers\Stripe\ACH; use App\PaymentDrivers\Stripe\EPS; use App\PaymentDrivers\Stripe\FPX; use App\PaymentDrivers\Stripe\ACSS; +use App\PaymentDrivers\Stripe\Alipay; +use App\PaymentDrivers\Stripe\ApplePay; +use App\PaymentDrivers\Stripe\BACS; use App\PaymentDrivers\Stripe\BECS; use App\PaymentDrivers\Stripe\SEPA; use App\PaymentDrivers\Stripe\iDeal; @@ -59,6 +63,7 @@ use App\PaymentDrivers\Stripe\Jobs\PaymentIntentFailureWebhook; use App\PaymentDrivers\Stripe\Jobs\PaymentIntentProcessingWebhook; use App\PaymentDrivers\Stripe\Jobs\PaymentIntentPartiallyFundedWebhook; + class StripePaymentDriver extends BaseDriver { use MakesHash, Utilities; @@ -96,6 +101,7 @@ class StripePaymentDriver extends BaseDriver GatewayType::ACSS => ACSS::class, GatewayType::FPX => FPX::class, GatewayType::KLARNA => Klarna::class, + GatewayType::BACS => BACS::class, GatewayType::DIRECT_DEBIT => BankTransfer::class, ]; @@ -234,6 +240,14 @@ class StripePaymentDriver extends BaseDriver && in_array($this->client->country->iso_3166_3, ['CAN', 'USA'])) { $types[] = GatewayType::ACSS; } + if ($this->client + && $this->client->currency() + && in_array($this->client->currency()->code, ['GBP']) + && isset($this->client->country) + && in_array($this->client->company->country()->getID(), ['826']) + && in_array($this->client->country->iso_3166_3, ['GBR'])) { + $types[] = GatewayType::BACS; + } if ($this->client && $this->client->currency() && in_array($this->client->currency()->code, ['EUR', 'DKK', 'GBP', 'NOK', 'SEK', 'AUD', 'NZD', 'CAD', 'PLN', 'CHF']) @@ -245,7 +259,7 @@ class StripePaymentDriver extends BaseDriver && $this->client->currency() && in_array($this->client->currency()->code, ['EUR', 'DKK', 'GBP', 'NOK', 'SEK', 'AUD', 'NZD', 'CAD', 'PLN', 'CHF', 'USD']) && isset($this->client->country) - && in_array($this->client->company->country()->getID(), ['840']) + && in_array($this->client->company->country()->id, ['840']) && in_array($this->client->country->iso_3166_3, ['AUT','BEL','DNK','FIN','FRA','DEU','IRL','ITA','NLD','NOR','ESP','SWE','GBR','USA'])) { $types[] = GatewayType::KLARNA; } @@ -310,6 +324,8 @@ class StripePaymentDriver extends BaseDriver return 'gateways.stripe.bancontact'; case GatewayType::BECS: return 'gateways.stripe.becs'; + case GatewayType::BACS: + return 'gateways.stripe.bacs'; case GatewayType::ACSS: return 'gateways.stripe.acss'; case GatewayType::FPX: @@ -663,8 +679,6 @@ class StripePaymentDriver extends BaseDriver public function processWebhookRequest(PaymentWebhookRequest $request) { - // if($request->type === 'payment_intent.requires_action') - // nlog($request->all()); if ($request->type === 'customer.source.updated') { $ach = new ACH($this); @@ -752,6 +766,63 @@ class StripePaymentDriver extends BaseDriver } } } + } elseif ($request->type === "payment_method.automatically_updated"){ + // Will notify customer on updated information + return response()->json([], 200); + } elseif ($request->type === "checkout.session.completed"){ + // Store payment token for Stripe BACS + $this->init(); + $setup_intent = $this->stripe->setupIntents->retrieve($request->data['object']['setup_intent'], []); + $clientpayment_token = ClientGatewayToken::where('gateway_customer_reference', $request->data['object']['customer'])->first(); + if ($clientpayment_token){ + $this->client = Client::where('id', $clientpayment_token->client_id)->first(); + $customer = $this->findOrCreateCustomer(); + $this->attach($setup_intent->payment_method, $customer); + $payment_method = $this->getStripePaymentMethod($setup_intent->payment_method); + $payment_meta = new \stdClass; + $payment_meta->brand = (string) $payment_method->bacs_debit->sort_code; + $payment_meta->last4 = (string) $payment_method->bacs_debit->last4; + $payment_meta->state = 'unauthorized'; + $payment_meta->type = GatewayType::BACS; + + $data = [ + 'payment_meta' => $payment_meta, + 'token' => $payment_method->id, + 'payment_method_id' => GatewayType::BACS, + ]; + $clientgateway = ClientGatewayToken::query() + ->where('token', $payment_method) + ->first(); + if (!$clientgateway){ + $this->storeGatewayToken($data, ['gateway_customer_reference' => $customer->id]); + } + } + return response()->json([], 200); + } elseif ($request->type === "mandate.updated"){ + // Check if payment method BACS is still valid + if ($request->data['object']['status'] === "active"){ + // Check if payment method exists + $payment_method = (string) $request->data['object']['payment_method']; + $clientgateway = ClientGatewayToken::query() + ->where('token', $payment_method) + ->first(); + if ($clientgateway){ + $clientgateway->meta->state = 'authorized'; + $clientgateway->update(); + }; + return response()->json([], 200); + } + elseif ($request->data['object']['status'] === "inactive" && $request->data['object']['payment_method']){ + // Delete payment method + $clientgateway = ClientGatewayToken::query() + ->where('token', $request->data['object']['payment_method']) + ->first(); + $clientgateway->delete(); + return response()->json([], 200); + } + elseif ($request->data['object']['status'] === "pending"){ + return response()->json([], 200); + } } return response()->json([], 200); diff --git a/database/migrations/2022_16_12_54687_add_stripe_bacs.php b/database/migrations/2022_16_12_54687_add_stripe_bacs.php new file mode 100644 index 000000000000..a454e483e29b --- /dev/null +++ b/database/migrations/2022_16_12_54687_add_stripe_bacs.php @@ -0,0 +1,39 @@ +id = 49; + $type->name = 'BACS'; + $type->gateway_type_id = GatewayType::BACS; + $type->save(); + } + + $gt = GatewayType::find(24); + + if(!$gt) + { + $type = new GatewayType(); + $type->id = 24; + $type->alias = 'bacs'; + $type->name = 'BACS'; + $type->save(); + } + } +}; diff --git a/lang/en/texts.php b/lang/en/texts.php index 7ab2aa9f4780..8d6ff548d554 100644 --- a/lang/en/texts.php +++ b/lang/en/texts.php @@ -4299,6 +4299,9 @@ $LANG = array( 'klarna' => 'Klarna', 'eps' => 'EPS', 'becs' => 'BECS Direct Debit', + 'bacs' => 'BACS Direct Debit', + 'payment_type_BACS' => 'BACS Direct Debit', + 'missing_payment_method' => 'Please add a payment method first, before trying to pay.', 'becs_mandate' => 'By providing your bank account details, you agree to this Direct Debit Request and the Direct Debit Request service agreement, and authorise Stripe Payments Australia Pty Ltd ACN 160 180 343 Direct Debit User ID number 507156 (“Stripe”) to debit your account through the Bulk Electronic Clearing System (BECS) on behalf of :company (the “Merchant”) for any amounts separately communicated to you by the Merchant. You certify that you are either an account holder or an authorised signatory on the account listed above.', 'you_need_to_accept_the_terms_before_proceeding' => 'You need to accept the terms before proceeding.', 'direct_debit' => 'Direct Debit', @@ -4907,7 +4910,7 @@ $LANG = array( 'export_company' => 'Create company backup', 'backup' => 'Backup', 'notification_purchase_order_created_body' => 'The following purchase_order :purchase_order was created for vendor :vendor for :amount.', - 'notification_purchase_order_created_subject' => 'Purchase Order :purchase_order was created for :vendor', + 'notification_purchase_order_created_subject' => 'Purchase Order :purchase_order was created for :vendor', 'notification_purchase_order_sent_subject' => 'Purchase Order :purchase_order was sent to :vendor', 'notification_purchase_order_sent' => 'The following vendor :vendor was emailed Purchase Order :purchase_order for :amount.', 'subscription_blocked' => 'This product is a restricted item, please contact the vendor for further information.', diff --git a/public/js/clients/payments/stripe-bacs.js b/public/js/clients/payments/stripe-bacs.js new file mode 100644 index 000000000000..91b29fd0c177 --- /dev/null +++ b/public/js/clients/payments/stripe-bacs.js @@ -0,0 +1,2 @@ +/*! For license information please see stripe-bacs.js.LICENSE.txt */ +(()=>{var e,t,n,o,r,a;function i(e,t){for(var n=0;n svg").classList.remove("hidden"),document.querySelector("#authorize-bacs > span").classList.add("hidden"),location.href=document.querySelector("meta[name=stripe-redirect-url]").content})):(o.payNowButton=document.getElementById("pay-now"),document.getElementById("pay-now").addEventListener("click",(function(e){o.payNowButton.disabled=!0,o.payNowButton.querySelector("svg").classList.remove("hidden"),o.payNowButton.querySelector("span").classList.add("hidden"),document.getElementById("server-response").submit()})),o.payment_data=Array.from(document.getElementsByClassName("toggle-payment-with-token")),o.payment_data.length>0?o.payment_data.forEach((function(e){return e.addEventListener("click",(function(e){document.querySelector("input[name=token]").value=e.target.dataset.token}))})):(o.errors.textContent=document.querySelector("meta[name=translation-payment-method-required]").content,o.errors.hidden=!1,o.payNowButton.disabled=!0,o.payNowButton.querySelector("span").classList.remove("hidden"),o.payNowButton.querySelector("svg").classList.add("hidden")))})),this.key=t,this.errors=document.getElementById("errors"),this.stripeConnect=n,this.onlyAuthorization=m})),s=null!==(e=null===(t=document.querySelector('meta[name="stripe-publishable-key"]'))||void 0===t?void 0:t.content)&&void 0!==e?e:"",l=null!==(n=null===(o=document.querySelector('meta[name="stripe-account-id"]'))||void 0===o?void 0:o.content)&&void 0!==n?n:"",m=null!==(r=null===(a=document.querySelector('meta[name="only-authorization"]'))||void 0===a?void 0:a.content)&&void 0!==r?r:"";new d(s,l).setupStripe().handle()})(); \ No newline at end of file diff --git a/public/js/clients/payments/stripe-bacs.js.LICENSE.txt b/public/js/clients/payments/stripe-bacs.js.LICENSE.txt new file mode 100644 index 000000000000..97e5374d6e40 --- /dev/null +++ b/public/js/clients/payments/stripe-bacs.js.LICENSE.txt @@ -0,0 +1,9 @@ +/** + * 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 + */ diff --git a/public/mix-manifest.json b/public/mix-manifest.json index 10fdd6a90e02..c63784ebb642 100644 --- a/public/mix-manifest.json +++ b/public/mix-manifest.json @@ -1,49 +1,50 @@ { - "/js/app.js": "/js/app.js?id=7b6124b74168ccb1cc7da22f7a2bc9ed", - "/js/clients/payment_methods/authorize-authorize-card.js": "/js/clients/payment_methods/authorize-authorize-card.js?id=b6723e0b8ea33f1f50617fa5f289a9d3", - "/js/clients/payments/authorize-credit-card-payment.js": "/js/clients/payments/authorize-credit-card-payment.js?id=faf4828cc6b3b73b69c53d3046661884", - "/js/clients/payments/forte-credit-card-payment.js": "/js/clients/payments/forte-credit-card-payment.js?id=f42dd0caddb3603e71db061924c4b172", - "/js/clients/payments/forte-ach-payment.js": "/js/clients/payments/forte-ach-payment.js?id=b8173c7c0dee76bf9ae6312a963ae0e4", - "/js/clients/payments/stripe-ach.js": "/js/clients/payments/stripe-ach.js?id=207f218c44553470287f35f33a7eb154", - "/js/clients/payments/stripe-klarna.js": "/js/clients/payments/stripe-klarna.js?id=7268f9282c6bb3b04d19d11a7b0c1681", - "/js/clients/invoices/action-selectors.js": "/js/clients/invoices/action-selectors.js?id=404b7ee18e420de0e73f5402b7e39122", - "/js/clients/purchase_orders/action-selectors.js": "/js/clients/purchase_orders/action-selectors.js?id=2f0c4e3bab30a98e33ac768255113174", - "/js/clients/purchase_orders/accept.js": "/js/clients/purchase_orders/accept.js?id=9bb483a89a887f753e49c0b635d6276a", - "/js/clients/invoices/payment.js": "/js/clients/invoices/payment.js?id=752e2bb6390f1a422e31868cf2a2bf67", - "/js/clients/payments/stripe-sofort.js": "/js/clients/payments/stripe-sofort.js?id=4fc5dec1bc4fc21b9e32b1b490c3e7ae", - "/js/clients/payments/stripe-alipay.js": "/js/clients/payments/stripe-alipay.js?id=0a4361f9c468fa087ff543f1793adc7d", - "/js/clients/payments/checkout-credit-card.js": "/js/clients/payments/checkout-credit-card.js?id=7cb96275b3eb4901054564c654fb60e3", - "/js/clients/quotes/action-selectors.js": "/js/clients/quotes/action-selectors.js?id=3a4c5cfac7dd4c9218be55945c3c8e85", - "/js/clients/quotes/approve.js": "/js/clients/quotes/approve.js?id=00615ec62b99e3245769d4603e6053ce", - "/js/clients/payments/stripe-credit-card.js": "/js/clients/payments/stripe-credit-card.js?id=6e7c8ab039a239727317ae8622de10db", - "/js/setup/setup.js": "/js/setup/setup.js?id=8cab3339ef48418e1fb2e7a9259d51ca", - "/js/clients/payments/card-js.min.js": "/js/clients/payments/card-js.min.js?id=cf50b5ba1fcd1d184bf0c10d710672c8", - "/js/clients/shared/pdf.js": "/js/clients/shared/pdf.js?id=682de6347049b32c9488f39c78a68ace", - "/js/clients/shared/multiple-downloads.js": "/js/clients/shared/multiple-downloads.js?id=d3c404bb646f1aeaf2382a8c57ab8e1a", - "/js/clients/linkify-urls.js": "/js/clients/linkify-urls.js?id=e1c0599d6f7dc163b549a6df0b3490b4", - "/js/clients/payments/braintree-credit-card.js": "/js/clients/payments/braintree-credit-card.js?id=8b036822abaa4ceb379008fc14208dc2", - "/js/clients/payments/braintree-paypal.js": "/js/clients/payments/braintree-paypal.js?id=de0b1d0c6da7ff509bef3aee8d09e7f8", - "/js/clients/payments/wepay-credit-card.js": "/js/clients/payments/wepay-credit-card.js?id=92ef8632637d335cd0e4bc29a05b7df8", - "/js/clients/payment_methods/wepay-bank-account.js": "/js/clients/payment_methods/wepay-bank-account.js?id=af85b3f6d53c55b5d0e3a80ef58ce0de", - "/js/clients/payments/paytrace-credit-card.js": "/js/clients/payments/paytrace-credit-card.js?id=3869bc6d80acc83f81d9afe8efaae728", - "/js/clients/payments/mollie-credit-card.js": "/js/clients/payments/mollie-credit-card.js?id=7cd5a1d95d33ada211ce185ad6e4bb33", - "/js/clients/payments/eway-credit-card.js": "/js/clients/payments/eway-credit-card.js?id=27274d334aed0824ce4654fa22132f7f", - "/js/clients/payment_methods/braintree-ach.js": "/js/clients/payment_methods/braintree-ach.js?id=f85ebb6a77002afd350086d1274b6af5", - "/js/clients/payments/square-credit-card.js": "/js/clients/payments/square-credit-card.js?id=238e7001420a22b001856193689a1e70", - "/js/clients/statements/view.js": "/js/clients/statements/view.js?id=13e043123f1e58409394458a70461d63", - "/js/clients/payments/razorpay-aio.js": "/js/clients/payments/razorpay-aio.js?id=494f58d2fd8984792833ba7d3055de08", - "/js/clients/payments/stripe-sepa.js": "/js/clients/payments/stripe-sepa.js?id=77d4e397d193196e482af80737bff64a", - "/js/clients/payment_methods/authorize-checkout-card.js": "/js/clients/payment_methods/authorize-checkout-card.js?id=659c4287fb8ef1c458071c206c4d965d", - "/js/clients/payments/stripe-giropay.js": "/js/clients/payments/stripe-giropay.js?id=852a9abf5f3a29f5d7d2f989cbeab374", - "/js/clients/payments/stripe-acss.js": "/js/clients/payments/stripe-acss.js?id=447c587a5eeb0c1de3091c8358db7ad7", - "/js/clients/payments/stripe-bancontact.js": "/js/clients/payments/stripe-bancontact.js?id=f694d3f9f01e4550cb5a3eb6cb43c12d", - "/js/clients/payments/stripe-becs.js": "/js/clients/payments/stripe-becs.js?id=97ea3555a8504662eda5fce9c9115e5a", - "/js/clients/payments/stripe-eps.js": "/js/clients/payments/stripe-eps.js?id=146d48d03f5fc4b1cf122189119e2877", - "/js/clients/payments/stripe-ideal.js": "/js/clients/payments/stripe-ideal.js?id=34cf4ee3f189427fb69d0df8f5a4b766", - "/js/clients/payments/stripe-przelewy24.js": "/js/clients/payments/stripe-przelewy24.js?id=cfdcc5bf20d6bfa09700708dfdd943e2", - "/js/clients/payments/stripe-browserpay.js": "/js/clients/payments/stripe-browserpay.js?id=7015e43eb5f9f9f2f45f54b41b5780a0", - "/js/clients/payments/stripe-fpx.js": "/js/clients/payments/stripe-fpx.js?id=243c2929386b10c6a0c49ca3bcabfb2d", - "/css/app.css": "/css/app.css?id=d3b1387842383d1983c767978d20ec86", + "/js/app.js": "/js/app.js?id=19300612c6880925e8043b61e8d49632", + "/js/clients/payment_methods/authorize-authorize-card.js": "/js/clients/payment_methods/authorize-authorize-card.js?id=9fb77e87fe0f85a367050e08f79ec9df", + "/js/clients/payments/authorize-credit-card-payment.js": "/js/clients/payments/authorize-credit-card-payment.js?id=803182f668c39d631ca5c55437876da4", + "/js/clients/payments/forte-credit-card-payment.js": "/js/clients/payments/forte-credit-card-payment.js?id=6e9f466c5504d3753f9b4ffc6f947095", + "/js/clients/payments/forte-ach-payment.js": "/js/clients/payments/forte-ach-payment.js?id=1d10fcc52a1f15858e5da216f1df45ec", + "/js/clients/payments/stripe-ach.js": "/js/clients/payments/stripe-ach.js?id=7bed15f51bca764378d9a3aa605b8664", + "/js/clients/payments/stripe-klarna.js": "/js/clients/payments/stripe-klarna.js?id=5770e0d82d3843c68903744530f5ae73", + "/js/clients/payments/stripe-bacs.js": "/js/clients/payments/stripe-bacs.js?id=5739aad61ac7bbb20f2149d203849ff7", + "/js/clients/invoices/action-selectors.js": "/js/clients/invoices/action-selectors.js?id=d4f86ddee4e8a1d6e9719010aa0fe62b", + "/js/clients/purchase_orders/action-selectors.js": "/js/clients/purchase_orders/action-selectors.js?id=160b8161599fc2429b449b0970d3ba6c", + "/js/clients/purchase_orders/accept.js": "/js/clients/purchase_orders/accept.js?id=ddd4aa4069ea79411eeec367b7d5986d", + "/js/clients/invoices/payment.js": "/js/clients/invoices/payment.js?id=28221de8f1cb37f845ba4ec59bcd8867", + "/js/clients/payments/stripe-sofort.js": "/js/clients/payments/stripe-sofort.js?id=1c5493a4c53a5b862d07ee1818179ea9", + "/js/clients/payments/stripe-alipay.js": "/js/clients/payments/stripe-alipay.js?id=0274ab4f8d2b411f2a2fe5142301e7af", + "/js/clients/payments/checkout-credit-card.js": "/js/clients/payments/checkout-credit-card.js?id=4bd34a0b160f6f29b3096d870ac4d308", + "/js/clients/quotes/action-selectors.js": "/js/clients/quotes/action-selectors.js?id=6fb63bae43d077b5061f4dadfe8dffc8", + "/js/clients/quotes/approve.js": "/js/clients/quotes/approve.js?id=61a346e1977d3a1fec3634b234baa25c", + "/js/clients/payments/stripe-credit-card.js": "/js/clients/payments/stripe-credit-card.js?id=809de47258a681f0ffebe787dd6a9a93", + "/js/setup/setup.js": "/js/setup/setup.js?id=27560b012f166f8b9417ced2188aab70", + "/js/clients/payments/card-js.min.js": "/js/clients/payments/card-js.min.js?id=8ce33c3deae058ad314fb8357e5be63b", + "/js/clients/shared/pdf.js": "/js/clients/shared/pdf.js?id=be5307abc990bb44f2f92628103b1d98", + "/js/clients/shared/multiple-downloads.js": "/js/clients/shared/multiple-downloads.js?id=c2caa29f753ad1f3a12ca45acddacd72", + "/js/clients/linkify-urls.js": "/js/clients/linkify-urls.js?id=2b2fe55f926789abc52f19111006e1ec", + "/js/clients/payments/braintree-credit-card.js": "/js/clients/payments/braintree-credit-card.js?id=8e3b1c4c78c976ff0c43cb739c26b1f3", + "/js/clients/payments/braintree-paypal.js": "/js/clients/payments/braintree-paypal.js?id=5764a8d406c1eda848d073f10d178626", + "/js/clients/payments/wepay-credit-card.js": "/js/clients/payments/wepay-credit-card.js?id=dbba20d70fbebb326ddbc46115af9771", + "/js/clients/payment_methods/wepay-bank-account.js": "/js/clients/payment_methods/wepay-bank-account.js?id=b8706d7de6127f184ad19b2a810880be", + "/js/clients/payments/paytrace-credit-card.js": "/js/clients/payments/paytrace-credit-card.js?id=e0b1231a7bf6252672836222285c0f52", + "/js/clients/payments/mollie-credit-card.js": "/js/clients/payments/mollie-credit-card.js?id=bbab588ed009a93345bec520cbe66869", + "/js/clients/payments/eway-credit-card.js": "/js/clients/payments/eway-credit-card.js?id=31d068e55757636f34834bc2494250df", + "/js/clients/payment_methods/braintree-ach.js": "/js/clients/payment_methods/braintree-ach.js?id=6d8c7fd66d911b20cdc4248e33db1b3a", + "/js/clients/payments/square-credit-card.js": "/js/clients/payments/square-credit-card.js?id=b180fd6378d3723d3e9133e0b1943ac6", + "/js/clients/statements/view.js": "/js/clients/statements/view.js?id=7971b212e8a849fe36bfe915f81023bd", + "/js/clients/payments/razorpay-aio.js": "/js/clients/payments/razorpay-aio.js?id=c36ab5621413ef1de7c864bc8eb7439e", + "/js/clients/payments/stripe-sepa.js": "/js/clients/payments/stripe-sepa.js?id=b258636d8bae366e9d8f54274f437181", + "/js/clients/payment_methods/authorize-checkout-card.js": "/js/clients/payment_methods/authorize-checkout-card.js?id=e43f862d70d8710761f0856e528ec3d1", + "/js/clients/payments/stripe-giropay.js": "/js/clients/payments/stripe-giropay.js?id=72ad4ad19297f211c2e6d0fa1fa1f76d", + "/js/clients/payments/stripe-acss.js": "/js/clients/payments/stripe-acss.js?id=90b1805b1ca0264474b38054a2664c5b", + "/js/clients/payments/stripe-bancontact.js": "/js/clients/payments/stripe-bancontact.js?id=03e5d7ee187e76b0b7c16bfa91804a8a", + "/js/clients/payments/stripe-becs.js": "/js/clients/payments/stripe-becs.js?id=de2bd0ef2859e19e4f98ea9d6d11cb54", + "/js/clients/payments/stripe-eps.js": "/js/clients/payments/stripe-eps.js?id=213d9ad34a79144a0d3345cb6a262e95", + "/js/clients/payments/stripe-ideal.js": "/js/clients/payments/stripe-ideal.js?id=0a6b434e3849db26c35a143e0347e914", + "/js/clients/payments/stripe-przelewy24.js": "/js/clients/payments/stripe-przelewy24.js?id=3d53d2f7d0291d9f92cf7414dd2d351c", + "/js/clients/payments/stripe-browserpay.js": "/js/clients/payments/stripe-browserpay.js?id=db71055862995fd6ae21becfc587a3de", + "/js/clients/payments/stripe-fpx.js": "/js/clients/payments/stripe-fpx.js?id=914a6846ad1e5584635e7430fef76875", + "/css/app.css": "/css/app.css?id=aeba2a01bf369ac522071ab602096c66", "/css/card-js.min.css": "/css/card-js.min.css?id=62afeb675235451543ada60afcedcb7c", "/vendor/clipboard.min.js": "/vendor/clipboard.min.js?id=15f52a1ee547f2bdd46e56747332ca2d" } diff --git a/resources/js/clients/payments/stripe-bacs.js b/resources/js/clients/payments/stripe-bacs.js new file mode 100644 index 000000000000..a67c9057c588 --- /dev/null +++ b/resources/js/clients/payments/stripe-bacs.js @@ -0,0 +1,87 @@ +/** + * 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 ProcessBACS { + constructor(key, stripeConnect) { + this.key = key; + this.errors = document.getElementById('errors'); + this.stripeConnect = stripeConnect; + this.onlyAuthorization = onlyAuthorization; + } + + setupStripe = () => { + + if (this.stripeConnect){ + // this.stripe.stripeAccount = this.stripeConnect; + + this.stripe = Stripe(this.key, { + stripeAccount: this.stripeConnect, + }); + + } + else { + this.stripe = Stripe(this.key); + } + + + return this; + }; + payment_data; + + handle = () => { + + if (this.onlyAuthorization) { + document.getElementById('authorize-bacs').addEventListener('click', (e) => { + document.getElementById('authorize-bacs').disabled = true; + document.querySelector('#authorize-bacs > svg').classList.remove('hidden'); + document.querySelector('#authorize-bacs > span').classList.add('hidden'); + location.href=document.querySelector('meta[name=stripe-redirect-url]').content; + });} + else{ + this.payNowButton = document.getElementById('pay-now'); + document.getElementById('pay-now').addEventListener('click', (e) => { + this.payNowButton.disabled = true; + this.payNowButton.querySelector('svg').classList.remove('hidden'); + this.payNowButton.querySelector('span').classList.add('hidden'); + document.getElementById('server-response').submit(); + }); + + this.payment_data = Array.from(document.getElementsByClassName('toggle-payment-with-token')); + if (this.payment_data.length > 0){ + this.payment_data.forEach((element) => + element.addEventListener('click', (element) => { + document.querySelector('input[name=token]').value = + element.target.dataset.token; + }) + );} + else{ + this.errors.textContent = document.querySelector( + 'meta[name=translation-payment-method-required]' + ).content; + this.errors.hidden = false; + this.payNowButton.disabled = true; + this.payNowButton.querySelector('span').classList.remove('hidden'); + this.payNowButton.querySelector('svg').classList.add('hidden'); + }} + + + } +} + +const publishableKey = document.querySelector( + 'meta[name="stripe-publishable-key"]' +)?.content ?? ''; + +const stripeConnect = + document.querySelector('meta[name="stripe-account-id"]')?.content ?? ''; +const onlyAuthorization = + document.querySelector('meta[name="only-authorization"]')?.content ?? ''; + +new ProcessBACS(publishableKey, stripeConnect).setupStripe().handle(); diff --git a/resources/js/clients/payments/stripe-sepa.js b/resources/js/clients/payments/stripe-sepa.js index 8e9a74536187..cafcf8e7cc9c 100644 --- a/resources/js/clients/payments/stripe-sepa.js +++ b/resources/js/clients/payments/stripe-sepa.js @@ -5,7 +5,7 @@ * * @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 ProcessSEPA { @@ -215,6 +215,22 @@ class ProcessSEPA { document.querySelector('#pay-now > svg').classList.add('hidden'); document.querySelector('#pay-now > span').classList.remove('hidden'); } + handleSuccess(result) { + document.querySelector( + 'input[name="gateway_response"]' + ).value = JSON.stringify(result.paymentIntent); + + let tokenBillingCheckbox = document.querySelector( + 'input[name="token-billing-checkbox"]:checked' + ); + + if (tokenBillingCheckbox) { + document.querySelector('input[name="store_card"]').value = + tokenBillingCheckbox.value; + } + + document.getElementById('server-response').submit(); + } } const publishableKey = diff --git a/resources/views/portal/ninja2020/components/livewire/payment-methods-table.blade.php b/resources/views/portal/ninja2020/components/livewire/payment-methods-table.blade.php index 0a7ff18fea53..9d77a7c3a26b 100644 --- a/resources/views/portal/ninja2020/components/livewire/payment-methods-table.blade.php +++ b/resources/views/portal/ninja2020/components/livewire/payment-methods-table.blade.php @@ -25,6 +25,11 @@ {{ ctrans('texts.bank_account') }} @endif + @if($client->getBACSGateway()) + + {{ ctrans('texts.bacs') }} + + @endif @endif diff --git a/resources/views/portal/ninja2020/gateways/stripe/bacs/authorize.blade.php b/resources/views/portal/ninja2020/gateways/stripe/bacs/authorize.blade.php new file mode 100644 index 000000000000..a18484125868 --- /dev/null +++ b/resources/views/portal/ninja2020/gateways/stripe/bacs/authorize.blade.php @@ -0,0 +1,38 @@ +@extends('portal.ninja2020.layout.payments', ['gateway_title' => 'BACS', 'card_title' => 'BACS']) + +@section('gateway_head') + + @if($gateway->getConfigField('account_id')) + + + @else + + @endif + + + +@endsection + +@section('gateway_content') +
+ @csrf + + + + +
+ + + @component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.method')]) + {{ ctrans('texts.bacs') }} + @endcomponent + + @component('portal.ninja2020.gateways.includes.pay_now', ['id' => 'authorize-bacs']) + {{ ctrans('texts.add_payment_method') }} + @endcomponent +@endsection + +@section('gateway_footer') + + +@endsection diff --git a/resources/views/portal/ninja2020/gateways/stripe/bacs/pay.blade.php b/resources/views/portal/ninja2020/gateways/stripe/bacs/pay.blade.php new file mode 100644 index 000000000000..895d09fdae92 --- /dev/null +++ b/resources/views/portal/ninja2020/gateways/stripe/bacs/pay.blade.php @@ -0,0 +1,50 @@ +@extends('portal.ninja2020.layout.payments', ['gateway_title' => 'BACS', 'card_title' => 'BACS']) + +@section('gateway_head') + @if($gateway->company_gateway->getConfigField('account_id')) + + + @else + + @endif + + +@endsection + +@section('gateway_content') +
+ @csrf + + + + + +
+ + + + @include('portal.ninja2020.gateways.includes.payment_details') + + @component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.payment_type')]) + {{ ctrans('texts.bacs') }} ({{ ctrans('texts.bank_transfer') }}) + @endcomponent + + @component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.pay_with')]) + @if (count($tokens) > 0) + @foreach ($tokens as $token) + + @endforeach + @endisset + + @endcomponent + @include('portal.ninja2020.gateways.includes.pay_now') +@endsection + +@push('footer') + + +@endpush diff --git a/webpack.mix.js b/webpack.mix.js index df0604426709..4706ea5bda62 100644 --- a/webpack.mix.js +++ b/webpack.mix.js @@ -26,6 +26,10 @@ mix.js("resources/js/app.js", "public/js") "resources/js/clients/payments/stripe-klarna.js", "public/js/clients/payments/stripe-klarna.js" ) + .js( + "resources/js/clients/payments/stripe-bacs.js", + "public/js/clients/payments/stripe-bacs.js" + ) .js( "resources/js/clients/invoices/action-selectors.js", "public/js/clients/invoices/action-selectors.js"