stripe = $stripe; } public function authorizeView(array $data) { $data['gateway'] = $this->stripe; return render('gateways.stripe.ach.authorize', array_merge($data)); } public function authorizeResponse(Request $request) { $this->stripe->init(); $stripe_response = json_decode($request->input('gateway_response')); $customer = $this->stripe->findOrCreateCustomer(); try { $source = Customer::createSource($customer->id, ['source' => $stripe_response->token->id], $this->stripe->stripe_connect_auth); } catch (InvalidRequestException $e) { throw new PaymentFailed($e->getMessage(), $e->getCode()); } $client_gateway_token = $this->storePaymentMethod($source, $request->input('method'), $customer); $verification = route('client.payment_methods.verification', ['payment_method' => $client_gateway_token->hashed_id, 'method' => GatewayType::BANK_TRANSFER], false); $mailer = new NinjaMailerObject(); $mailer->mailable = new ACHVerificationNotification( auth()->guard('contact')->user()->client->company, route('client.contact_login', ['contact_key' => auth()->guard('contact')->user()->contact_key, 'next' => $verification]) ); $mailer->company = auth()->guard('contact')->user()->client->company; $mailer->settings = auth()->guard('contact')->user()->client->company->settings; $mailer->to_user = auth()->guard('contact')->user(); NinjaMailerJob::dispatch($mailer); return redirect()->route('client.payment_methods.verification', ['payment_method' => $client_gateway_token->hashed_id, 'method' => GatewayType::BANK_TRANSFER]); } public function verificationView(ClientGatewayToken $token) { if (isset($token->meta->state) && $token->meta->state === 'authorized') { return redirect() ->route('client.payment_methods.show', $token->hashed_id) ->with('message', __('texts.payment_method_verified')); } $data = [ 'token' => $token, 'gateway' => $this->stripe, ]; return render('gateways.stripe.ach.verify', $data); } public function processVerification(Request $request, ClientGatewayToken $token) { $request->validate([ 'transactions.*' => ['integer', 'min:1'], ]); if (isset($token->meta->state) && $token->meta->state === 'authorized') { return redirect() ->route('client.payment_methods.show', $token->hashed_id) ->with('message', __('texts.payment_method_verified')); } $this->stripe->init(); $bank_account = Customer::retrieveSource($request->customer, $request->source, [], $this->stripe->stripe_connect_auth); try { $bank_account->verify(['amounts' => request()->transactions]); $meta = $token->meta; $meta->state = 'authorized'; $token->meta = $meta; $token->save(); return redirect() ->route('client.payment_methods.show', $token->hashed_id) ->with('message', __('texts.payment_method_verified')); } catch (CardException $e) { return back()->with('error', $e->getMessage()); } } public function paymentView(array $data) { $data['gateway'] = $this->stripe; $data['currency'] = $this->stripe->client->getCurrencyCode(); $data['payment_method_id'] = GatewayType::BANK_TRANSFER; $data['customer'] = $this->stripe->findOrCreateCustomer(); $data['amount'] = $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency()); $intent = false; if(count($data['tokens']) == 0) { $intent = $this->stripe->createPaymentIntent([ 'amount' => $data['amount'], 'currency' => $data['currency'], 'setup_future_usage' => 'off_session', 'customer' => $data['customer']->id, 'payment_method_types' => ['us_bank_account'], ] ); } $data['client_secret'] = $intent ? $intent->client_secret : false; return render('gateways.stripe.ach.pay', $data); } public function tokenBilling(ClientGatewayToken $cgt, PaymentHash $payment_hash) { $amount = array_sum(array_column($payment_hash->invoices(), 'amount')) + $payment_hash->fee_total; $invoice = Invoice::whereIn('id', $this->transformKeys(array_column($payment_hash->invoices(), 'invoice_id'))) ->withTrashed() ->first(); if ($invoice) { $description = "Invoice {$invoice->number} for {$amount} for client {$this->stripe->client->present()->name()}"; } else { $description = "Payment with no invoice for amount {$amount} for client {$this->stripe->client->present()->name()}"; } $this->stripe->init(); $response = null; try { $state = [ 'gateway_type_id' => GatewayType::BANK_TRANSFER, 'amount' => $this->stripe->convertToStripeAmount($amount, $this->stripe->client->currency()->precision, $this->stripe->client->currency()), 'currency' => $this->stripe->client->getCurrencyCode(), 'customer' => $cgt->gateway_customer_reference, 'source' => $cgt->token, ]; $state['charge'] = \Stripe\Charge::create([ 'amount' => $state['amount'], 'currency' => $state['currency'], 'customer' => $state['customer'], 'source' => $state['source'], 'description' => $description, ], $this->stripe->stripe_connect_auth); $payment_hash->data = array_merge((array)$payment_hash->data, $state); $payment_hash->save(); if ($state['charge']->status === 'pending' && is_null($state['charge']->failure_message)) { return $this->processPendingPayment($state, false); } return $this->processUnsuccessfulPayment($state); } catch (Exception $e) { if ($e instanceof CardException) { return redirect()->route('client.payment_methods.verification', ['payment_method' => $cgt->hashed_id, 'method' => GatewayType::BANK_TRANSFER]); } throw new PaymentFailed($e->getMessage(), $e->getCode()); } } public function handlePaymentIntentResponse($request) { nlog($request->all()); dd($request->all()); } public function paymentResponse($request) { $this->stripe->init(); //it may be a payment intent here. if($request->input('client_secret')) $this->handlePaymentIntentResponse($request); $source = ClientGatewayToken::query() ->where('id', $this->decodePrimaryKey($request->source)) ->where('company_id', auth()->guard('contact')->user()->client->company->id) ->first(); if (!$source) { throw new PaymentFailed(ctrans('texts.payment_token_not_found'), 401); } $state = [ 'payment_method' => $request->payment_method_id, 'gateway_type_id' => $request->company_gateway_id, 'amount' => $this->stripe->convertToStripeAmount($request->amount, $this->stripe->client->currency()->precision, $this->stripe->client->currency()), 'currency' => $request->currency, 'customer' => $request->customer, ]; $state = array_merge($state, $request->all()); $state['source'] = $source->token; $this->stripe->payment_hash->data = array_merge((array)$this->stripe->payment_hash->data, $state); $this->stripe->payment_hash->save(); $amount = array_sum(array_column($this->stripe->payment_hash->invoices(), 'amount')) + $this->stripe->payment_hash->fee_total; $invoice = Invoice::whereIn('id', $this->transformKeys(array_column($this->stripe->payment_hash->invoices(), 'invoice_id'))) ->withTrashed() ->first(); if ($invoice) { $description = "Invoice {$invoice->number} for {$amount} for client {$this->stripe->client->present()->name()}"; } else { $description = "Payment with no invoice for amount {$amount} for client {$this->stripe->client->present()->name()}"; } try { $state['charge'] = \Stripe\Charge::create([ 'amount' => $state['amount'], 'currency' => $state['currency'], 'customer' => $state['customer'], 'source' => $state['source'], 'description' => $description, ], $this->stripe->stripe_connect_auth); $state = array_merge($state, $request->all()); $this->stripe->payment_hash->data = array_merge((array)$this->stripe->payment_hash->data, $state); $this->stripe->payment_hash->save(); 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 CardException) { return redirect()->route('client.payment_methods.verification', ['payment_method' => $source->hashed_id, 'method' => GatewayType::BANK_TRANSFER]); } throw new PaymentFailed($e->getMessage(), $e->getCode()); } } public function processPendingPayment($state, $client_present = true) { $this->stripe->init(); $data = [ 'payment_method' => $state['source'], 'payment_type' => PaymentType::ACH, 'amount' => $this->stripe->convertFromStripeAmount($this->stripe->payment_hash->data->amount, $this->stripe->client->currency()->precision, $this->stripe->client->currency()), 'transaction_reference' => $state['charge']->id, 'gateway_type_id' => GatewayType::BANK_TRANSFER, ]; $payment = $this->stripe->createPayment($data, Payment::STATUS_PENDING); SystemLogger::dispatch( ['response' => $state['charge'], 'data' => $data], SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_SUCCESS, SystemLog::TYPE_STRIPE, $this->stripe->client, $this->stripe->client->company, ); if(!$client_present) return $payment; return redirect()->route('client.payments.show', ['payment' => $this->stripe->encodePrimaryKey($payment->id)]); } public function processUnsuccessfulPayment($state) { $this->stripe->sendFailureMail($state['charge']); $message = [ 'server_response' => $state['charge'], 'data' => $this->stripe->payment_hash->data, ]; SystemLogger::dispatch( $message, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_STRIPE, $this->stripe->client ); throw new PaymentFailed('Failed to process the payment.', 500); } private function storePaymentMethod($method, $payment_method_id, $customer) { try { $payment_meta = new \stdClass; $payment_meta->brand = (string) \sprintf('%s (%s)', $method->bank_name, ctrans('texts.ach')); $payment_meta->last4 = (string) $method->last4; $payment_meta->type = GatewayType::BANK_TRANSFER; $payment_meta->state = 'unauthorized'; $data = [ 'payment_meta' => $payment_meta, 'token' => $method->id, 'payment_method_id' => $payment_method_id, ]; return $this->stripe->storeGatewayToken($data, ['gateway_customer_reference' => $customer->id]); } catch (Exception $e) { return $this->stripe->processInternallyFailedPayment($this->stripe, $e); } } }