From 7c70bc539428f7da97406303d5d89ec40d49bfc1 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 19 Jun 2023 14:25:42 +1000 Subject: [PATCH 01/51] Paypal Pro --- app/PaymentDrivers/PayPalProPaymentDriver.php | 253 ++++++++++++++++++ .../ninja2020/gateways/paypal/pay.blade.php | 42 +++ 2 files changed, 295 insertions(+) create mode 100644 app/PaymentDrivers/PayPalProPaymentDriver.php create mode 100644 resources/views/portal/ninja2020/gateways/paypal/pay.blade.php diff --git a/app/PaymentDrivers/PayPalProPaymentDriver.php b/app/PaymentDrivers/PayPalProPaymentDriver.php new file mode 100644 index 000000000000..66ea31b572b3 --- /dev/null +++ b/app/PaymentDrivers/PayPalProPaymentDriver.php @@ -0,0 +1,253 @@ +omnipay_gateway = Omnipay::create( + $this->company_gateway->gateway->provider + ); + + $this->omnipay_gateway->initialize((array) $this->company_gateway->getConfig()); + } + + public function setPaymentMethod($payment_method_id) + { + // PayPal doesn't have multiple ways of paying. + // There's just one, off-site redirect. + + return $this; + } + + public function authorizeView($payment_method) + { + // PayPal doesn't support direct authorization. + + return $this; + } + + public function authorizeResponse($request) + { + // PayPal doesn't support direct authorization. + + return $this; + } + + public function processPaymentView($data) + { + $this->initializeOmnipayGateway(); + + $data['gateway'] = $this; + + $this->payment_hash->data = array_merge((array) $this->payment_hash->data, ['amount' => $data['total']['amount_with_fee']]); + $this->payment_hash->save(); + + // $response = $this->omnipay_gateway + // ->purchase($this->generatePaymentDetails($data)) + // ->setItems($this->generatePaymentItems($data)) + // ->send(); + + // if ($response->isRedirect()) { + // return $response->redirect(); + // } + + // $this->sendFailureMail($response->getMessage() ?: ''); + + // $message = [ + // 'server_response' => $response->getMessage(), + // 'data' => $this->payment_hash->data, + // ]; + + // SystemLogger::dispatch( + // $message, + // SystemLog::CATEGORY_GATEWAY_RESPONSE, + // SystemLog::EVENT_GATEWAY_FAILURE, + // SystemLog::TYPE_PAYPAL, + // $this->client, + // $this->client->company, + // ); + + // throw new PaymentFailed($response->getMessage(), $response->getCode()); + + return render('gateways.paypal.pay', $data); + + } + + public function processPaymentResponse($request) + { + $this->initializeOmnipayGateway(); + + $response = $this->omnipay_gateway + ->completePurchase(['amount' => $this->payment_hash->data->amount, 'currency' => $this->client->getCurrencyCode()]) + ->send(); + + if ($response->isCancelled() && $this->client->getSetting('enable_client_portal')) { + return redirect()->route('client.invoices.index')->with('warning', ctrans('texts.status_cancelled')); + } elseif ($response->isCancelled() && !$this->client->getSetting('enable_client_portal')) { + redirect()->route('client.invoices.show', ['invoice' => $this->payment_hash->fee_invoice])->with('warning', ctrans('texts.status_cancelled')); + } + + if ($response->isSuccessful()) { + $data = [ + 'payment_method' => $response->getData()['TOKEN'], + 'payment_type' => PaymentType::PAYPAL, + 'amount' => $this->payment_hash->data->amount, + 'transaction_reference' => $response->getTransactionReference(), + 'gateway_type_id' => GatewayType::PAYPAL, + ]; + + $payment = $this->createPayment($data, \App\Models\Payment::STATUS_COMPLETED); + + SystemLogger::dispatch( + ['response' => (array) $response->getData(), 'data' => $data], + SystemLog::CATEGORY_GATEWAY_RESPONSE, + SystemLog::EVENT_GATEWAY_SUCCESS, + SystemLog::TYPE_PAYPAL, + $this->client, + $this->client->company, + ); + + return redirect()->route('client.payments.show', ['payment' => $this->encodePrimaryKey($payment->id)]); + } + + if (! $response->isSuccessful()) { + $data = $response->getData(); + + $this->sendFailureMail($response->getMessage() ?: ''); + + $message = [ + 'server_response' => $data['L_LONGMESSAGE0'], + 'data' => $this->payment_hash->data, + ]; + + SystemLogger::dispatch( + $message, + SystemLog::CATEGORY_GATEWAY_RESPONSE, + SystemLog::EVENT_GATEWAY_FAILURE, + SystemLog::TYPE_PAYPAL, + $this->client, + $this->client->company, + ); + + throw new PaymentFailed($response->getMessage(), $response->getCode()); + } + } + + public function generatePaymentDetails(array $data) + { + $_invoice = collect($this->payment_hash->data->invoices)->first(); + $invoice = Invoice::withTrashed()->find($this->decodePrimaryKey($_invoice->invoice_id)); + + // $this->fee = $this->feeCalc($invoice, $data['total']['amount_with_fee']); + + return [ + 'currency' => $this->client->getCurrencyCode(), + 'transactionType' => 'Purchase', + 'clientIp' => request()->getClientIp(), + // 'amount' => round(($data['total']['amount_with_fee'] + $this->fee),2), + 'amount' => round($data['total']['amount_with_fee'], 2), + 'returnUrl' => route('client.payments.response', [ + 'company_gateway_id' => $this->company_gateway->id, + 'payment_hash' => $this->payment_hash->hash, + 'payment_method_id' => GatewayType::PAYPAL, + ]), + 'cancelUrl' => $this->client->company->domain()."/client/invoices/{$invoice->hashed_id}", + 'description' => implode(',', collect($this->payment_hash->data->invoices) + ->map(function ($invoice) { + return sprintf('%s: %s', ctrans('texts.invoice_number'), $invoice->invoice_number); + })->toArray()), + 'transactionId' => $this->payment_hash->hash.'-'.time(), + 'ButtonSource' => 'InvoiceNinja_SP', + 'solutionType' => 'Sole', + ]; + } + + public function generatePaymentItems(array $data) + { + $_invoice = collect($this->payment_hash->data->invoices)->first(); + $invoice = Invoice::withTrashed()->find($this->decodePrimaryKey($_invoice->invoice_id)); + + $items = []; + + $items[] = new Item([ + 'name' => ' ', + 'description' => ctrans('texts.invoice_number').'# '.$invoice->number, + 'price' => $data['total']['amount_with_fee'], + 'quantity' => 1, + ]); + + return $items; + } + + private function feeCalc($invoice, $invoice_total) + { + $invoice->service()->removeUnpaidGatewayFees(); + $invoice = $invoice->fresh(); + + $balance = floatval($invoice->balance); + + $_updated_invoice = $invoice->service()->addGatewayFee($this->company_gateway, GatewayType::PAYPAL, $invoice_total)->save(); + + if (floatval($_updated_invoice->balance) > $balance) { + $fee = floatval($_updated_invoice->balance) - $balance; + + $this->payment_hash->fee_total = $fee; + $this->payment_hash->save(); + + return $fee; + } + + return 0; + } +} diff --git a/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php b/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php new file mode 100644 index 000000000000..934fb0a0a90d --- /dev/null +++ b/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php @@ -0,0 +1,42 @@ +@extends('portal.ninja2020.layout.payments', ['gateway_title' => ctrans('texts.payment_type_credit_card'), 'card_title' => ctrans('texts.payment_type_credit_card')]) + +@section('gateway_head') + +@endsection + +@section('gateway_content') + +
+ + + + + + +
+ + @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')]) + + @endcomponent + + @include('portal.ninja2020.gateways.includes.save_card') + + @include('portal.ninja2020.gateways.includes.pay_now') +@endsection + +@section('gateway_footer') +@endsection + +@push('footer') + +@endpush \ No newline at end of file From 452d158759591bc9e6871c7093ca19890644503e Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 19 Jun 2023 15:51:30 +1000 Subject: [PATCH 02/51] paypal pro --- .../ninja2020/gateways/paypal/pay.blade.php | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php b/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php index 934fb0a0a90d..65349257889e 100644 --- a/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php +++ b/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php @@ -14,7 +14,7 @@ - + @@ -27,6 +27,18 @@ @component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.pay_with')]) + + @endcomponent @include('portal.ninja2020.gateways.includes.save_card') @@ -38,5 +50,7 @@ @endsection @push('footer') - + @endpush \ No newline at end of file From ae4029a3e4dd2ac6d872353e109c013d34dabde4 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 20 Jun 2023 14:25:42 +1000 Subject: [PATCH 03/51] minor fixes --- app/Models/PaymentType.php | 2 + app/PaymentDrivers/PayPalProPaymentDriver.php | 62 ++-- .../ninja2020/gateways/paypal/pay.blade.php | 295 +++++++++++++++--- 3 files changed, 286 insertions(+), 73 deletions(-) diff --git a/app/Models/PaymentType.php b/app/Models/PaymentType.php index 12e716ca0034..85210d94dd9c 100644 --- a/app/Models/PaymentType.php +++ b/app/Models/PaymentType.php @@ -76,6 +76,7 @@ class PaymentType extends StaticModel const BACS = 49; const STRIPE_BANK_TRANSFER = 50; const CASH_APP = 51; + const VENMO = 25; public array $type_names = [ self::CREDIT => 'payment_type_Credit', @@ -119,6 +120,7 @@ class PaymentType extends StaticModel self::Interac_E_Transfer => 'payment_type_Interac E Transfer', self::STRIPE_BANK_TRANSFER => 'bank_transfer', self::CASH_APP => 'payment_type_Cash App', + self::VENMO => 'payment_type_Venmo', ]; public static function parseCardType($cardName) diff --git a/app/PaymentDrivers/PayPalProPaymentDriver.php b/app/PaymentDrivers/PayPalProPaymentDriver.php index 66ea31b572b3..bdfd64eb2421 100644 --- a/app/PaymentDrivers/PayPalProPaymentDriver.php +++ b/app/PaymentDrivers/PayPalProPaymentDriver.php @@ -12,15 +12,16 @@ namespace App\PaymentDrivers; -use App\Exceptions\PaymentFailed; -use App\Jobs\Util\SystemLogger; -use App\Models\GatewayType; -use App\Models\Invoice; -use App\Models\PaymentType; -use App\Models\SystemLog; -use App\Utils\Traits\MakesHash; -use Omnipay\Common\Item; use Omnipay\Omnipay; +use App\Models\Invoice; +use Omnipay\Common\Item; +use App\Models\SystemLog; +use App\Models\GatewayType; +use App\Models\PaymentType; +use App\Jobs\Util\SystemLogger; +use App\Utils\Traits\MakesHash; +use App\Exceptions\PaymentFailed; +use Illuminate\Support\Facades\Http; class PayPalProPaymentDriver extends BaseDriver { @@ -53,13 +54,23 @@ class PayPalProPaymentDriver extends BaseDriver * * @return void */ - private function initializeOmnipayGateway(): void + private function initializeOmnipayGateway(): self { $this->omnipay_gateway = Omnipay::create( $this->company_gateway->gateway->provider ); - $this->omnipay_gateway->initialize((array) $this->company_gateway->getConfig()); + $this->omnipay_gateway = Omnipay::create('PayPal_Rest'); + + // Initialise the gateway + $this->omnipay_gateway->initialize(array( + 'clientId' => 'AdRZZt44vJYAtXirmzMjnvUMoFIloN9kpuSgshQB7SJqLHbgtMP_rcmhy83FYY4a-c3R-_e4wZC8E3oG', + 'secret' => 'ENPRXSxr6Jy1YQWhh87eN4fSlNVj5uFT2PDmBqPs_QYJD8MXGcsvJATgR8Xc5sOb6T0q1AHCwfmv9B7n', + 'testMode' => true, // Or false when you are ready for live transactions + )); + + return $this; + // $this->omnipay_gateway->initialize((array) $this->company_gateway->getConfig()); } public function setPaymentMethod($payment_method_id) @@ -93,32 +104,19 @@ class PayPalProPaymentDriver extends BaseDriver $this->payment_hash->data = array_merge((array) $this->payment_hash->data, ['amount' => $data['total']['amount_with_fee']]); $this->payment_hash->save(); - // $response = $this->omnipay_gateway - // ->purchase($this->generatePaymentDetails($data)) - // ->setItems($this->generatePaymentItems($data)) - // ->send(); + // $data['token'] = base64_decode($this->omnipay_gateway->getToken()); - // if ($response->isRedirect()) { - // return $response->redirect(); - // } + $access_token = $this->omnipay_gateway->getToken(); - // $this->sendFailureMail($response->getMessage() ?: ''); + // $r = Http::withToken($access_token) + // ->withHeaders(['Accept-Language' => 'en_US'])->post("https://api-m.sandbox.paypal.com/v1/identity/generate-token",[]); - // $message = [ - // 'server_response' => $response->getMessage(), - // 'data' => $this->payment_hash->data, - // ]; + $r = Http:: + withToken($access_token) + ->post('https://api-m.sandbox.paypal.com/v1/identity/generate-token'); - // SystemLogger::dispatch( - // $message, - // SystemLog::CATEGORY_GATEWAY_RESPONSE, - // SystemLog::EVENT_GATEWAY_FAILURE, - // SystemLog::TYPE_PAYPAL, - // $this->client, - // $this->client->company, - // ); - - // throw new PaymentFailed($response->getMessage(), $response->getCode()); + dd($r); + nlog($r->body()); return render('gateways.paypal.pay', $data); diff --git a/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php b/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php index 65349257889e..087ab12fe563 100644 --- a/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php +++ b/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php @@ -2,55 +2,268 @@ @section('gateway_head') + + + @endsection @section('gateway_content') - -
- - - - - - -
- - @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')]) - - - - @endcomponent - - @include('portal.ninja2020.gateways.includes.save_card') - - @include('portal.ninja2020.gateways.includes.pay_now') + +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+ + +
+
+ + +
+
+ +
+
+ +
+
+ +
+
+ +
+

+ +
@endsection @section('gateway_footer') @endsection @push('footer') - + + @endpush \ No newline at end of file From 6c554172d91a1a4c1a6b44b6b2269766a1aeea3b Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 20 Jun 2023 15:51:29 +1000 Subject: [PATCH 04/51] Figured out auth --- app/PaymentDrivers/PayPalProPaymentDriver.php | 32 +++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/app/PaymentDrivers/PayPalProPaymentDriver.php b/app/PaymentDrivers/PayPalProPaymentDriver.php index bdfd64eb2421..52ba512f1543 100644 --- a/app/PaymentDrivers/PayPalProPaymentDriver.php +++ b/app/PaymentDrivers/PayPalProPaymentDriver.php @@ -14,6 +14,7 @@ namespace App\PaymentDrivers; use Omnipay\Omnipay; use App\Models\Invoice; +use App\Utils\CurlUtils; use Omnipay\Common\Item; use App\Models\SystemLog; use App\Models\GatewayType; @@ -106,17 +107,44 @@ class PayPalProPaymentDriver extends BaseDriver // $data['token'] = base64_decode($this->omnipay_gateway->getToken()); + // $access_token = $this->omnipay_gateway->createToken(); $access_token = $this->omnipay_gateway->getToken(); +nlog($access_token); // $r = Http::withToken($access_token) // ->withHeaders(['Accept-Language' => 'en_US'])->post("https://api-m.sandbox.paypal.com/v1/identity/generate-token",[]); +//curl -v https://api-m.sandbox.paypal.com/v1/oauth2/token \ -H "Accept: application/json" \ -H "Accept-Language: en_US" \ -u "client_id:secret" \ -d "grant_type=client_credentials" + + + // $basic = 'Basic ' . base64_encode("AdRZZt44vJYAtXirmzMjnvUMoFIloN9kpuSgshQB7SJqLHbgtMP_rcmhy83FYY4a-c3R-_e4wZC8E3oG:ENPRXSxr6Jy1YQWhh87eN4fSlNVj5uFT2PDmBqPs_QYJD8MXGcsvJATgR8Xc5sOb6T0q1AHCwfmv9B7n"); + + + $headers = [ + 'Accept' => 'application/json', + 'Content-type' => 'application/json', + // 'Authorization' => $basic, + 'Accept-Language' => 'en_US', + 'User-Agent' => 'curl/7.68.0' + ]; + + // $r = Http::withHeaders($headers)->post('https://api-m.sandbox.paypal.com/v1/oauth2/token?grant_type=client_credentials'); + + + // if ($response) { + // return $response; + // } + +// $r = Http::withToken($access_token) + // ->withHeaders(['Accept-Language' => 'en_US'])->post("https://api-m.sandbox.paypal.com/v1/identity/generate-token",[]); + $r = Http:: withToken($access_token) - ->post('https://api-m.sandbox.paypal.com/v1/identity/generate-token'); + ->withHeaders($headers) + ->post("https://api-m.sandbox.paypal.com/v1/identity/generate-token",['body' => '']); - dd($r); nlog($r->body()); + dd($r); return render('gateways.paypal.pay', $data); From 3bdc94ae64b24735c77abb74cae15cd0fb4e0080 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 20 Jun 2023 22:28:59 +1000 Subject: [PATCH 05/51] Paypal pro --- app/PaymentDrivers/PayPalProPaymentDriver.php | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/app/PaymentDrivers/PayPalProPaymentDriver.php b/app/PaymentDrivers/PayPalProPaymentDriver.php index 52ba512f1543..7d49219ad3fc 100644 --- a/app/PaymentDrivers/PayPalProPaymentDriver.php +++ b/app/PaymentDrivers/PayPalProPaymentDriver.php @@ -65,8 +65,8 @@ class PayPalProPaymentDriver extends BaseDriver // Initialise the gateway $this->omnipay_gateway->initialize(array( - 'clientId' => 'AdRZZt44vJYAtXirmzMjnvUMoFIloN9kpuSgshQB7SJqLHbgtMP_rcmhy83FYY4a-c3R-_e4wZC8E3oG', - 'secret' => 'ENPRXSxr6Jy1YQWhh87eN4fSlNVj5uFT2PDmBqPs_QYJD8MXGcsvJATgR8Xc5sOb6T0q1AHCwfmv9B7n', + 'clientId' => '', + 'secret' => '', 'testMode' => true, // Or false when you are ready for live transactions )); @@ -107,16 +107,7 @@ class PayPalProPaymentDriver extends BaseDriver // $data['token'] = base64_decode($this->omnipay_gateway->getToken()); - // $access_token = $this->omnipay_gateway->createToken(); $access_token = $this->omnipay_gateway->getToken(); -nlog($access_token); - - // $r = Http::withToken($access_token) - // ->withHeaders(['Accept-Language' => 'en_US'])->post("https://api-m.sandbox.paypal.com/v1/identity/generate-token",[]); -//curl -v https://api-m.sandbox.paypal.com/v1/oauth2/token \ -H "Accept: application/json" \ -H "Accept-Language: en_US" \ -u "client_id:secret" \ -d "grant_type=client_credentials" - - - // $basic = 'Basic ' . base64_encode("AdRZZt44vJYAtXirmzMjnvUMoFIloN9kpuSgshQB7SJqLHbgtMP_rcmhy83FYY4a-c3R-_e4wZC8E3oG:ENPRXSxr6Jy1YQWhh87eN4fSlNVj5uFT2PDmBqPs_QYJD8MXGcsvJATgR8Xc5sOb6T0q1AHCwfmv9B7n"); $headers = [ From 409aa60928b184c3a9f0ea3b6a27b41b8f843e75 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 20 Jun 2023 22:39:55 +1000 Subject: [PATCH 06/51] refactor for paypal rest --- ...Driver.php => PayPalRestPaymentDriver.php} | 38 +++----------- ..._123355_add_paypal_rest_payment_driver.php | 49 +++++++++++++++++++ database/seeders/PaymentLibrariesSeeder.php | 3 +- 3 files changed, 57 insertions(+), 33 deletions(-) rename app/PaymentDrivers/{PayPalProPaymentDriver.php => PayPalRestPaymentDriver.php} (85%) create mode 100644 database/migrations/2023_06_20_123355_add_paypal_rest_payment_driver.php diff --git a/app/PaymentDrivers/PayPalProPaymentDriver.php b/app/PaymentDrivers/PayPalRestPaymentDriver.php similarity index 85% rename from app/PaymentDrivers/PayPalProPaymentDriver.php rename to app/PaymentDrivers/PayPalRestPaymentDriver.php index 7d49219ad3fc..7ed1ab9a609d 100644 --- a/app/PaymentDrivers/PayPalProPaymentDriver.php +++ b/app/PaymentDrivers/PayPalRestPaymentDriver.php @@ -14,7 +14,6 @@ namespace App\PaymentDrivers; use Omnipay\Omnipay; use App\Models\Invoice; -use App\Utils\CurlUtils; use Omnipay\Common\Item; use App\Models\SystemLog; use App\Models\GatewayType; @@ -24,7 +23,7 @@ use App\Utils\Traits\MakesHash; use App\Exceptions\PaymentFailed; use Illuminate\Support\Facades\Http; -class PayPalProPaymentDriver extends BaseDriver +class PayPalRestPaymentDriver extends BaseDriver { use MakesHash; @@ -61,17 +60,9 @@ class PayPalProPaymentDriver extends BaseDriver $this->company_gateway->gateway->provider ); - $this->omnipay_gateway = Omnipay::create('PayPal_Rest'); - - // Initialise the gateway - $this->omnipay_gateway->initialize(array( - 'clientId' => '', - 'secret' => '', - 'testMode' => true, // Or false when you are ready for live transactions - )); + $this->omnipay_gateway->initialize((array) $this->company_gateway->getConfig()); - return $this; - // $this->omnipay_gateway->initialize((array) $this->company_gateway->getConfig()); + return $this; } public function setPaymentMethod($payment_method_id) @@ -105,34 +96,17 @@ class PayPalProPaymentDriver extends BaseDriver $this->payment_hash->data = array_merge((array) $this->payment_hash->data, ['amount' => $data['total']['amount_with_fee']]); $this->payment_hash->save(); - // $data['token'] = base64_decode($this->omnipay_gateway->getToken()); - $access_token = $this->omnipay_gateway->getToken(); - $headers = [ 'Accept' => 'application/json', 'Content-type' => 'application/json', - // 'Authorization' => $basic, 'Accept-Language' => 'en_US', - 'User-Agent' => 'curl/7.68.0' ]; - // $r = Http::withHeaders($headers)->post('https://api-m.sandbox.paypal.com/v1/oauth2/token?grant_type=client_credentials'); - - - // if ($response) { - // return $response; - // } - -// $r = Http::withToken($access_token) - // ->withHeaders(['Accept-Language' => 'en_US'])->post("https://api-m.sandbox.paypal.com/v1/identity/generate-token",[]); - - - $r = Http:: - withToken($access_token) - ->withHeaders($headers) - ->post("https://api-m.sandbox.paypal.com/v1/identity/generate-token",['body' => '']); + $r = Http::withToken($access_token) + ->withHeaders($headers) + ->post("https://api-m.sandbox.paypal.com/v1/identity/generate-token",['body' => '']); nlog($r->body()); dd($r); diff --git a/database/migrations/2023_06_20_123355_add_paypal_rest_payment_driver.php b/database/migrations/2023_06_20_123355_add_paypal_rest_payment_driver.php new file mode 100644 index 000000000000..d3d4b2a1c369 --- /dev/null +++ b/database/migrations/2023_06_20_123355_add_paypal_rest_payment_driver.php @@ -0,0 +1,49 @@ +clientId = ""; + $fields->secret = ""; + $fields->testMode = false; + + + $paypal = new Gateway; + $paypal->id = 60; + $paypal->name = 'PayPal REST'; + $paypal->key = '80af24a6a691230bbec33e930ab40665'; + $paypal->provider = 'PayPal_Rest'; + $paypal->is_offsite = false; + $paypal->fields = \json_encode($fields); + $paypal->visible = 1; + $paypal->site_url = 'https://www.paypal.com/'; + $paypal->save(); + } + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +}; diff --git a/database/seeders/PaymentLibrariesSeeder.php b/database/seeders/PaymentLibrariesSeeder.php index b7420fb8c4ee..4a096febef58 100644 --- a/database/seeders/PaymentLibrariesSeeder.php +++ b/database/seeders/PaymentLibrariesSeeder.php @@ -83,6 +83,7 @@ class PaymentLibrariesSeeder extends Seeder ['id' => 57, 'name' => 'Square', 'provider' => 'Square', 'is_offsite' => false, 'sort_order' => 21, 'key' => '65faab2ab6e3223dbe848b1686490baz', 'fields' => '{"accessToken":"","applicationId":"","locationId":"","testMode":false}'], ['id' => 58, 'name' => 'Razorpay', 'provider' => 'Razorpay', 'is_offsite' => false, 'sort_order' => 21, 'key' => 'hxd6gwg3ekb9tb3v9lptgx1mqyg69zu9', 'fields' => '{"apiKey":"","apiSecret":""}'], ['id' => 59, 'name' => 'Forte', 'provider' => 'Forte', 'is_offsite' => false, 'sort_order' => 21, 'key' => 'kivcvjexxvdiyqtj3mju5d6yhpeht2xs', 'fields' => '{"testMode":false,"apiLoginId":"","apiAccessId":"","secureKey":"","authOrganizationId":"","organizationId":"","locationId":""}'], + ['id' => 60, 'name' => 'PayPal REST', 'provider' => 'PayPal_Rest', 'key' => '80af24a6a691230bbec33e930ab40665', 'fields' => '{"clientId":"","secret":"","signature":"","testMode":false}'], ]; foreach ($gateways as $gateway) { @@ -99,7 +100,7 @@ class PaymentLibrariesSeeder extends Seeder Gateway::query()->update(['visible' => 0]); - Gateway::whereIn('id', [1,3,7,11,15,20,39,46,55,50,57,52,58,59])->update(['visible' => 1]); + Gateway::whereIn('id', [1,3,7,11,15,20,39,46,55,50,57,52,58,59,60])->update(['visible' => 1]); if (Ninja::isHosted()) { Gateway::whereIn('id', [20])->update(['visible' => 0]); From 8e62c5ac51ff0b010d2bbc5f98d47bafd316eae8 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 20 Jun 2023 23:10:25 +1000 Subject: [PATCH 07/51] Minor fixes --- app/Models/Gateway.php | 5 +++++ .../2023_06_20_123355_add_paypal_rest_payment_driver.php | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/Models/Gateway.php b/app/Models/Gateway.php index 56603bf3f05b..9fde400732e4 100644 --- a/app/Models/Gateway.php +++ b/app/Models/Gateway.php @@ -190,6 +190,11 @@ class Gateway extends StaticModel GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true], // Forte GatewayType::BANK_TRANSFER => ['refund' => true, 'token_billing' => true, 'webhooks' => [' ']], ]; + case 60: + return [ + GatewayType::PAYPAL => ['refund' => false, 'token_billing' => false], + ]; //Paypal + default: return []; } diff --git a/database/migrations/2023_06_20_123355_add_paypal_rest_payment_driver.php b/database/migrations/2023_06_20_123355_add_paypal_rest_payment_driver.php index d3d4b2a1c369..df40da51c578 100644 --- a/database/migrations/2023_06_20_123355_add_paypal_rest_payment_driver.php +++ b/database/migrations/2023_06_20_123355_add_paypal_rest_payment_driver.php @@ -23,7 +23,6 @@ return new class extends Migration $fields->secret = ""; $fields->testMode = false; - $paypal = new Gateway; $paypal->id = 60; $paypal->name = 'PayPal REST'; From f647139918a89afd32ec43f7ea6db64f2d8caaa3 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 21 Jun 2023 19:22:20 +1000 Subject: [PATCH 08/51] Working on Paypal REST integration --- .../PayPalRestPaymentDriver.php | 137 ++++++++++++++---- .../ninja2020/gateways/paypal/pay.blade.php | 125 ++++++---------- 2 files changed, 157 insertions(+), 105 deletions(-) diff --git a/app/PaymentDrivers/PayPalRestPaymentDriver.php b/app/PaymentDrivers/PayPalRestPaymentDriver.php index 7ed1ab9a609d..935181d99d8f 100644 --- a/app/PaymentDrivers/PayPalRestPaymentDriver.php +++ b/app/PaymentDrivers/PayPalRestPaymentDriver.php @@ -37,6 +37,8 @@ class PayPalRestPaymentDriver extends BaseDriver public const SYSTEM_LOG_TYPE = SystemLog::TYPE_PAYPAL; + private string $api_endpoint_url = ''; + public function gatewayTypes() { return [ @@ -45,16 +47,6 @@ class PayPalRestPaymentDriver extends BaseDriver } public function init() - { - return $this; - } - - /** - * Initialize Omnipay PayPal_Express gateway. - * - * @return void - */ - private function initializeOmnipayGateway(): self { $this->omnipay_gateway = Omnipay::create( $this->company_gateway->gateway->provider @@ -62,6 +54,8 @@ class PayPalRestPaymentDriver extends BaseDriver $this->omnipay_gateway->initialize((array) $this->company_gateway->getConfig()); + $this->api_endpoint_url = $this->company_gateway->getConfigField('testMode') ? 'https://api-m.sandbox.paypal.com' : 'https://api-m.paypal.com'; + return $this; } @@ -89,32 +83,121 @@ class PayPalRestPaymentDriver extends BaseDriver public function processPaymentView($data) { - $this->initializeOmnipayGateway(); + $this->init(); $data['gateway'] = $this; $this->payment_hash->data = array_merge((array) $this->payment_hash->data, ['amount' => $data['total']['amount_with_fee']]); $this->payment_hash->save(); - $access_token = $this->omnipay_gateway->getToken(); - - $headers = [ - 'Accept' => 'application/json', - 'Content-type' => 'application/json', - 'Accept-Language' => 'en_US', - ]; - - $r = Http::withToken($access_token) - ->withHeaders($headers) - ->post("https://api-m.sandbox.paypal.com/v1/identity/generate-token",['body' => '']); - - nlog($r->body()); - dd($r); + $data['client_id'] = $this->company_gateway->getConfigField('clientId'); + $data['token'] = $this->getClientToken(); + $data['order_id'] = $this->createOrder($data); return render('gateways.paypal.pay', $data); } + public function processPaymentResponse($request) + { + $this->init(); + + nlog($request->all()); + + $response = json_decode($request['gateway_response'], true); + + $order_id = $response['orderID']; + + nlog($order_id); + + $r = $this->gatewayRequest("/v2/checkout/orders/{$order_id}/capture", 'post', []); + + dd($r->body()); + + } + + private function getClientToken(): string + { + + $r = $this->gatewayRequest('/v1/identity/generate-token', 'post', ['body' => '']); + + if($r->successful()) + return $r->json()['client_token']; + + throw new PaymentFailed('Unable to gain client token from Paypal. Check your configuration', 401); + + } + + private function createOrder(array $data): string + { + + $_invoice = collect($this->payment_hash->data->invoices)->first(); + + $invoice = Invoice::withTrashed()->find($this->decodePrimaryKey($_invoice->invoice_id)); + + $order = [ + "intent" => "CAPTURE", + "purchase_units" => [ + [ + "description" =>ctrans('texts.invoice_number').'# '.$invoice->number, + "invoice_id" => $invoice->number, + 'reference_id' => 'PUHF', + 'description' => 'Sporting Goods', + 'custom_id' => 'CUST-HighFashions', + 'soft_descriptor' => 'HighFashions', + "amount" => [ + "value" => (string)$data['amount_with_fee'], + "currency_code"=> $this->client->currency()->code, + "breakdown" => [ + "item_total" => [ + "currency_code" => $this->client->currency()->code, + "value" => (string)$data['amount_with_fee'] + ] + ] + ], + "items"=> [ + [ + "name" => ctrans('texts.invoice_number').'# '.$invoice->number, + "quantity" => "1", + "unit_amount" => [ + "currency_code" => $this->client->currency()->code, + "value" => (string)$data['amount_with_fee'] + ], + ], + ], + ] + ] + ]; + + $r = $this->gatewayRequest('/v2/checkout/orders', 'post', $order); + + return $r->json()['id']; + + } + + public function gatewayRequest(string $uri, string $verb, array $data, ?array $headers = []) + { + $r = Http::withToken($this->omnipay_gateway->getToken()) + ->withHeaders($this->getHeaders($headers)) + ->{$verb}("{$this->api_endpoint_url}{$uri}", $data); + + if($r->successful()) + return $r; + + throw new PaymentFailed("Gateway failure - {$r->body()}", 401); + + } + + private function getHeaders(array $headers = []): array + { + return array_merge([ + 'Accept' => 'application/json', + 'Content-type' => 'application/json', + 'Accept-Language' => 'en_US', + ], $headers); + } + + /* public function processPaymentResponse($request) { $this->initializeOmnipayGateway(); @@ -221,6 +304,8 @@ class PayPalRestPaymentDriver extends BaseDriver return $items; } + */ + private function feeCalc($invoice, $invoice_total) { $invoice->service()->removeUnpaidGatewayFees(); @@ -241,4 +326,6 @@ class PayPalRestPaymentDriver extends BaseDriver return 0; } + + } diff --git a/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php b/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php index 087ab12fe563..b7f6dbffe518 100644 --- a/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php +++ b/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php @@ -1,8 +1,6 @@ @extends('portal.ninja2020.layout.payments', ['gateway_title' => ctrans('texts.payment_type_credit_card'), 'card_title' => ctrans('texts.payment_type_credit_card')]) @section('gateway_head') - - + @csrf + + + + + + + +
@@ -101,85 +108,18 @@ @endsection @push('footer') - @endpush \ No newline at end of file From 7a4cfbe7ccc6d0d77fa62837c41f8a213bd59305 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 21 Jun 2023 21:11:41 +1000 Subject: [PATCH 09/51] Working on Paypal REST integration --- .../PayPalRestPaymentDriver.php | 37 ++- app/Utils/Traits/Inviteable.php | 2 + .../ninja2020/gateways/paypal/pay.blade.php | 230 ++++-------------- 3 files changed, 69 insertions(+), 200 deletions(-) diff --git a/app/PaymentDrivers/PayPalRestPaymentDriver.php b/app/PaymentDrivers/PayPalRestPaymentDriver.php index 935181d99d8f..50925d760ea3 100644 --- a/app/PaymentDrivers/PayPalRestPaymentDriver.php +++ b/app/PaymentDrivers/PayPalRestPaymentDriver.php @@ -100,20 +100,35 @@ class PayPalRestPaymentDriver extends BaseDriver public function processPaymentResponse($request) { - $this->init(); - - nlog($request->all()); $response = json_decode($request['gateway_response'], true); - $order_id = $response['orderID']; + if($response['status'] == 'COMPLETED'){ - nlog($order_id); + $data = [ + 'payment_type' => PaymentType::PAYPAL, + 'amount' => $response['purchase_units'][0]['amount']['value'], + 'transaction_reference' => $response['purchase_units'][0]['payments']['captures'][0]['id'], + 'gateway_type_id' => GatewayType::PAYPAL, + ]; - $r = $this->gatewayRequest("/v2/checkout/orders/{$order_id}/capture", 'post', []); + $payment = $this->createPayment($data, \App\Models\Payment::STATUS_COMPLETED); - dd($r->body()); + SystemLogger::dispatch( + ['response' => $response, 'data' => $data], + SystemLog::CATEGORY_GATEWAY_RESPONSE, + SystemLog::EVENT_GATEWAY_SUCCESS, + SystemLog::TYPE_PAYPAL, + $this->client, + $this->client->company, + ); + return redirect()->route('client.payments.show', ['payment' => $this->encodePrimaryKey($payment->id)]); + + } + else { + throw new PaymentFailed('Payment failed. Please try again.', 401); + } } private function getClientToken(): string @@ -141,10 +156,10 @@ class PayPalRestPaymentDriver extends BaseDriver [ "description" =>ctrans('texts.invoice_number').'# '.$invoice->number, "invoice_id" => $invoice->number, - 'reference_id' => 'PUHF', - 'description' => 'Sporting Goods', - 'custom_id' => 'CUST-HighFashions', - 'soft_descriptor' => 'HighFashions', + // 'reference_id' => 'PUHF', + // 'description' => 'Sporting Goods', + // 'custom_id' => 'CUST-HighFashions', + // 'soft_descriptor' => 'HighFashions', "amount" => [ "value" => (string)$data['amount_with_fee'], "currency_code"=> $this->client->currency()->code, diff --git a/app/Utils/Traits/Inviteable.php b/app/Utils/Traits/Inviteable.php index 98584eed484a..f85f64be339b 100644 --- a/app/Utils/Traits/Inviteable.php +++ b/app/Utils/Traits/Inviteable.php @@ -68,6 +68,8 @@ trait Inviteable $qr = $writer->writeString($this->getPaymentLink(), 'utf-8'); + return "
$qr
"; + return " {$qr}"; } diff --git a/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php b/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php index b7f6dbffe518..82de6e218bfc 100644 --- a/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php +++ b/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php @@ -21,87 +21,7 @@
-
- - -
-
-
- -
-
-
- -
-
-
- - -
- - -
-
- - -
-
- -
-
- -
-
- -
-
- -
-

- - + @endsection @section('gateway_footer') @@ -113,122 +33,54 @@ @endpush \ No newline at end of file From cca107e00ed657e61cdf704b34640763cb6a4b6a Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 21 Jun 2023 22:19:13 +1000 Subject: [PATCH 10/51] Additional payment types for paypal --- app/Models/Gateway.php | 9 ++++++ app/Models/GatewayType.php | 28 +++++++++++-------- .../PayPalRestPaymentDriver.php | 4 +-- lang/en/texts.php | 3 ++ .../ninja2020/gateways/paypal/pay.blade.php | 21 ++++++++++---- 5 files changed, 46 insertions(+), 19 deletions(-) diff --git a/app/Models/Gateway.php b/app/Models/Gateway.php index 9fde400732e4..f041adac7909 100644 --- a/app/Models/Gateway.php +++ b/app/Models/Gateway.php @@ -193,6 +193,15 @@ class Gateway extends StaticModel case 60: return [ GatewayType::PAYPAL => ['refund' => false, 'token_billing' => false], + GatewayType::CREDIT_CARD => ['refund' => false, 'token_billing' => false], + GatewayType::VENMO => ['refund' => false, 'token_billing' => false], + GatewayType::SEPA => ['refund' => false, 'token_billing' => false], + GatewayType::BANCONTACT => ['refund' => false, 'token_billing' => false], + GatewayType::EPS => ['refund' => false, 'token_billing' => false], + GatewayType::MYBANK => ['refund' => false, 'token_billing' => false], + GatewayType::PAYLATER => ['refund' => false, 'token_billing' => false], + GatewayType::PRZELEWY24 => ['refund' => false, 'token_billing' => false], + GatewayType::SOFORT => ['refund' => false, 'token_billing' => false], ]; //Paypal default: diff --git a/app/Models/GatewayType.php b/app/Models/GatewayType.php index 2ca4159cd3e1..cdbd079cda51 100644 --- a/app/Models/GatewayType.php +++ b/app/Models/GatewayType.php @@ -29,17 +29,6 @@ namespace App\Models; * @method static \Illuminate\Database\Eloquent\Builder|GatewayType whereId($value) * @method static \Illuminate\Database\Eloquent\Builder|GatewayType whereName($value) * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods - * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods - * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods - * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods - * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods - * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods - * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods - * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods - * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods - * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods - * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods - * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods * @mixin \Eloquent */ class GatewayType extends StaticModel @@ -94,6 +83,14 @@ class GatewayType extends StaticModel const BACS = 24; + const VENMO = 25; + + const MERCADOPAGO = 26; + + const MYBANK = 27; + + const PAYLATER = 28; + public function gateway() { return $this->belongsTo(Gateway::class); @@ -153,9 +150,18 @@ class GatewayType extends StaticModel return ctrans('texts.fpx'); case self::KLARNA: return ctrans('texts.klarna'); + case self::VENMO: + return ctrans('texts.payment_type_Venmo'); + case self::MERCADOPAGO: + return ctrans('texts.mercado_pago'); + case self::MYBANK: + return ctrans('texts.mybank'); + case self::PAYLATER: + return ctrans('texts.paypal_paylater'); default: return ' '; break; } } } + diff --git a/app/PaymentDrivers/PayPalRestPaymentDriver.php b/app/PaymentDrivers/PayPalRestPaymentDriver.php index 50925d760ea3..e8b0a3a9897b 100644 --- a/app/PaymentDrivers/PayPalRestPaymentDriver.php +++ b/app/PaymentDrivers/PayPalRestPaymentDriver.php @@ -61,8 +61,8 @@ class PayPalRestPaymentDriver extends BaseDriver public function setPaymentMethod($payment_method_id) { - // PayPal doesn't have multiple ways of paying. - // There's just one, off-site redirect. +// ['paypal', 'card', 'venmo', 'sepa', 'bancontact', 'eps', 'giropay', 'ideal', 'mercadopago', 'mybank', 'paylater', 'p24', 'sofort'] + return $this; } diff --git a/lang/en/texts.php b/lang/en/texts.php index a51ee140b234..3ce8aecb3ed7 100644 --- a/lang/en/texts.php +++ b/lang/en/texts.php @@ -5109,6 +5109,9 @@ $LANG = array( 'add_gateway_help_message' => 'Add a payment gateway (ie. Stripe, WePay or PayPal) to accept online payments', 'lang_Hungarian' => 'Hungarian', 'use_mobile_to_manage_plan' => 'Use your phone subscription settings to manage your plan', + 'mercado_pago' => 'Mercado Pago', + 'mybank' => 'MyBank', + 'paypal_paylater' => 'Pay in 4', ); diff --git a/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php b/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php index 82de6e218bfc..e5a45262f8d7 100644 --- a/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php +++ b/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php @@ -62,12 +62,21 @@ var FUNDING_SOURCES = [ - // paypal.FUNDING.PAYPAL, - paypal.FUNDING.VENMO, - paypal.FUNDING.PAYLATER, - paypal.FUNDING.CREDIT, - paypal.FUNDING.CARD, - ] + paypal.FUNDING.PAYPAL, + paypal.FUNDING.CARD, + paypal.FUNDING.VENMO, + paypal.FUNDING.SEPA, + paypal.FUNDING.BANCONTACT, + paypal.FUNDING.EPS, + paypal.FUNDING.GIROPAY, + paypal.FUNDING.IDEAL, + paypal.FUNDING.MERCADOPAGO, + paypal.FUNDING.MYBANK, + paypal.FUNDING.PAYLATER, + paypal.FUNDING.P24, + paypal.FUNDING.SOFORT, + ]; + // Loop over each funding source FUNDING_SOURCES.forEach(function (fundingSource) { // Initialize the buttons From 9f969d9f7a2126d45cf64a81c9023b4b9690b905 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 23 Jun 2023 14:51:52 +1000 Subject: [PATCH 11/51] Watch shipping postal code --- app/Observers/ClientObserver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Observers/ClientObserver.php b/app/Observers/ClientObserver.php index 0b06b2cf3dee..e33316e1833a 100644 --- a/app/Observers/ClientObserver.php +++ b/app/Observers/ClientObserver.php @@ -88,7 +88,7 @@ class ClientObserver { /** Monitor postal code changes for US based clients for tax calculations */ - if($client->getOriginal('postal_code') != $client->postal_code && $client->country_id == 840 && $client->company->calculate_taxes && !$client->company->account->isFreeHostedClient()) { + if(($client->getOriginal('shipping_postal_code') != $client->shipping_postal_code || $client->getOriginal('postal_code') != $client->postal_code) && $client->country_id == 840 && $client->company->calculate_taxes && !$client->company->account->isFreeHostedClient()) { UpdateTaxData::dispatch($client, $client->company); } From 2efd4f55f0f0b64390a3ce8715be5ecbbf3bddc1 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 23 Jun 2023 20:05:36 +1000 Subject: [PATCH 12/51] Updated translations --- app/Console/Commands/CreateSingleAccount.php | 23 ++++++++++ .../PayPalRestPaymentDriver.php | 21 +++++++++ config/ninja.php | 1 + lang/en/texts.php | 1 + phpstan.neon | 4 +- .../ninja2020/gateways/paypal/pay.blade.php | 43 ++++--------------- 6 files changed, 56 insertions(+), 37 deletions(-) diff --git a/app/Console/Commands/CreateSingleAccount.php b/app/Console/Commands/CreateSingleAccount.php index 626b1d29249e..9d83e6b8f086 100644 --- a/app/Console/Commands/CreateSingleAccount.php +++ b/app/Console/Commands/CreateSingleAccount.php @@ -711,6 +711,29 @@ class CreateSingleAccount extends Command $cg->save(); } + if (config('ninja.testvars.paypal_rest') && ($this->gateway == 'all' || $this->gateway == 'paypal_rest')) { + $cg = new CompanyGateway; + $cg->company_id = $company->id; + $cg->user_id = $user->id; + $cg->gateway_key = '80af24a6a691230bbec33e930ab40665'; + $cg->require_cvv = true; + $cg->require_billing_address = true; + $cg->require_shipping_address = true; + $cg->update_details = true; + $cg->config = encrypt(config('ninja.testvars.paypal_rest')); + $cg->save(); + + $gateway_types = $cg->driver()->gatewayTypes(); + + $fees_and_limits = new stdClass; + $fees_and_limits->{$gateway_types[0]} = new FeesAndLimits; + + $cg->fees_and_limits = $fees_and_limits; + $cg->save(); + } + + + if (config('ninja.testvars.checkout') && ($this->gateway == 'all' || $this->gateway == 'checkout')) { $cg = new CompanyGateway; $cg->company_id = $company->id; diff --git a/app/PaymentDrivers/PayPalRestPaymentDriver.php b/app/PaymentDrivers/PayPalRestPaymentDriver.php index e8b0a3a9897b..25b141c7a764 100644 --- a/app/PaymentDrivers/PayPalRestPaymentDriver.php +++ b/app/PaymentDrivers/PayPalRestPaymentDriver.php @@ -93,11 +93,17 @@ class PayPalRestPaymentDriver extends BaseDriver $data['client_id'] = $this->company_gateway->getConfigField('clientId'); $data['token'] = $this->getClientToken(); $data['order_id'] = $this->createOrder($data); + $data['funding_options'] = $this->getFundingOptions(); return render('gateways.paypal.pay', $data); } + private function getFundingOptions():string + { + + } + public function processPaymentResponse($request) { @@ -152,6 +158,21 @@ class PayPalRestPaymentDriver extends BaseDriver $order = [ "intent" => "CAPTURE", + "payer" => [ + "name" => [ + "given_name" => $this->client->present()->first_name(), + "surname" => $this->client->present()->last_name(), + ], + "email_address" => $this->client->present()->email(), + "address" => [ + "address_line_1" => $this->client->address1, + "address_line_2" => $this->client->address2, + "admin_area_1" => $this->client->city, + "admin_area_2" => $this->client->state, + "postal_code" => $this->client->postal_code, + "country_code" => $this->client->country->iso_3166_2, + ] + ], "purchase_units" => [ [ "description" =>ctrans('texts.invoice_number').'# '.$invoice->number, diff --git a/config/ninja.php b/config/ninja.php index 77cf14b0d32d..aff82f1a9cdd 100644 --- a/config/ninja.php +++ b/config/ninja.php @@ -84,6 +84,7 @@ return [ 'password' => 'password', 'stripe' => env('STRIPE_KEYS', ''), 'paypal' => env('PAYPAL_KEYS', ''), + 'paypal_rest' => env('PAYPAL_REST_KEYS', ''), 'authorize' => env('AUTHORIZE_KEYS', ''), 'checkout' => env('CHECKOUT_KEYS', ''), 'travis' => env('TRAVIS', false), diff --git a/lang/en/texts.php b/lang/en/texts.php index 3ce8aecb3ed7..e3ee2134067c 100644 --- a/lang/en/texts.php +++ b/lang/en/texts.php @@ -5112,6 +5112,7 @@ $LANG = array( 'mercado_pago' => 'Mercado Pago', 'mybank' => 'MyBank', 'paypal_paylater' => 'Pay in 4', + 'paid_date' => 'Paid Date', ); diff --git a/phpstan.neon b/phpstan.neon index 8bbf3a89f08a..0012887ccc40 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -7,11 +7,11 @@ parameters: - '#Call to an undefined method .*badMethod\(\)#' - '#Call to an undefined method Illuminate\Database\Eloquent\Builder::exclude#' parallel: - maximumNumberOfProcesses: 8 + maximumNumberOfProcesses: 1 level: 4 paths: - 'app/' excludePaths: - 'vendor/' universalObjectCratesClasses: - - App\DataMapper\Tax\RuleInterface \ No newline at end of file + - App\DataMapper\Tax\RuleInterface diff --git a/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php b/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php index e5a45262f8d7..c86f619d5c1b 100644 --- a/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php +++ b/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php @@ -1,4 +1,4 @@ -@extends('portal.ninja2020.layout.payments', ['gateway_title' => ctrans('texts.payment_type_credit_card'), 'card_title' => ctrans('texts.payment_type_credit_card')]) +@extends('portal.ninja2020.layout.payments', ['gateway_title' => ctrans('texts.payment_type_credit_card'), 'card_title' => 'PayPal']) @section('gateway_head') + From 78532572344009cad5aa99c79233f8e8029a3d03 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 23 Jun 2023 23:04:14 +1000 Subject: [PATCH 13/51] minoir fixes --- .../PayPalRestPaymentDriver.php | 68 +++++++++++++++++-- 1 file changed, 61 insertions(+), 7 deletions(-) diff --git a/app/PaymentDrivers/PayPalRestPaymentDriver.php b/app/PaymentDrivers/PayPalRestPaymentDriver.php index 25b141c7a764..a522e2887b8a 100644 --- a/app/PaymentDrivers/PayPalRestPaymentDriver.php +++ b/app/PaymentDrivers/PayPalRestPaymentDriver.php @@ -14,7 +14,6 @@ namespace App\PaymentDrivers; use Omnipay\Omnipay; use App\Models\Invoice; -use Omnipay\Common\Item; use App\Models\SystemLog; use App\Models\GatewayType; use App\Models\PaymentType; @@ -41,9 +40,37 @@ class PayPalRestPaymentDriver extends BaseDriver public function gatewayTypes() { - return [ - GatewayType::PAYPAL, + + $enums = [ + 3 => 'paypal', + 1 => 'card', + 25 => 'venmo', + 9 => 'sepa', + 12 => 'bancontact', + 17 => 'eps', + 15 => 'giropay', + 13 => 'ideal', + 26 => 'mercadopago', + 27 => 'mybank', + 28 => 'paylater', + 16 => 'p24', + 7 => 'sofort' ]; + + $funding_options = []; + + foreach ($this->company_gateway->fees_and_limits as $key => $value) { + if ($value->is_enabled) { + $funding_options[] = $key; + } + } + + return $funding_options; + + + // return [ + // GatewayType::PAYPAL, + // ]; } public function init() @@ -61,9 +88,6 @@ class PayPalRestPaymentDriver extends BaseDriver public function setPaymentMethod($payment_method_id) { -// ['paypal', 'card', 'venmo', 'sepa', 'bancontact', 'eps', 'giropay', 'ideal', 'mercadopago', 'mybank', 'paylater', 'p24', 'sofort'] - - return $this; } @@ -101,7 +125,37 @@ class PayPalRestPaymentDriver extends BaseDriver private function getFundingOptions():string { - + + $enums = [ + 3 => 'paypal', + 1 => 'card', + 25 => 'venmo', + 9 => 'sepa', + 12 => 'bancontact', + 17 => 'eps', + 15 => 'giropay', + 13 => 'ideal', + 26 => 'mercadopago', + 27 => 'mybank', + 28 => 'paylater', + 16 => 'p24', + 7 => 'sofort' + ]; + + $funding_options = ''; + + foreach($this->company_gateway->fees_and_limits as $key => $value) { + + if($value->is_enabled) { + + $funding_options .=$enums[$key].','; + + } + + } + + return rtrim($funding_options, ','); + } public function processPaymentResponse($request) From 8aa8d6521c5a67c17f270cf70899f64bc0558dc4 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sun, 23 Jul 2023 09:32:05 +1000 Subject: [PATCH 14/51] Updates for paypal rest --- app/Models/Gateway.php | 14 ++-- .../PayPalRestPaymentDriver.php | 69 +++++++++---------- .../ninja2020/gateways/paypal/pay.blade.php | 2 +- 3 files changed, 41 insertions(+), 44 deletions(-) diff --git a/app/Models/Gateway.php b/app/Models/Gateway.php index f041adac7909..2d3107a14138 100644 --- a/app/Models/Gateway.php +++ b/app/Models/Gateway.php @@ -195,13 +195,13 @@ class Gateway extends StaticModel GatewayType::PAYPAL => ['refund' => false, 'token_billing' => false], GatewayType::CREDIT_CARD => ['refund' => false, 'token_billing' => false], GatewayType::VENMO => ['refund' => false, 'token_billing' => false], - GatewayType::SEPA => ['refund' => false, 'token_billing' => false], - GatewayType::BANCONTACT => ['refund' => false, 'token_billing' => false], - GatewayType::EPS => ['refund' => false, 'token_billing' => false], - GatewayType::MYBANK => ['refund' => false, 'token_billing' => false], - GatewayType::PAYLATER => ['refund' => false, 'token_billing' => false], - GatewayType::PRZELEWY24 => ['refund' => false, 'token_billing' => false], - GatewayType::SOFORT => ['refund' => false, 'token_billing' => false], + // GatewayType::SEPA => ['refund' => false, 'token_billing' => false], + // GatewayType::BANCONTACT => ['refund' => false, 'token_billing' => false], + // GatewayType::EPS => ['refund' => false, 'token_billing' => false], + // GatewayType::MYBANK => ['refund' => false, 'token_billing' => false], + // GatewayType::PAYLATER => ['refund' => false, 'token_billing' => false], + // GatewayType::PRZELEWY24 => ['refund' => false, 'token_billing' => false], + // GatewayType::SOFORT => ['refund' => false, 'token_billing' => false], ]; //Paypal default: diff --git a/app/PaymentDrivers/PayPalRestPaymentDriver.php b/app/PaymentDrivers/PayPalRestPaymentDriver.php index a522e2887b8a..1157554cef8a 100644 --- a/app/PaymentDrivers/PayPalRestPaymentDriver.php +++ b/app/PaymentDrivers/PayPalRestPaymentDriver.php @@ -38,25 +38,28 @@ class PayPalRestPaymentDriver extends BaseDriver private string $api_endpoint_url = ''; + private string $paypal_payment_method = ''; + + private array $funding_options = [ + 3 => 'paypal', + 1 => 'card', + 25 => 'venmo', + 9 => 'sepa', + 12 => 'bancontact', + 17 => 'eps', + 15 => 'giropay', + 13 => 'ideal', + 26 => 'mercadopago', + 27 => 'mybank', + 28 => 'paylater', + 16 => 'p24', + 7 => 'sofort' + ]; + + public function gatewayTypes() { - $enums = [ - 3 => 'paypal', - 1 => 'card', - 25 => 'venmo', - 9 => 'sepa', - 12 => 'bancontact', - 17 => 'eps', - 15 => 'giropay', - 13 => 'ideal', - 26 => 'mercadopago', - 27 => 'mybank', - 28 => 'paylater', - 16 => 'p24', - 7 => 'sofort' - ]; - $funding_options = []; foreach ($this->company_gateway->fees_and_limits as $key => $value) { @@ -65,12 +68,8 @@ class PayPalRestPaymentDriver extends BaseDriver } } - return $funding_options; + return $funding_options; - - // return [ - // GatewayType::PAYPAL, - // ]; } public function init() @@ -88,6 +87,8 @@ class PayPalRestPaymentDriver extends BaseDriver public function setPaymentMethod($payment_method_id) { + $this->paypal_payment_method = $this->funding_options[$payment_method_id]; + return $this; } @@ -117,7 +118,7 @@ class PayPalRestPaymentDriver extends BaseDriver $data['client_id'] = $this->company_gateway->getConfigField('clientId'); $data['token'] = $this->getClientToken(); $data['order_id'] = $this->createOrder($data); - $data['funding_options'] = $this->getFundingOptions(); + $data['funding_options'] = $this->paypal_payment_method; return render('gateways.paypal.pay', $data); @@ -130,16 +131,16 @@ class PayPalRestPaymentDriver extends BaseDriver 3 => 'paypal', 1 => 'card', 25 => 'venmo', - 9 => 'sepa', - 12 => 'bancontact', - 17 => 'eps', - 15 => 'giropay', - 13 => 'ideal', - 26 => 'mercadopago', - 27 => 'mybank', - 28 => 'paylater', - 16 => 'p24', - 7 => 'sofort' + // 9 => 'sepa', + // 12 => 'bancontact', + // 17 => 'eps', + // 15 => 'giropay', + // 13 => 'ideal', + // 26 => 'mercadopago', + // 27 => 'mybank', + // 28 => 'paylater', + // 16 => 'p24', + // 7 => 'sofort' ]; $funding_options = ''; @@ -231,10 +232,6 @@ class PayPalRestPaymentDriver extends BaseDriver [ "description" =>ctrans('texts.invoice_number').'# '.$invoice->number, "invoice_id" => $invoice->number, - // 'reference_id' => 'PUHF', - // 'description' => 'Sporting Goods', - // 'custom_id' => 'CUST-HighFashions', - // 'soft_descriptor' => 'HighFashions', "amount" => [ "value" => (string)$data['amount_with_fee'], "currency_code"=> $this->client->currency()->code, diff --git a/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php b/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php index c86f619d5c1b..3c7cd18d0a5c 100644 --- a/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php +++ b/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php @@ -28,7 +28,7 @@ @endsection @push('footer') - -@endpush - @section('body')
@@ -31,8 +24,8 @@
- @include('portal.ninja2020.components.entity-documents', ['entity' => $credit]) - @livewire('pdf-slot', ['entity' => $credit, 'invitation' => $invitation, 'db' => $invitation->company->db]) +@include('portal.ninja2020.components.entity-documents', ['entity' => $credit]) +@livewire('pdf-slot', ['entity' => $credit, 'invitation' => $invitation, 'db' => $invitation->company->db]) @endsection diff --git a/resources/views/portal/ninja2020/invoices/show-fullscreen.blade.php b/resources/views/portal/ninja2020/invoices/show-fullscreen.blade.php index 8de82110754e..bedbdd2d2de2 100644 --- a/resources/views/portal/ninja2020/invoices/show-fullscreen.blade.php +++ b/resources/views/portal/ninja2020/invoices/show-fullscreen.blade.php @@ -1,2 +1 @@ - - + \ No newline at end of file diff --git a/resources/views/portal/ninja2020/purchase_orders/show-fullscreen.blade.php b/resources/views/portal/ninja2020/purchase_orders/show-fullscreen.blade.php index 2aa2bf4b229b..b7805f506e30 100644 --- a/resources/views/portal/ninja2020/purchase_orders/show-fullscreen.blade.php +++ b/resources/views/portal/ninja2020/purchase_orders/show-fullscreen.blade.php @@ -1,2 +1 @@ - - + \ No newline at end of file diff --git a/resources/views/portal/ninja2020/quotes/show-fullscreen.blade.php b/resources/views/portal/ninja2020/quotes/show-fullscreen.blade.php index 22043d045fe8..18d480091a4a 100644 --- a/resources/views/portal/ninja2020/quotes/show-fullscreen.blade.php +++ b/resources/views/portal/ninja2020/quotes/show-fullscreen.blade.php @@ -1,3 +1 @@ - - - + \ No newline at end of file diff --git a/resources/views/portal/ninja2020/quotes/show.blade.php b/resources/views/portal/ninja2020/quotes/show.blade.php index 7a0b9f2c3340..4b981fb5d4c2 100644 --- a/resources/views/portal/ninja2020/quotes/show.blade.php +++ b/resources/views/portal/ninja2020/quotes/show.blade.php @@ -2,15 +2,9 @@ @section('meta_title', ctrans('texts.entity_number_placeholder', ['entity' => ctrans('texts.quote'), 'entity_number' => $quote->number])) @push('head') - - - - - @include('portal.ninja2020.components.no-cache') - @endpush From 2a3c652b0c087f5c180e567bc1cb4706426ccab6 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 26 Jul 2023 13:18:31 +1000 Subject: [PATCH 44/51] Static analysis cleanup --- .../ClientPortal/PaymentController.php | 1 - app/Http/Controllers/InvoiceController.php | 5 -- app/Jobs/RecurringInvoice/SendRecurring.php | 3 -- app/Jobs/Util/ReminderJob.php | 6 --- app/Models/User.php | 54 ------------------- app/Services/Invoice/ApplyPaymentAmount.php | 1 - app/Services/Invoice/MarkSent.php | 1 - 7 files changed, 71 deletions(-) diff --git a/app/Http/Controllers/ClientPortal/PaymentController.php b/app/Http/Controllers/ClientPortal/PaymentController.php index 826fb9fb15ff..ef3eb52c87ec 100644 --- a/app/Http/Controllers/ClientPortal/PaymentController.php +++ b/app/Http/Controllers/ClientPortal/PaymentController.php @@ -177,7 +177,6 @@ class PaymentController extends Controller if ($invoices->sum('balance') > 0) { $invoice = $invoices->first(); - $invoice->service()->touchPdf(true); return redirect()->route('client.invoice.show', ['invoice' => $invoice->hashed_id, 'hash' => $request->input('hash')]); } diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index 636f0479531b..d52068faa310 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -683,7 +683,6 @@ class InvoiceController extends BaseController return $this->itemResponse($quote); - break; case 'history': // code... break; @@ -717,7 +716,6 @@ class InvoiceController extends BaseController echo Storage::get($file); }, basename($file), ['Content-Type' => 'application/pdf']); - break; case 'restore': $this->invoice_repo->restore($invoice); @@ -742,8 +740,6 @@ class InvoiceController extends BaseController break; case 'cancel': $invoice = $invoice->service()->handleCancellation()->deletePdf()->save(); - // $invoice = $invoice->service()->handleCancellation()->touchPdf()->save(); - if (! $bulk) { $this->itemResponse($invoice); } @@ -765,7 +761,6 @@ class InvoiceController extends BaseController default: return response()->json(['message' => ctrans('texts.action_unavailable', ['action' => $action])], 400); - break; } } diff --git a/app/Jobs/RecurringInvoice/SendRecurring.php b/app/Jobs/RecurringInvoice/SendRecurring.php index 808580736484..c398f0f2d363 100644 --- a/app/Jobs/RecurringInvoice/SendRecurring.php +++ b/app/Jobs/RecurringInvoice/SendRecurring.php @@ -91,9 +91,6 @@ class SendRecurring implements ShouldQueue $invoice = $this->createRecurringInvitations($invoice); - /* 09-01-2022 ensure we create the PDFs at this point in time! */ - $invoice->service()->touchPdf(true); - /* Set next date here to prevent a recurring loop forming */ $this->recurring_invoice->next_send_date = $this->recurring_invoice->nextSendDate(); $this->recurring_invoice->next_send_date_client = $this->recurring_invoice->nextSendDateClient(); diff --git a/app/Jobs/Util/ReminderJob.php b/app/Jobs/Util/ReminderJob.php index 1c10a0a3d5c4..2f80eeb6c628 100644 --- a/app/Jobs/Util/ReminderJob.php +++ b/app/Jobs/Util/ReminderJob.php @@ -205,9 +205,6 @@ class ReminderJob implements ShouldQueue ->markSent() ->save(); - //30-6-2023 - fix for duplicate touching - // $invoice->service()->touchPdf(true); - $enabled_reminder = 'enable_'.$reminder_template; if ($reminder_template == 'endless_reminder') { $enabled_reminder = 'enable_reminder_endless'; @@ -308,14 +305,11 @@ class ReminderJob implements ShouldQueue /**Refresh Invoice values*/ $invoice = $invoice->calc()->getInvoice(); - // $invoice->service()->deletePdf(); 24-11-2022 no need to delete here because we regenerate later anyway nlog('adjusting client balance and invoice balance by #'.$invoice->number.' '.($invoice->balance - $temp_invoice_balance)); $invoice->client->service()->updateBalance($invoice->balance - $temp_invoice_balance); $invoice->ledger()->updateInvoiceBalance($invoice->balance - $temp_invoice_balance, "Late Fee Adjustment for invoice {$invoice->number}"); - $invoice->service()->touchPdf(true); - return $invoice; } } diff --git a/app/Models/User.php b/app/Models/User.php index 9ceb5f9ad2f3..814d501052a1 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -77,16 +77,6 @@ use Illuminate\Foundation\Auth\User as Authenticatable; * @property int $verified_phone_number * @property-read \App\Models\Account $account * @property-read \App\Models\Company $company - * @property-read \Illuminate\Database\Eloquent\Collection $clients - * @property-read int|null $clients_count - * @property-read \Illuminate\Database\Eloquent\Collection $companies - * @property-read int|null $companies_count - * @property-read \Illuminate\Database\Eloquent\Collection $company_users - * @property-read int|null $company_users_count - * @property-read \Illuminate\Database\Eloquent\Collection $contacts - * @property-read int|null $contacts_count - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read int|null $documents_count * @property-read mixed $hashed_id * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications * @property-read int|null $notifications_count @@ -99,53 +89,9 @@ use Illuminate\Foundation\Auth\User as Authenticatable; * @method static \Illuminate\Database\Eloquent\Builder|User onlyTrashed() * @method static \Illuminate\Database\Eloquent\Builder|User query() * @method static \Illuminate\Database\Eloquent\Builder|User where($column, $value) - * @method static \Illuminate\Database\Eloquent\Builder|User whereAcceptedTermsVersion($value) - * @method static \Illuminate\Database\Eloquent\Builder|User whereAccountId($value) - * @method static \Illuminate\Database\Eloquent\Builder|User whereAvatar($value) - * @method static \Illuminate\Database\Eloquent\Builder|User whereAvatarHeight($value) - * @method static \Illuminate\Database\Eloquent\Builder|User whereAvatarSize($value) - * @method static \Illuminate\Database\Eloquent\Builder|User whereAvatarWidth($value) - * @method static \Illuminate\Database\Eloquent\Builder|User whereConfirmationCode($value) - * @method static \Illuminate\Database\Eloquent\Builder|User whereCreatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|User whereCustomValue1($value) - * @method static \Illuminate\Database\Eloquent\Builder|User whereCustomValue2($value) - * @method static \Illuminate\Database\Eloquent\Builder|User whereCustomValue3($value) - * @method static \Illuminate\Database\Eloquent\Builder|User whereCustomValue4($value) - * @method static \Illuminate\Database\Eloquent\Builder|User whereDeletedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|User whereDeviceToken($value) - * @method static \Illuminate\Database\Eloquent\Builder|User whereEmail($value) - * @method static \Illuminate\Database\Eloquent\Builder|User whereEmailVerifiedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|User whereFailedLogins($value) - * @method static \Illuminate\Database\Eloquent\Builder|User whereFirstName($value) - * @method static \Illuminate\Database\Eloquent\Builder|User whereGoogle2faSecret($value) - * @method static \Illuminate\Database\Eloquent\Builder|User whereHasPassword($value) - * @method static \Illuminate\Database\Eloquent\Builder|User whereId($value) - * @method static \Illuminate\Database\Eloquent\Builder|User whereIp($value) - * @method static \Illuminate\Database\Eloquent\Builder|User whereIsDeleted($value) - * @method static \Illuminate\Database\Eloquent\Builder|User whereLastConfirmedEmailAddress($value) - * @method static \Illuminate\Database\Eloquent\Builder|User whereLastLogin($value) - * @method static \Illuminate\Database\Eloquent\Builder|User whereLastName($value) - * @method static \Illuminate\Database\Eloquent\Builder|User whereOauthProviderId($value) - * @method static \Illuminate\Database\Eloquent\Builder|User whereOauthUserId($value) - * @method static \Illuminate\Database\Eloquent\Builder|User whereOauthUserRefreshToken($value) - * @method static \Illuminate\Database\Eloquent\Builder|User whereOauthUserToken($value) - * @method static \Illuminate\Database\Eloquent\Builder|User whereOauthUserTokenExpiry($value) - * @method static \Illuminate\Database\Eloquent\Builder|User wherePassword($value) - * @method static \Illuminate\Database\Eloquent\Builder|User wherePhone($value) - * @method static \Illuminate\Database\Eloquent\Builder|User whereReferralCode($value) - * @method static \Illuminate\Database\Eloquent\Builder|User whereRememberToken($value) - * @method static \Illuminate\Database\Eloquent\Builder|User whereSignature($value) - * @method static \Illuminate\Database\Eloquent\Builder|User whereSmsVerificationCode($value) - * @method static \Illuminate\Database\Eloquent\Builder|User whereThemeId($value) - * @method static \Illuminate\Database\Eloquent\Builder|User whereUpdatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|User whereVerifiedPhoneNumber($value) * @method static \Illuminate\Database\Eloquent\Builder|User withTrashed() * @method static \Illuminate\Database\Eloquent\Builder|User withoutTrashed() - * @property-read \Illuminate\Database\Eloquent\Collection $clients - * @property-read \Illuminate\Database\Eloquent\Collection $companies * @property-read \Illuminate\Database\Eloquent\Collection $company_users - * @property-read \Illuminate\Database\Eloquent\Collection $contacts - * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications * @property-read \Illuminate\Database\Eloquent\Collection $tokens * @method bool hasPermissionTo(string $permission) diff --git a/app/Services/Invoice/ApplyPaymentAmount.php b/app/Services/Invoice/ApplyPaymentAmount.php index 59f7269d9a57..f075a3992b28 100644 --- a/app/Services/Invoice/ApplyPaymentAmount.php +++ b/app/Services/Invoice/ApplyPaymentAmount.php @@ -73,7 +73,6 @@ class ApplyPaymentAmount extends AbstractService ->updatePaidToDate($payment->amount) ->setCalculatedStatus() ->applyNumber() - ->touchPdf() ->save(); $this->invoice diff --git a/app/Services/Invoice/MarkSent.php b/app/Services/Invoice/MarkSent.php index 87c3a89f0664..79c2fa3fbd82 100644 --- a/app/Services/Invoice/MarkSent.php +++ b/app/Services/Invoice/MarkSent.php @@ -50,7 +50,6 @@ class MarkSent extends AbstractService ->service() ->applyNumber() ->setDueDate() - ->touchPdf() ->setReminder() ->save(); From c5071296571729db2ae4e050511a1f62c89aaf91 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 26 Jul 2023 13:19:13 +1000 Subject: [PATCH 45/51] Static analysis cleanup --- app/Services/Payment/DeletePayment.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Services/Payment/DeletePayment.php b/app/Services/Payment/DeletePayment.php index 98242ecb8543..d1fd931b1cb2 100644 --- a/app/Services/Payment/DeletePayment.php +++ b/app/Services/Payment/DeletePayment.php @@ -21,7 +21,7 @@ class DeletePayment private float $_paid_to_date_deleted = 0; /** - * @param mixed $payment + * @param Payment $payment * @return void */ public function __construct(public Payment $payment, private bool $update_client_paid_to_date) @@ -29,7 +29,7 @@ class DeletePayment } /** - * @return mixed + * @return Payment * @throws BindingResolutionException */ public function run() From 0d067cad7da2f9d5f4bb2d2a5d767e44825b04cc Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 26 Jul 2023 13:50:56 +1000 Subject: [PATCH 46/51] Cleanly force payment and verification of ach payments --- app/Models/PaymentHash.php | 11 +---------- app/PaymentDrivers/Stripe/ACH.php | 10 ++++++++++ app/Services/Payment/SendEmail.php | 1 - 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app/Models/PaymentHash.php b/app/Models/PaymentHash.php index 555eae5e6558..875e412bd1c4 100644 --- a/app/Models/PaymentHash.php +++ b/app/Models/PaymentHash.php @@ -20,23 +20,14 @@ use Illuminate\Database\Eloquent\Model; * @property string $hash * @property string $fee_total * @property int|null $fee_invoice_id - * @property object $data + * @property object|array $data * @property int|null $payment_id * @property \Illuminate\Support\Carbon|null $created_at * @property \Illuminate\Support\Carbon|null $updated_at * @property-read \App\Models\Invoice|null $fee_invoice * @property-read \App\Models\Payment|null $payment - * @method static \Illuminate\Database\Eloquent\Builder|PaymentHash newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|PaymentHash newQuery() * @method static \Illuminate\Database\Eloquent\Builder|PaymentHash query() - * @method static \Illuminate\Database\Eloquent\Builder|PaymentHash whereCreatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|PaymentHash whereData($value) - * @method static \Illuminate\Database\Eloquent\Builder|PaymentHash whereFeeInvoiceId($value) - * @method static \Illuminate\Database\Eloquent\Builder|PaymentHash whereFeeTotal($value) - * @method static \Illuminate\Database\Eloquent\Builder|PaymentHash whereHash($value) - * @method static \Illuminate\Database\Eloquent\Builder|PaymentHash whereId($value) - * @method static \Illuminate\Database\Eloquent\Builder|PaymentHash wherePaymentId($value) - * @method static \Illuminate\Database\Eloquent\Builder|PaymentHash whereUpdatedAt($value) * @mixin \Eloquent */ class PaymentHash extends Model diff --git a/app/PaymentDrivers/Stripe/ACH.php b/app/PaymentDrivers/Stripe/ACH.php index 10bb0e8c42a5..b68d4e120ed8 100644 --- a/app/PaymentDrivers/Stripe/ACH.php +++ b/app/PaymentDrivers/Stripe/ACH.php @@ -187,6 +187,16 @@ class ACH $intent = false; + if (count($data['tokens']) == 1) { + + $token = $data['tokens'][0]; + + $meta = $token->meta; + + if(isset($meta->state) && $meta->state == 'unauthorized') + return redirect()->route('client.payment_methods.show', $token->hashed_id); + } + if (count($data['tokens']) == 0) { $intent = $this->stripe->createPaymentIntent( diff --git a/app/Services/Payment/SendEmail.php b/app/Services/Payment/SendEmail.php index 6253911dac9c..8b9a56499562 100644 --- a/app/Services/Payment/SendEmail.php +++ b/app/Services/Payment/SendEmail.php @@ -23,7 +23,6 @@ class SendEmail /** * Builds the correct template to send. - * @return void */ public function run() { From 67c34f80a96ddb04025ceef72d377e3bbbb90d17 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 26 Jul 2023 14:31:40 +1000 Subject: [PATCH 47/51] Set todays date on a new expense --- app/Factory/ExpenseFactory.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Factory/ExpenseFactory.php b/app/Factory/ExpenseFactory.php index f43d71260a6f..89ccfc3c4c50 100644 --- a/app/Factory/ExpenseFactory.php +++ b/app/Factory/ExpenseFactory.php @@ -28,7 +28,7 @@ class ExpenseFactory $expense->tax_rate2 = 0; $expense->tax_name3 = ''; $expense->tax_rate3 = 0; - $expense->date = null; + $expense->date = now()->format('Y-m-d'); $expense->payment_date = null; $expense->amount = 0; $expense->foreign_amount = 0; From 84885837ab23341c0e69dd7039050bb9b11e3d51 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 26 Jul 2023 14:44:39 +1000 Subject: [PATCH 48/51] Fixes for string delimiters --- app/Http/Controllers/ImportController.php | 15 +++++++++++---- app/Http/Requests/Import/PreImportRequest.php | 3 ++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/app/Http/Controllers/ImportController.php b/app/Http/Controllers/ImportController.php index c8aa9c70dbdf..ee953dcf35eb 100644 --- a/app/Http/Controllers/ImportController.php +++ b/app/Http/Controllers/ImportController.php @@ -170,20 +170,27 @@ class ImportController extends Controller return $data; } - public function detectDelimiter($csvfile) + /** + * Returns the best delimiter + * + * @param string $csvfile + * @return string + */ + public function detectDelimiter($csvfile): string { $delimiters = [',', '.', ';']; $bestDelimiter = ' '; $count = 0; foreach ($delimiters as $delimiter) { - + if (substr_count(strstr($csvfile, "\n", true), $delimiter) >= $count) { - $count = substr_count($csvfile, $delimiter); - $bestDelimiter = $delimiter; + $count = substr_count(strstr($csvfile, "\n", true), $delimiter); + $bestDelimiter = $delimiter; } } + return $bestDelimiter; } } diff --git a/app/Http/Requests/Import/PreImportRequest.php b/app/Http/Requests/Import/PreImportRequest.php index 7f23b64fcfe4..9e959ac882db 100644 --- a/app/Http/Requests/Import/PreImportRequest.php +++ b/app/Http/Requests/Import/PreImportRequest.php @@ -28,9 +28,10 @@ class PreImportRequest extends Request public function rules() { return [ - 'files.*' => 'file|mimes:csv,txt', + 'files.*' => 'file|mimetypes:text/csv,text/plain,application/octet-stream', 'files' => 'required|array|min:1|max:6', 'import_type' => 'required', ]; } + } From 2dbf6cc4ff6e47b09363f571f06f4dff502530a8 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 26 Jul 2023 18:02:12 +1000 Subject: [PATCH 49/51] restrict length of reference for e-invoice --- app/Services/Invoice/EInvoice/FacturaEInvoice.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Services/Invoice/EInvoice/FacturaEInvoice.php b/app/Services/Invoice/EInvoice/FacturaEInvoice.php index 039d4aebf7a5..b497dffa9d8c 100644 --- a/app/Services/Invoice/EInvoice/FacturaEInvoice.php +++ b/app/Services/Invoice/EInvoice/FacturaEInvoice.php @@ -214,7 +214,7 @@ class FacturaEInvoice extends AbstractService { $po = $this->invoice->po_number ?? ''; - $this->fac->setReferences($po, $this->invoice->custom_value1, $this->invoice->custom_value2); + $this->fac->setReferences($po, substr($this->invoice->custom_value1, 0, 20), $this->invoice->custom_value2); return $this; } From e3d5abe5b50d4554017fe7e6ab502cbb8a8e5b4c Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 26 Jul 2023 19:00:47 +1000 Subject: [PATCH 50/51] Fixes for client country exports --- app/Export/CSV/ClientExport.php | 4 +- app/Jobs/Cron/UpdateCalculatedFields.php | 8 +++- app/Models/Project.php | 47 ------------------------ app/Models/Task.php | 4 +- 4 files changed, 10 insertions(+), 53 deletions(-) diff --git a/app/Export/CSV/ClientExport.php b/app/Export/CSV/ClientExport.php index 32f42ba807b7..6e4dded88159 100644 --- a/app/Export/CSV/ClientExport.php +++ b/app/Export/CSV/ClientExport.php @@ -163,11 +163,11 @@ class ClientExport extends BaseExport } if (in_array('client.country_id', $this->input['report_keys'])) { - $entity['country'] = $client->country ? ctrans("texts.country_{$client->country->name}") : ''; + $entity['client.country_id'] = $client->country ? ctrans("texts.country_{$client->country->name}") : ''; } if (in_array('client.shipping_country_id', $this->input['report_keys'])) { - $entity['shipping_country'] = $client->shipping_country ? ctrans("texts.country_{$client->shipping_country->name}") : ''; + $entity['client.shipping_country_id'] = $client->shipping_country ? ctrans("texts.country_{$client->shipping_country->name}") : ''; } if (in_array('client.currency_id', $this->input['report_keys'])) { diff --git a/app/Jobs/Cron/UpdateCalculatedFields.php b/app/Jobs/Cron/UpdateCalculatedFields.php index dcfdb6d48e90..62ace5ac7d70 100644 --- a/app/Jobs/Cron/UpdateCalculatedFields.php +++ b/app/Jobs/Cron/UpdateCalculatedFields.php @@ -43,7 +43,9 @@ class UpdateCalculatedFields if (! config('ninja.db.multi_db_enabled')) { - Project::with('tasks')->where('updated_at', '>', now()->subHours(2)) + Project::with('tasks')->whereHas('tasks', function ($query){ + $query->where('updated_at', '>', now()->subHours(2)); + }) ->cursor() ->each(function ($project) { @@ -59,7 +61,9 @@ class UpdateCalculatedFields MultiDB::setDB($db); - Project::with('tasks')->where('updated_at', '>', now()->subHours(2)) + Project::with('tasks')->whereHas('tasks', function ($query){ + $query->where('updated_at', '>', now()->subHours(2)); + }) ->cursor() ->each(function ($project) { $project->current_hours = $this->calculateDuration($project); diff --git a/app/Models/Project.php b/app/Models/Project.php index 03f33215041b..d25bd23630c6 100644 --- a/app/Models/Project.php +++ b/app/Models/Project.php @@ -32,11 +32,9 @@ use Laracasts\Presenter\PresentableTrait; * @property string $color * @property-read \App\Models\Client|null $client * @property-read \App\Models\Company $company - * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read int|null $documents_count * @property-read mixed $hashed_id * @property-read Project|null $project - * @property-read \Illuminate\Database\Eloquent\Collection $tasks * @property-read int|null $tasks_count * @property-read \App\Models\User $user * @property-read \App\Models\Vendor|null $vendor @@ -49,55 +47,10 @@ use Laracasts\Presenter\PresentableTrait; * @method static \Illuminate\Database\Eloquent\Builder|Project onlyTrashed() * @method static \Illuminate\Database\Eloquent\Builder|Project query() * @method static \Illuminate\Database\Eloquent\Builder|BaseModel scope() - * @method static \Illuminate\Database\Eloquent\Builder|Project whereAssignedUserId($value) - * @method static \Illuminate\Database\Eloquent\Builder|Project whereBudgetedHours($value) - * @method static \Illuminate\Database\Eloquent\Builder|Project whereClientId($value) - * @method static \Illuminate\Database\Eloquent\Builder|Project whereColor($value) - * @method static \Illuminate\Database\Eloquent\Builder|Project whereCompanyId($value) - * @method static \Illuminate\Database\Eloquent\Builder|Project whereCreatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|Project whereCustomValue1($value) - * @method static \Illuminate\Database\Eloquent\Builder|Project whereCustomValue2($value) - * @method static \Illuminate\Database\Eloquent\Builder|Project whereCustomValue3($value) - * @method static \Illuminate\Database\Eloquent\Builder|Project whereCustomValue4($value) - * @method static \Illuminate\Database\Eloquent\Builder|Project whereDeletedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|Project whereDueDate($value) - * @method static \Illuminate\Database\Eloquent\Builder|Project whereId($value) - * @method static \Illuminate\Database\Eloquent\Builder|Project whereIsDeleted($value) - * @method static \Illuminate\Database\Eloquent\Builder|Project whereName($value) - * @method static \Illuminate\Database\Eloquent\Builder|Project whereNumber($value) - * @method static \Illuminate\Database\Eloquent\Builder|Project wherePrivateNotes($value) - * @method static \Illuminate\Database\Eloquent\Builder|Project wherePublicNotes($value) - * @method static \Illuminate\Database\Eloquent\Builder|Project whereTaskRate($value) - * @method static \Illuminate\Database\Eloquent\Builder|Project whereUpdatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|Project whereUserId($value) * @method static \Illuminate\Database\Eloquent\Builder|Project withTrashed() * @method static \Illuminate\Database\Eloquent\Builder|Project withoutTrashed() * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $tasks - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $tasks - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $tasks - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $tasks - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $tasks - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $tasks - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $tasks - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $tasks - * @property int|null $current_hours - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $tasks - * @method static \Illuminate\Database\Eloquent\Builder|Project whereCurrentHours($value) - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $tasks - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $tasks - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $tasks * @mixin \Eloquent */ class Project extends BaseModel diff --git a/app/Models/Task.php b/app/Models/Task.php index d27221ebd68b..caee4ca8a374 100644 --- a/app/Models/Task.php +++ b/app/Models/Task.php @@ -36,8 +36,8 @@ use Illuminate\Support\Carbon; * @property string|null $custom_value4 * @property int|null $duration * @property string|null $description - * @property int $is_deleted - * @property int $is_running + * @property bool $is_deleted + * @property bool $is_running * @property string|null $time_log * @property string|null $number * @property string $rate From 69b4253c25a3ab70dc541c198b399c419475fd49 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 26 Jul 2023 19:08:42 +1000 Subject: [PATCH 51/51] v5.6.22 --- VERSION.txt | 2 +- app/Services/Invoice/EInvoice/FacturaEInvoice.php | 4 ++-- config/ninja.php | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/VERSION.txt b/VERSION.txt index 1d9b417168bf..29a1e2867ec1 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -5.6.21 \ No newline at end of file +5.6.22 \ No newline at end of file diff --git a/app/Services/Invoice/EInvoice/FacturaEInvoice.php b/app/Services/Invoice/EInvoice/FacturaEInvoice.php index b497dffa9d8c..4f995ddafa80 100644 --- a/app/Services/Invoice/EInvoice/FacturaEInvoice.php +++ b/app/Services/Invoice/EInvoice/FacturaEInvoice.php @@ -233,8 +233,8 @@ class FacturaEInvoice extends AbstractService foreach($this->invoice->line_items as $item) { $this->fac->addItem(new FacturaeItem([ - 'name' => $item->notes, - 'description' => $item->product_key, + 'name' => $item->product_key, + 'description' => $item->notes, 'quantity' => $item->quantity, 'unitPriceWithoutTax' => $item->cost, 'discountsAndRebates' => $item->discount, diff --git a/config/ninja.php b/config/ninja.php index 086fae76a203..f36da4e8a710 100644 --- a/config/ninja.php +++ b/config/ninja.php @@ -15,8 +15,8 @@ return [ 'require_https' => env('REQUIRE_HTTPS', true), 'app_url' => rtrim(env('APP_URL', ''), '/'), 'app_domain' => env('APP_DOMAIN', 'invoicing.co'), - 'app_version' => env('APP_VERSION','5.6.21'), - 'app_tag' => env('APP_TAG','5.6.21'), + 'app_version' => env('APP_VERSION','5.6.22'), + 'app_tag' => env('APP_TAG','5.6.22'), 'minimum_client_version' => '5.0.16', 'terms_version' => '1.0.1', 'api_secret' => env('API_SECRET', ''),