diff --git a/app/Http/Controllers/ClientPortal/PaymentController.php b/app/Http/Controllers/ClientPortal/PaymentController.php index dd79784878eb..0f9c4fc32ab6 100644 --- a/app/Http/Controllers/ClientPortal/PaymentController.php +++ b/app/Http/Controllers/ClientPortal/PaymentController.php @@ -120,7 +120,7 @@ class PaymentController extends Controller return $gateway ->driver(auth()->user()->client) - ->setPaymentMethod('App\\PaymentDrivers\\Stripe\\SOFORT') + ->setPaymentMethod('App\\PaymentDrivers\\Stripe\\ACH') ->processPaymentView($data); } @@ -130,7 +130,7 @@ class PaymentController extends Controller return $gateway ->driver(auth()->user()->client) - ->setPaymentMethod('App\\PaymentDrivers\\Stripe\\SOFORT') + ->setPaymentMethod('App\\PaymentDrivers\\Stripe\\ACH') ->processPaymentResponse($request); } } diff --git a/app/Http/Controllers/ClientPortal/PaymentMethodController.php b/app/Http/Controllers/ClientPortal/PaymentMethodController.php index 892b40f11fc2..3359c9d02fb4 100644 --- a/app/Http/Controllers/ClientPortal/PaymentMethodController.php +++ b/app/Http/Controllers/ClientPortal/PaymentMethodController.php @@ -50,7 +50,7 @@ class PaymentMethodController extends Controller return $gateway ->driver(auth()->user()->client) - ->setPaymentMethod('App\\PaymentDrivers\\Stripe\\CreditCard') + ->setPaymentMethod('App\\PaymentDrivers\\Stripe\\ACH') ->authorizeView($data); } @@ -66,7 +66,7 @@ class PaymentMethodController extends Controller return $gateway ->driver(auth()->user()->client) - ->setPaymentMethod('App\\PaymentDrivers\\Stripe\\CreditCard') + ->setPaymentMethod('App\\PaymentDrivers\\Stripe\\ACH') ->authorizeCreditCardResponse($request); } @@ -106,6 +106,26 @@ class PaymentMethodController extends Controller // } + public function verify(ClientGatewayToken $payment_method) + { + $gateway = auth()->user()->client->getCreditCardGateway(); + + return $gateway + ->driver(auth()->user()->client) + ->setPaymentMethod('App\\PaymentDrivers\\Stripe\\ACH') + ->verificationView($payment_method); + } + + public function processVerification(ClientGatewaytoken $payment_method) + { + $gateway = auth()->user()->client->getCreditCardGateway(); + + return $gateway + ->driver(auth()->user()->client) + ->setPaymentMethod('App\\PaymentDrivers\\Stripe\\ACH') + ->processVerification($payment_method); + } + /** * Remove the specified resource from storage. * diff --git a/app/PaymentDrivers/Stripe/ACH.php b/app/PaymentDrivers/Stripe/ACH.php new file mode 100644 index 000000000000..b69abe1a2fc5 --- /dev/null +++ b/app/PaymentDrivers/Stripe/ACH.php @@ -0,0 +1,221 @@ +stripe = $stripe; + } + + public function authorizeView(array $data) + { + return render('gateways.stripe.ach.authorize', array_merge($data)); + } + + public function authorizeResponse($request) + { + $state = [ + 'server_response' => json_decode($request->gateway_response), + 'gateway_id' => $request->company_gateway_id, + 'gateway_type_id' => $request->gateway_type_id, + 'is_default' => $request->is_default, + ]; + + $customer = $this->stripe->findOrCreateCustomer(); + + $this->stripe->init(); + + $local_stripe = new \Stripe\StripeClient( + $this->stripe->company_gateway->getConfigField('apiKey') + ); + + try { + $local_stripe->customers->createSource( + $customer->id, + ['source' => $state['server_response']->token->id] + ); + } catch (InvalidRequestException $e) { + return back()->with('ach_error', $e->getMessage()); + } + + $payment_meta = $state['server_response']->token->bank_account; + $payment_meta->brand = ctrans('texts.ach'); + $payment_meta->type = ctrans('texts.bank_transfer'); + $payment_meta->verified_at = null; + $payment_meta->token = $state['server_response']->token->id; + + $client_gateway_token = new ClientGatewayToken(); + $client_gateway_token->company_id = $this->stripe->client->company->id; + $client_gateway_token->client_id = $this->stripe->client->id; + $client_gateway_token->token = $state['server_response']->token->bank_account->id; + $client_gateway_token->company_gateway_id = $this->stripe->company_gateway->id; + $client_gateway_token->gateway_type_id = $state['gateway_type_id']; + $client_gateway_token->gateway_customer_reference = $customer->id; + $client_gateway_token->meta = $payment_meta; + $client_gateway_token->save(); + + if ($state['is_default'] == 'true' || $this->stripe->client->gateway_tokens->count() == 1) { + $this->stripe->client->gateway_tokens()->update(['is_default' => 0]); + + $client_gateway_token->is_default = 1; + $client_gateway_token->save(); + } + + return redirect()->route('client.payment_methods.verification', $client_gateway_token->hashed_id); + } + + public function verificationView(ClientGatewayToken $token) + { + return render('gateways.stripe.ach.verify', compact('token')); + } + + public function processVerification(ClientGatewayToken $token) + { + $this->stripe->init(); + + $bank_account = \Stripe\Customer::retrieveSource( + request()->customer, + request()->source, + ); + + try { + $status = $bank_account->verify(['amounts' => request()->transactions]); + + $token->meta->verified_at = now(); + $token->save(); + + return redirect() + ->route('client.invoices.index') + ->with('success', __('texts.payment_method_verified')); + } catch (\Stripe\Exception\CardException $e) { + return back()->with('error', $e->getMessage()); + } + } + + public function paymentView(array $data) + { + $state = [ + 'amount' => $data['amount_with_fee'], + 'currency' => $this->stripe->client->getCurrencyCode(), + 'invoices' => $data['invoices'], + 'gateway' => $this->stripe, + 'payment_method_id' => GatewayType::BANK_TRANSFER, + 'token' => $data['token'], + 'customer' => $this->stripe->findOrCreateCustomer(), + ]; + + return render('gateways.stripe.ach.pay', $state); + } + + public function paymentResponse($request) + { + $state = [ + 'payment_method' => $request->payment_method_id, + 'gateway_type_id' => $request->company_gateway_id, + 'hashed_ids' => $request->hashed_ids, + 'amount' => $this->stripe->convertToStripeAmount($request->amount, $this->stripe->client->currency()->precision), + 'currency' => $request->currency, + 'source' => $request->source, + 'customer' => $request->customer, + ]; + + if ($this->stripe->getContact()) { + $state['client_contact'] = $this->stripe->getContact(); + } else { + $state['client_contact'] = $state['invoices']->first()->invitations->first()->contact; + } + + $this->stripe->init(); + + try { + $state['charge'] = \Stripe\Charge::create([ + 'amount' => $state['amount'], + 'currency' => $state['currency'], + 'customer' => $state['customer'], + 'source' => $state['source'], + ]); + + if ($state['charge']->status === 'pending' && is_null($state['charge']->failure_message)) { + return $this->processPendingPayment($state); + } + + return $this->processUnsuccessfulPayment($state); + } catch (\Exception $e) { + if ($e instanceof \Stripe\Exception\CardException) { + return redirect()->route('client.payment_methods.verification', ClientGatewayToken::first()->hashed_id); + } + } + } + + public function processPendingPayment($state) + { + $state['charge_id'] = $state['charge']->id; + + $this->stripe->init(); + + $state['payment_type'] = PaymentType::ACH; + + $data = [ + 'payment_method' => $state['charge_id'], + 'payment_type' => $state['payment_type'], + 'amount' => $state['charge']->amount, + ]; + + $payment = $this->stripe->createPayment($data, Payment::STATUS_PENDING); + + $this->stripe->attachInvoices($payment, $state['hashed_ids']); + + $payment->service()->updateInvoicePayment(); + + event(new PaymentWasCreated($payment, $payment->company)); + + $logger_message = [ + 'server_response' => $state['charge'], + 'data' => $data, + ]; + + SystemLogger::dispatch($logger_message, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_SUCCESS, SystemLog::TYPE_STRIPE, $this->stripe->client); + + return redirect()->route('client.payments.show', ['payment' => $this->stripe->encodePrimaryKey($payment->id)]); + } + + public function processUnsuccessfulPayment($state) + { + PaymentFailureMailer::dispatch($this->stripe->client, $state['charge']->failure_message, $this->stripe->client->company, $state['amount']); + + $message = [ + 'server_response' => $state['charge'], + 'data' => $state, + ]; + + SystemLogger::dispatch($message, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_STRIPE, $this->stripe->client); + + throw new \Exception('Failed to process the payment.', 1); + } +} diff --git a/webpack.mix.js b/webpack.mix.js index 7fc84ee76278..af87cd87c057 100644 --- a/webpack.mix.js +++ b/webpack.mix.js @@ -10,6 +10,10 @@ mix.js("resources/js/app.js", "public/js") "resources/js/clients/payment_methods/authorize-authorize-card.js", "public/js/clients/payment_methods/authorize-authorize-card.js" ) + .js( + "resources/js/clients/payment_methods/authorize-ach.js", + "public/js/clients/payment_methods/authorize-ach.js" + ) .js( "resources/js/clients/invoices/action-selectors.js", "public/js/clients/invoices/action-selectors.js"