From fe891a42934bcbd31549f23c079eb03ea1548b49 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 2 Aug 2024 14:44:12 +1000 Subject: [PATCH] Fixes for Rotessa Import Customers + Payments --- .../Components/Rotessa/AccountComponent.php | 5 +- .../Components/Rotessa/AddressComponent.php | 5 +- .../Components/Rotessa/ContactComponent.php | 4 +- .../Components/RotessaComponents.php | 123 ------------------ app/PaymentDrivers/Rotessa/PaymentMethod.php | 17 +-- app/PaymentDrivers/RotessaPaymentDriver.php | 15 ++- composer.lock | 46 +++---- 7 files changed, 47 insertions(+), 168 deletions(-) delete mode 100644 app/Http/ViewComposers/Components/RotessaComponents.php diff --git a/app/Http/ViewComposers/Components/Rotessa/AccountComponent.php b/app/Http/ViewComposers/Components/Rotessa/AccountComponent.php index e161b3a121..01df934dcf 100644 --- a/app/Http/ViewComposers/Components/Rotessa/AccountComponent.php +++ b/app/Http/ViewComposers/Components/Rotessa/AccountComponent.php @@ -33,10 +33,7 @@ class AccountComponent extends Component "authorization_type" => 'Online' ]; - public array $account; - - public function __construct(array $account) { - $this->account = $account; + public function __construct(public array $account) { $this->attributes = $this->newAttributeBag(Arr::only($this->account, $this->fields) ); } diff --git a/app/Http/ViewComposers/Components/Rotessa/AddressComponent.php b/app/Http/ViewComposers/Components/Rotessa/AddressComponent.php index b26222b18c..836bbf00ff 100644 --- a/app/Http/ViewComposers/Components/Rotessa/AddressComponent.php +++ b/app/Http/ViewComposers/Components/Rotessa/AddressComponent.php @@ -25,10 +25,7 @@ class AddressComponent extends Component 'country' => 'US' ]; - public array $address; - - public function __construct(array $address) { - $this->address = $address; + public function __construct(public array $address) { if(strlen($this->address['state']) > 2 ) { $this->address['state'] = $this->address['country'] == 'US' ? array_search($this->address['state'], USStates::$states) : CAProvinces::getAbbreviation($this->address['state']); } diff --git a/app/Http/ViewComposers/Components/Rotessa/ContactComponent.php b/app/Http/ViewComposers/Components/Rotessa/ContactComponent.php index 3557cd0535..e5419856a6 100644 --- a/app/Http/ViewComposers/Components/Rotessa/ContactComponent.php +++ b/app/Http/ViewComposers/Components/Rotessa/ContactComponent.php @@ -18,9 +18,9 @@ class ContactComponent extends Component $contact = collect($contact->client->contacts->firstWhere('is_primary', 1)->toArray())->merge([ 'home_phone' =>$contact->client->phone, - 'custom_identifier' => $contact->client->number, + 'custom_identifier' => $contact->client->client_hash, 'name' =>$contact->client->name, - 'id' => $contact->client->contact_key, + 'id' => null, ] )->all(); $this->attributes = $this->newAttributeBag(Arr::only($contact, $this->fields) ); diff --git a/app/Http/ViewComposers/Components/RotessaComponents.php b/app/Http/ViewComposers/Components/RotessaComponents.php deleted file mode 100644 index b984d0bb23..0000000000 --- a/app/Http/ViewComposers/Components/RotessaComponents.php +++ /dev/null @@ -1,123 +0,0 @@ -client->contacts->firstWhere('is_primary', 1)->toArray())->merge([ - 'home_phone' =>$contact->client->phone, - 'custom_identifier' => $contact->client->number, - 'name' =>$contact->client->name, - 'id' => null - ] )->all(); - - $this->attributes = $this->newAttributeBag(Arr::only($contact, $this->fields) ); - } - - private $fields = [ - 'name', - 'email', - 'home_phone', - 'phone', - 'custom_identifier', - 'customer_type' , - 'id' - ]; - - private $defaults = [ - 'customer_type' => "Business", - 'customer_identifier' => null, - 'id' => null - ]; - - public function render() - { - return render('gateways.rotessa.components.contact', array_merge($this->defaults, $this->attributes->getAttributes() ) ); - } -} - -// Address Component -class AddressComponent extends Component -{ - private $fields = [ - 'address_1', - 'address_2', - 'city', - 'postal_code', - 'province_code', - 'country' - ]; - - private $defaults = [ - 'country' => 'US' - ]; - - public array $address; - - public function __construct(array $address) { - $this->address = $address; - if(strlen($this->address['state']) > 2 ) { - $this->address['state'] = $this->address['country'] == 'US' ? array_search($this->address['state'], USStates::$states) : CAProvinces::getAbbreviation($this->address['state']); - } - - $this->attributes = $this->newAttributeBag( - Arr::only(Arr::mapWithKeys($this->address, function ($item, $key) { - return in_array($key, ['address1','address2','state'])?[ (['address1'=>'address_1','address2'=>'address_2','state'=>'province_code'])[$key] => $item ] :[ $key => $item ]; - }), - $this->fields) ); - } - - - public function render() - { - return render('gateways.rotessa.components.address',array_merge( $this->defaults, $this->attributes->getAttributes() ) ); - } -} - -// AmericanBankInfo Component -class AccountComponent extends Component -{ - private $fields = [ - 'bank_account_type', - 'routing_number', - 'institution_number', - 'transit_number', - 'bank_name', - 'country', - 'account_number' - ]; - - private $defaults = [ - 'bank_account_type' => null, - 'routing_number' => null, - 'institution_number' => null, - 'transit_number' => null, - 'bank_name' => ' ', - 'account_number' => null, - 'country' => 'US', - "authorization_type" => 'Online' - ]; - - public array $account; - - public function __construct(array $account) { - $this->account = $account; - $this->attributes = $this->newAttributeBag(Arr::only($this->account, $this->fields) ); - } - - public function render() - { - return render('gateways.rotessa.components.account', array_merge($this->attributes->getAttributes(), $this->defaults) ); - } -} diff --git a/app/PaymentDrivers/Rotessa/PaymentMethod.php b/app/PaymentDrivers/Rotessa/PaymentMethod.php index 09717a0da7..9cb90c7adf 100755 --- a/app/PaymentDrivers/Rotessa/PaymentMethod.php +++ b/app/PaymentDrivers/Rotessa/PaymentMethod.php @@ -37,11 +37,9 @@ use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest; class PaymentMethod implements MethodInterface { - protected RotessaPaymentDriver $rotessa; - public function __construct(RotessaPaymentDriver $rotessa) + public function __construct(protected RotessaPaymentDriver $rotessa) { - $this->rotessa = $rotessa; $this->rotessa->init(); } @@ -60,9 +58,6 @@ class PaymentMethod implements MethodInterface 'id' => null ] )->all(); $data['gateway'] = $this->rotessa; - // Set gateway type according to client country - // $data['gateway_type_id'] = $data['client']->country->iso_3166_2 == 'US' ? GatewayType::BANK_TRANSFER : ( $data['client']->country->iso_3166_2 == 'CA' ? GatewayType::ACSS : (int) request('method')); - // TODO: detect GatewayType based on client country USA vs CAN $data['gateway_type_id'] = GatewayType::ACSS ; $data['account'] = [ 'routing_number' => $data['client']->routing_id, @@ -104,6 +99,7 @@ class PaymentMethod implements MethodInterface 'customer_id'=>['required_without:custom_identifier','integer'], ]); $customer = new Customer( ['address' => $request->only('address_1','address_2','city','postal_code','province_code','country'), 'custom_identifier' => $request->input('custom_identifier') ] + $request->all()); + $this->rotessa->findOrCreateCustomer($customer->resolve()); return redirect()->route('client.payment_methods.index')->withMessage(ctrans('texts.payment_method_added')); @@ -142,8 +138,10 @@ class PaymentMethod implements MethodInterface */ public function paymentResponse(PaymentResponseRequest $request) { + $response= null; $customer = null; + try { $request->validate([ 'source' => ['required','string','exists:client_gateway_tokens,token'], @@ -153,17 +151,20 @@ class PaymentMethod implements MethodInterface $customer = ClientGatewayToken::query() ->where('company_gateway_id', $this->rotessa->company_gateway->id) ->where('client_id', $this->rotessa->client->id) + ->where('is_deleted', 0) ->where('token', $request->input('source')) ->first(); + if(!$customer) throw new \Exception('Client gateway token not found!', SystemLog::TYPE_ROTESSA); $transaction = new Transaction($request->only('frequency' ,'installments','amount','process_date') + ['comment' => $this->rotessa->getDescription(false) ]); $transaction->additional(['customer_id' => $customer->gateway_customer_reference]); $transaction = array_filter( $transaction->resolve()); $response = $this->rotessa->gateway->capture($transaction)->send(); + if(!$response->isSuccessful()) throw new \Exception($response->getMessage(), (int) $response->getCode()); - return $this->processPendingPayment($response->getParameter('id'), (float) $response->getParameter('amount'), (int) $customer->gateway_type_id , $customer->token); + return $this->processPendingPayment($response->getParameter('id'), (float) $response->getParameter('amount'), PaymentType::ACSS , $customer->token); } catch(\Throwable $e) { $this->processUnsuccessfulPayment( new InvalidResponseException($e->getMessage(), (int) $e->getCode()) ); } @@ -194,7 +195,7 @@ class PaymentMethod implements MethodInterface /** * Handle unsuccessful payment for Rotessa. * - * @param Exception $exception + * @param \Exception $exception * @throws PaymentFailed * @return void */ diff --git a/app/PaymentDrivers/RotessaPaymentDriver.php b/app/PaymentDrivers/RotessaPaymentDriver.php index 23915c758c..585503d16d 100644 --- a/app/PaymentDrivers/RotessaPaymentDriver.php +++ b/app/PaymentDrivers/RotessaPaymentDriver.php @@ -11,6 +11,7 @@ namespace App\PaymentDrivers; +use App\DataMapper\ClientSettings; use Omnipay\Omnipay; use App\Models\Client; use App\Models\Payment; @@ -183,6 +184,9 @@ class RotessaPaymentDriver extends BaseDriver "updated_at": "2015-02-10T23:50:45.000-06:00" } */ + $settings = ClientSettings::defaults(); + $settings->currency_id = $this->company_gateway->company->getSetting('currency_id'); + $client = (\App\Factory\ClientFactory::create($this->company_gateway->company_id, $this->company_gateway->user_id))->fill( [ 'address1' => $customer->address['address_1'] ?? '', @@ -192,7 +196,8 @@ class RotessaPaymentDriver extends BaseDriver 'state' => $customer->address['province_code'] ?? '', 'country_id' => empty($customer->transit_number) ? 840 : 124, 'routing_id' => empty(($r = $customer->routing_number))? null : $r, - "number" => str_pad($customer->account_number,3,'0',STR_PAD_LEFT) + "number" => str_pad($customer->account_number,3,'0',STR_PAD_LEFT), + "settings" => $settings, ] ); $client->saveQuietly(); @@ -234,8 +239,9 @@ class RotessaPaymentDriver extends BaseDriver $existing = ClientGatewayToken::query() ->where('company_gateway_id', $this->company_gateway->id) ->where('client_id', $this->client->id) + ->where('is_deleted',0) ->orWhere(function (Builder $query) use ($data) { - $query->where('token', encrypt(join(".", Arr::only($data, 'id','custom_identifier'))) ) + $query->where('token', join(".", Arr::only($data, ['id','custom_identifier']))) ->where('gateway_customer_reference', Arr::only($data,'id')); }) ->exists(); @@ -246,14 +252,15 @@ class RotessaPaymentDriver extends BaseDriver $customer = new Customer($result->getData()); $data = array_filter($customer->resolve()); + } // $payment_method_id = Arr::has($data,'address.postal_code') && ((int) $data['address']['postal_code'])? GatewayType::BANK_TRANSFER: GatewayType::ACSS; // TODO: Check/ Validate postal code between USA vs CAN $payment_method_id = GatewayType::ACSS; $gateway_token = $this->storeGatewayToken( [ - 'payment_meta' => $data + ['brand' => 'Rotessa', 'last4' => $data['bank_name'], 'type' => $data['bank_account_type'] ], - 'token' => encrypt(join(".", Arr::only($data, 'id','custom_identifier'))), + 'payment_meta' => $data + ['brand' => 'Bank Transfer', 'last4' => substr($data['account_number'], -4), 'type' => GatewayType::ACSS ], + 'token' => join(".", Arr::only($data, ['id','custom_identifier'])), 'payment_method_id' => $payment_method_id , ], ['gateway_customer_reference' => $data['id'] diff --git a/composer.lock b/composer.lock index fa1c4b178c..25bf656a39 100644 --- a/composer.lock +++ b/composer.lock @@ -535,16 +535,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.316.10", + "version": "3.317.0", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "eeb8df6ff6caa428e8bcd631ad2a96430900a249" + "reference": "6d46c8e00c22f66349b8a509bd2c5ced72cceff2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/eeb8df6ff6caa428e8bcd631ad2a96430900a249", - "reference": "eeb8df6ff6caa428e8bcd631ad2a96430900a249", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/6d46c8e00c22f66349b8a509bd2c5ced72cceff2", + "reference": "6d46c8e00c22f66349b8a509bd2c5ced72cceff2", "shasum": "" }, "require": { @@ -624,9 +624,9 @@ "support": { "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80", "issues": "https://github.com/aws/aws-sdk-php/issues", - "source": "https://github.com/aws/aws-sdk-php/tree/3.316.10" + "source": "https://github.com/aws/aws-sdk-php/tree/3.317.0" }, - "time": "2024-07-30T18:10:20+00:00" + "time": "2024-08-01T18:12:34+00:00" }, { "name": "bacon/bacon-qr-code", @@ -4758,16 +4758,16 @@ }, { "name": "laravel/pint", - "version": "v1.17.0", + "version": "v1.17.1", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "4dba80c1de4b81dc4c4fb10ea6f4781495eb29f5" + "reference": "b5b6f716db298671c1dfea5b1082ec2c0ae7064f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/4dba80c1de4b81dc4c4fb10ea6f4781495eb29f5", - "reference": "4dba80c1de4b81dc4c4fb10ea6f4781495eb29f5", + "url": "https://api.github.com/repos/laravel/pint/zipball/b5b6f716db298671c1dfea5b1082ec2c0ae7064f", + "reference": "b5b6f716db298671c1dfea5b1082ec2c0ae7064f", "shasum": "" }, "require": { @@ -4820,7 +4820,7 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2024-07-23T16:40:20+00:00" + "time": "2024-08-01T09:06:33+00:00" }, { "name": "laravel/prompts", @@ -16968,16 +16968,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.11.8", + "version": "1.11.9", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "6adbd118e6c0515dd2f36b06cde1d6da40f1b8ec" + "reference": "e370bcddadaede0c1716338b262346f40d296f82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/6adbd118e6c0515dd2f36b06cde1d6da40f1b8ec", - "reference": "6adbd118e6c0515dd2f36b06cde1d6da40f1b8ec", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/e370bcddadaede0c1716338b262346f40d296f82", + "reference": "e370bcddadaede0c1716338b262346f40d296f82", "shasum": "" }, "require": { @@ -17022,7 +17022,7 @@ "type": "github" } ], - "time": "2024-07-24T07:01:22+00:00" + "time": "2024-08-01T16:25:18+00:00" }, { "name": "phpunit/php-code-coverage", @@ -19031,16 +19031,16 @@ }, { "name": "spatie/flare-client-php", - "version": "1.7.0", + "version": "1.8.0", "source": { "type": "git", "url": "https://github.com/spatie/flare-client-php.git", - "reference": "097040ff51e660e0f6fc863684ac4b02c93fa234" + "reference": "180f8ca4c0d0d6fc51477bd8c53ce37ab5a96122" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/flare-client-php/zipball/097040ff51e660e0f6fc863684ac4b02c93fa234", - "reference": "097040ff51e660e0f6fc863684ac4b02c93fa234", + "url": "https://api.github.com/repos/spatie/flare-client-php/zipball/180f8ca4c0d0d6fc51477bd8c53ce37ab5a96122", + "reference": "180f8ca4c0d0d6fc51477bd8c53ce37ab5a96122", "shasum": "" }, "require": { @@ -19058,7 +19058,7 @@ "phpstan/extension-installer": "^1.1", "phpstan/phpstan-deprecation-rules": "^1.0", "phpstan/phpstan-phpunit": "^1.0", - "spatie/phpunit-snapshot-assertions": "^4.0|^5.0" + "spatie/pest-plugin-snapshots": "^1.0|^2.0" }, "type": "library", "extra": { @@ -19088,7 +19088,7 @@ ], "support": { "issues": "https://github.com/spatie/flare-client-php/issues", - "source": "https://github.com/spatie/flare-client-php/tree/1.7.0" + "source": "https://github.com/spatie/flare-client-php/tree/1.8.0" }, "funding": [ { @@ -19096,7 +19096,7 @@ "type": "github" } ], - "time": "2024-06-12T14:39:14+00:00" + "time": "2024-08-01T08:27:26+00:00" }, { "name": "spatie/ignition",