From 8c00bce71b829206d526b3dcb2e4105975ffc6e1 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 5 May 2021 14:29:58 +1000 Subject: [PATCH 01/43] We Pay signup --- .../Controllers/StripeConnectController.php | 1 - app/Http/Controllers/WePayController.php | 62 ++++++ app/Http/Livewire/WepaySignup.php | 73 +++++++ app/Models/SystemLog.php | 1 + app/PaymentDrivers/Stripe/Connect/Account.php | 159 -------------- app/PaymentDrivers/WePay/CreditCard.php | 31 +++ app/PaymentDrivers/WePay/Setup.php | 206 ++++++++++++++++++ app/PaymentDrivers/WePayPaymentDriver.php | 110 ++++++++++ composer.json | 3 +- composer.lock | 53 ++++- config/ninja.php | 5 + .../2021_05_05_014713_activate_we_pay.php | 30 +++ database/seeders/PaymentLibrariesSeeder.php | 2 +- .../gateways/wepay/signup/index.blade.php | 14 ++ .../wepay/signup/wepay-signup.blade.php | 93 ++++++++ routes/api.php | 2 + routes/client.php | 1 + 17 files changed, 683 insertions(+), 163 deletions(-) create mode 100644 app/Http/Controllers/WePayController.php create mode 100644 app/Http/Livewire/WepaySignup.php create mode 100644 app/PaymentDrivers/WePay/CreditCard.php create mode 100644 app/PaymentDrivers/WePay/Setup.php create mode 100644 app/PaymentDrivers/WePayPaymentDriver.php create mode 100644 database/migrations/2021_05_05_014713_activate_we_pay.php create mode 100644 resources/views/portal/ninja2020/gateways/wepay/signup/index.blade.php create mode 100644 resources/views/portal/ninja2020/gateways/wepay/signup/wepay-signup.blade.php diff --git a/app/Http/Controllers/StripeConnectController.php b/app/Http/Controllers/StripeConnectController.php index 7410f3766174..73e1651bdc99 100644 --- a/app/Http/Controllers/StripeConnectController.php +++ b/app/Http/Controllers/StripeConnectController.php @@ -34,7 +34,6 @@ class StripeConnectController extends BaseController if(!is_array($request->getTokenContent())) abort(400, 'Invalid token'); - MultiDB::findAndSetDbByCompanyKey($request->getTokenContent()['company_key']); $data = [ diff --git a/app/Http/Controllers/WePayController.php b/app/Http/Controllers/WePayController.php new file mode 100644 index 000000000000..6dfe7496fbe8 --- /dev/null +++ b/app/Http/Controllers/WePayController.php @@ -0,0 +1,62 @@ + auth()->user()->id, + // 'company_key'=> auth()->user()->company()->company_key, + // 'context' => $request->input('context'), + // ]; + + $hash = Cache::get($token); + + //temporarily comment this out + // if(!$hash) + // abort(400, 'Link expired'); + // MultiDB::findAndSetDbByCompanyKey($hash['company_key']); + // $data['user_id'] = $this->encodePrimaryKey($hash['user_id']); + // $data['company_key'] = $hash['company_key']; + + $data['user_id'] = 1; + $user = User::first(); + + $data['company_key'] = $user->account->companies()->first()->company_key; + + $wepay_driver = new WePayPaymentDriver(new CompanyGateway, null, null); + + return $wepay_driver->setup($data); + + } + + public function processSignup(Request $request) + { + + } +} diff --git a/app/Http/Livewire/WepaySignup.php b/app/Http/Livewire/WepaySignup.php new file mode 100644 index 000000000000..d90b77c18d52 --- /dev/null +++ b/app/Http/Livewire/WepaySignup.php @@ -0,0 +1,73 @@ + ['sometimes'], + 'last_name' => ['sometimes'], + 'email' => ['required', 'email'], + ]; + + public function mount() + { + $user = User::find($this->user_id); + $company = Company::where('company_key', $this->company_key)->first(); + + $this->fill([ + 'user' => $user, + 'first_name' => $user->first_name, + 'last_name' => $user->last_name, + 'email' => $user->email, + 'company_name' => $company->present()->name(), + 'saved' => ctrans('texts.confirm'), + 'terms' => ''.ctrans('texts.terms_of_service').'', + 'privacy_policy' => ''.ctrans('texts.privacy_policy').'', + ]); + } + + public function render() + { + return render('gateways.wepay.signup.wepay-signup'); + } + + public function submit() + { + + $data = $this->validate($this->rules); + + // $this->user + // ->fill($data) + // ->save(); + + $this->saved = ctrans('texts.saved_at', ['time' => now()->toTimeString()]); + } +} diff --git a/app/Models/SystemLog.php b/app/Models/SystemLog.php index 816846ba255f..c263df95328f 100644 --- a/app/Models/SystemLog.php +++ b/app/Models/SystemLog.php @@ -65,6 +65,7 @@ class SystemLog extends Model const TYPE_CHECKOUT = 304; const TYPE_AUTHORIZE = 305; const TYPE_CUSTOM = 306; + const TYPE_WEPAY = 309; const TYPE_QUOTA_EXCEEDED = 400; const TYPE_UPSTREAM_FAILURE = 401; diff --git a/app/PaymentDrivers/Stripe/Connect/Account.php b/app/PaymentDrivers/Stripe/Connect/Account.php index 69c85d771e7c..4d837cb2ea11 100644 --- a/app/PaymentDrivers/Stripe/Connect/Account.php +++ b/app/PaymentDrivers/Stripe/Connect/Account.php @@ -47,164 +47,5 @@ class Account ]); } -/*** If this is a new account (ie there is no account_id in company_gateways.config, the we need to create an account as below. - -/// -// $stripe = new \Stripe\StripeClient( -// 'sk_test_4eC39HqLyjWDarjtT1zdp7dc' -// ); -// $stripe->accounts->create([ -// 'type' => 'standard', -// 'country' => 'US', //if we have it - inject -// 'email' => 'jenny.rosen@example.com', //if we have it - inject -// ]); -/// - - -//response - -//******************* We should store the 'id' as a property in the config with the key `account_id` - -/** - * { - "id": "acct_1032D82eZvKYlo2C", - "object": "account", - "business_profile": { - "mcc": null, - "name": "Stripe.com", - "product_description": null, - "support_address": null, - "support_email": null, - "support_phone": null, - "support_url": null, - "url": null - }, - "capabilities": { - "card_payments": "active", - "transfers": "active" - }, - "charges_enabled": false, - "country": "US", - "default_currency": "usd", - "details_submitted": false, - "email": "site@stripe.com", - "metadata": {}, - "payouts_enabled": false, - "requirements": { - "current_deadline": null, - "currently_due": [ - "business_profile.product_description", - "business_profile.support_phone", - "business_profile.url", - "external_account", - "tos_acceptance.date", - "tos_acceptance.ip" - ], - "disabled_reason": "requirements.past_due", - "errors": [], - "eventually_due": [ - "business_profile.product_description", - "business_profile.support_phone", - "business_profile.url", - "external_account", - "tos_acceptance.date", - "tos_acceptance.ip" - ], - "past_due": [], - "pending_verification": [] - }, - "settings": { - "bacs_debit_payments": {}, - "branding": { - "icon": null, - "logo": null, - "primary_color": null, - "secondary_color": null - }, - "card_issuing": { - "tos_acceptance": { - "date": null, - "ip": null - } - }, - "card_payments": { - "decline_on": { - "avs_failure": true, - "cvc_failure": false - }, - "statement_descriptor_prefix": null - }, - "dashboard": { - "display_name": "Stripe.com", - "timezone": "US/Pacific" - }, - "payments": { - "statement_descriptor": null, - "statement_descriptor_kana": null, - "statement_descriptor_kanji": null - }, - "payouts": { - "debit_negative_balances": true, - "schedule": { - "delay_days": 7, - "interval": "daily" - }, - "statement_descriptor": null - }, - "sepa_debit_payments": {} - }, - "type": "standard" -} - - */ - - -//At this stage we have an account, so we need to generate the account link -//then create the account link - -// now we start the stripe onboarding flow -// https://stripe.com/docs/api/account_links/object -// -/** - * $stripe = new \Stripe\StripeClient( - 'sk_test_4eC39HqLyjWDarjtT1zdp7dc' -); -$stripe->accountLinks->create([ - 'account' => 'acct_1032D82eZvKYlo2C', - 'refresh_url' => 'https://example.com/reauth', - 'return_url' => 'https://example.com/return', - 'type' => 'account_onboarding', -]); - */ - -/** - * Response = - * { - "object": "account_link", - "created": 1618869558, - "expires_at": 1618869858, - "url": "https://connect.stripe.com/setup/s/9BhFaPdfseRF" -} - */ - -//The users account may not be active yet, we need to pull the account back and check for the property `charges_enabled` -// -// - - -// What next? -// -// Now we need to create a superclass of the StripePaymentDriver, i believe the only thing we need to change is the way we initialize the gateway.. - -/** - * -\Stripe\Stripe::setApiKey("{{PLATFORM_SECRET_KEY}}"); <--- platform secret key = Invoice Ninja secret key -\Stripe\Customer::create( - ["email" => "person@example.edu"], - ["stripe_account" => "{{CONNECTED_STRIPE_ACCOUNT_ID}}"] <------ company_gateway.config.account_id -); - - - */ } diff --git a/app/PaymentDrivers/WePay/CreditCard.php b/app/PaymentDrivers/WePay/CreditCard.php new file mode 100644 index 000000000000..2b54246784d2 --- /dev/null +++ b/app/PaymentDrivers/WePay/CreditCard.php @@ -0,0 +1,31 @@ +wepay = $wepay; + } + + public function authorizeView($data) + { + + } + +} diff --git a/app/PaymentDrivers/WePay/Setup.php b/app/PaymentDrivers/WePay/Setup.php new file mode 100644 index 000000000000..740ef8025520 --- /dev/null +++ b/app/PaymentDrivers/WePay/Setup.php @@ -0,0 +1,206 @@ +wepay = $wepay; + } + + public function boot($data) + { + /* + 'user_id', + 'company_key', + */ + + return render('gateways.wepay.signup.index', $data); + } + + + public function processSignup(Request $request) + { + + } +} + + +/* +protected function setupWePay($accountGateway, &$response) + { + $user = Auth::user(); + $account = $user->account; + + $rules = [ + 'company_name' => 'required', + 'tos_agree' => 'required', + 'first_name' => 'required', + 'last_name' => 'required', + 'email' => 'required|email', + 'country' => 'required|in:US,CA,GB', + ]; + + $validator = Validator::make(Input::all(), $rules); + + if ($validator->fails()) { + return Redirect::to('gateways/create') + ->withErrors($validator) + ->withInput(); + } + + if (! $user->email) { + $user->email = trim(Input::get('email')); + $user->first_name = trim(Input::get('first_name')); + $user->last_name = trim(Input::get('last_name')); + $user->save(); + } + + try { + $wepay = Utils::setupWePay(); + + $userDetails = [ + 'client_id' => WEPAY_CLIENT_ID, + 'client_secret' => WEPAY_CLIENT_SECRET, + 'email' => Input::get('email'), + 'first_name' => Input::get('first_name'), + 'last_name' => Input::get('last_name'), + 'original_ip' => \Request::getClientIp(true), + 'original_device' => \Request::server('HTTP_USER_AGENT'), + 'tos_acceptance_time' => time(), + 'redirect_uri' => URL::to('gateways'), + 'scope' => 'manage_accounts,collect_payments,view_user,preapprove_payments,send_money', + ]; + + $wepayUser = $wepay->request('user/register/', $userDetails); + + $accessToken = $wepayUser->access_token; + $accessTokenExpires = $wepayUser->expires_in ? (time() + $wepayUser->expires_in) : null; + + $wepay = new WePay($accessToken); + + $accountDetails = [ + 'name' => Input::get('company_name'), + 'description' => trans('texts.wepay_account_description'), + 'theme_object' => json_decode(WEPAY_THEME), + 'callback_uri' => $accountGateway->getWebhookUrl(), + 'rbits' => $account->present()->rBits, + 'country' => Input::get('country'), + ]; + + if (Input::get('country') == 'CA') { + $accountDetails['currencies'] = ['CAD']; + $accountDetails['country_options'] = ['debit_opt_in' => boolval(Input::get('debit_cards'))]; + } elseif (Input::get('country') == 'GB') { + $accountDetails['currencies'] = ['GBP']; + } + + $wepayAccount = $wepay->request('account/create/', $accountDetails); + + try { + $wepay->request('user/send_confirmation/', []); + $confirmationRequired = true; + } catch (\WePayException $ex) { + if ($ex->getMessage() == 'This access_token is already approved.') { + $confirmationRequired = false; + } else { + throw $ex; + } + } + + $accountGateway->gateway_id = GATEWAY_WEPAY; + $accountGateway->setConfig([ + 'userId' => $wepayUser->user_id, + 'accessToken' => $accessToken, + 'tokenType' => $wepayUser->token_type, + 'tokenExpires' => $accessTokenExpires, + 'accountId' => $wepayAccount->account_id, + 'state' => $wepayAccount->state, + 'testMode' => WEPAY_ENVIRONMENT == WEPAY_STAGE, + 'country' => Input::get('country'), + ]); + + if ($confirmationRequired) { + Session::flash('message', trans('texts.created_wepay_confirmation_required')); + } else { + $updateUri = $wepay->request('/account/get_update_uri', [ + 'account_id' => $wepayAccount->account_id, + 'redirect_uri' => URL::to('gateways'), + ]); + + $response = Redirect::to($updateUri->uri); + + return true; + } + + $response = Redirect::to("gateways/{$accountGateway->public_id}/edit"); + + return true; + } catch (\WePayException $e) { + Session::flash('error', $e->getMessage()); + $response = Redirect::to('gateways/create') + ->withInput(); + + return false; + } + } + */ + + +/* + + +rbits + + + private function createRBit($type, $source, $properties) + { + $data = new stdClass(); + $data->receive_time = time(); + $data->type = $type; + $data->source = $source; + $data->properties = new stdClass(); + + foreach ($properties as $key => $val) { + $data->properties->$key = $val; + } + + return $data; + } + + public function rBits() + { + $account = $this->entity; + $user = $account->users()->first(); + $data = []; + + $data[] = $this->createRBit('business_name', 'user', ['business_name' => $account->name]); + $data[] = $this->createRBit('industry_code', 'user', ['industry_detail' => $account->present()->industry]); + $data[] = $this->createRBit('comment', 'partner_database', ['comment_text' => 'Logo image not present']); + $data[] = $this->createRBit('business_description', 'user', ['business_description' => $account->present()->size]); + + $data[] = $this->createRBit('person', 'user', ['name' => $user->getFullName()]); + $data[] = $this->createRBit('email', 'user', ['email' => $user->email]); + $data[] = $this->createRBit('phone', 'user', ['phone' => $user->phone]); + $data[] = $this->createRBit('website_uri', 'user', ['uri' => $account->website]); + $data[] = $this->createRBit('external_account', 'partner_database', ['is_partner_account' => 'yes', 'account_type' => 'Invoice Ninja', 'create_time' => time()]); + + return $data; + } + */ \ No newline at end of file diff --git a/app/PaymentDrivers/WePayPaymentDriver.php b/app/PaymentDrivers/WePayPaymentDriver.php new file mode 100644 index 000000000000..79253491029c --- /dev/null +++ b/app/PaymentDrivers/WePayPaymentDriver.php @@ -0,0 +1,110 @@ + CreditCard::class, //maps GatewayType => Implementation class + ]; + + const SYSTEM_LOG_TYPE = SystemLog::TYPE_WEPAY; + + public function init() + { + + if (WePay::getEnvironment() == 'none') { + + if(config('ninja.wepay.environment') == 'staging') + WePay::useStaing(config('ninja.wepay.client_id'), config('ninja.wepay.client_secret')); + else + WePay::useProduction(config('ninja.wepay.client_id'), config('ninja.wepay.client_secret')); + + } + + if ($this->company_gateway) + $this->wepay = new WePay($this->company_gateway->getConfig()->accessToken); + + $this->wepay = new WePay(null); + + } + + public function setup(array $data) + { + return (new Setup($this))->boot($data); + } + + public function processSetup(Request $request) + { + return (new Setup($this))->processSignup($request); + } + + public function setPaymentMethod($payment_method_id) + { + $class = self::$methods[$payment_method_id]; + $this->payment_method = new $class($this); + return $this; + } + + public function authorizeView(array $data) + { + return $this->payment_method->authorizeView($data); //this is your custom implementation from here + } + + public function authorizeResponse($request) + { + return $this->payment_method->authorizeResponse($request); //this is your custom implementation from here + } + + public function processPaymentView(array $data) + { + return $this->payment_method->paymentView($data); //this is your custom implementation from here + } + + public function processPaymentResponse($request) + { + return $this->payment_method->paymentResponse($request); //this is your custom implementation from here + } + + public function refund(Payment $payment, $amount, $return_client_response = false) + { + return $this->payment_method->yourRefundImplementationHere(); //this is your custom implementation from here + } + + public function tokenBilling(ClientGatewayToken $cgt, PaymentHash $payment_hash) + { + return $this->payment_method->yourTokenBillingImplmentation(); //this is your custom implementation from here + } +} diff --git a/composer.json b/composer.json index 569fda98006f..72dc4b702128 100644 --- a/composer.json +++ b/composer.json @@ -70,6 +70,7 @@ "turbo124/beacon": "^1.0", "turbo124/laravel-gmail": "^5", "webpatser/laravel-countries": "dev-master#75992ad", + "wepay/php-sdk": "^0.3", "wildbit/swiftmailer-postmark": "^3.3" }, "require-dev": { @@ -136,4 +137,4 @@ }, "minimum-stability": "dev", "prefer-stable": true -} \ No newline at end of file +} diff --git a/composer.lock b/composer.lock index 8fa65833cc1f..ada9fd8b8449 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "38a79899673526624db4d62a76dd9a5e", + "content-hash": "8ebeefd50035c907152aebf54d1dbd21", "packages": [ { "name": "asm/php-ansible", @@ -10155,6 +10155,57 @@ }, "time": "2019-07-12T14:06:05+00:00" }, + { + "name": "wepay/php-sdk", + "version": "0.3.1", + "source": { + "type": "git", + "url": "https://github.com/wepay/PHP-SDK.git", + "reference": "2a89ceb2954d117d082f869d3bfcb7864e6c2a7d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wepay/PHP-SDK/zipball/2a89ceb2954d117d082f869d3bfcb7864e6c2a7d", + "reference": "2a89ceb2954d117d082f869d3bfcb7864e6c2a7d", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "wepay.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "WePay", + "email": "api@wepay.com" + } + ], + "description": "WePay APIv2 SDK for PHP", + "keywords": [ + "payment", + "sdk", + "wepay" + ], + "support": { + "issues": "https://github.com/wepay/PHP-SDK/issues", + "source": "https://github.com/wepay/PHP-SDK/tree/master" + }, + "time": "2017-01-21T07:03:26+00:00" + }, { "name": "wildbit/swiftmailer-postmark", "version": "3.3.0", diff --git a/config/ninja.php b/config/ninja.php index 4481e81ae629..b2ef34d1616d 100644 --- a/config/ninja.php +++ b/config/ninja.php @@ -148,4 +148,9 @@ return [ 'disable_auto_update' => env('DISABLE_AUTO_UPDATE', false), 'invoiceninja_hosted_pdf_generation' => env('NINJA_HOSTED_PDF', false), 'ninja_stripe_key' => env('NINJA_STRIPE_KEY', null), + 'wepay' => [ + 'environment' => env('WEPAY_ENVIRONMENT', 'staging'), + 'client_id' => env('WEPAY_CLIENT_ID', ''), + 'client_secret' => env('WEPAY_CLIENT_SECRET',''), + ] ]; diff --git a/database/migrations/2021_05_05_014713_activate_we_pay.php b/database/migrations/2021_05_05_014713_activate_we_pay.php new file mode 100644 index 000000000000..37ab245249c9 --- /dev/null +++ b/database/migrations/2021_05_05_014713_activate_we_pay.php @@ -0,0 +1,30 @@ +=1) + Gateway::whereIn('id', [49])->update(['visible' => true]); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} diff --git a/database/seeders/PaymentLibrariesSeeder.php b/database/seeders/PaymentLibrariesSeeder.php index 3f358f131534..1ddbef36d5e3 100644 --- a/database/seeders/PaymentLibrariesSeeder.php +++ b/database/seeders/PaymentLibrariesSeeder.php @@ -95,7 +95,7 @@ class PaymentLibrariesSeeder extends Seeder Gateway::query()->update(['visible' => 0]); - Gateway::whereIn('id', [1,15,20,39,55])->update(['visible' => 1]); + Gateway::whereIn('id', [1,15,20,39,55,49])->update(['visible' => 1]); Gateway::all()->each(function ($gateway) { $gateway->site_url = $gateway->getHelp(); diff --git a/resources/views/portal/ninja2020/gateways/wepay/signup/index.blade.php b/resources/views/portal/ninja2020/gateways/wepay/signup/index.blade.php new file mode 100644 index 000000000000..f8df912e7ea4 --- /dev/null +++ b/resources/views/portal/ninja2020/gateways/wepay/signup/index.blade.php @@ -0,0 +1,14 @@ +@extends('portal.ninja2020.layout.clean') +@section('meta_title', ctrans('texts.sign_up_with_wepay')) + +@section('body') + + @livewire('wepay-signup', ['user_id' => $user_id, 'company_key' => $company_key]) + + +@endsection + +@push('footer') + +@endpush \ No newline at end of file diff --git a/resources/views/portal/ninja2020/gateways/wepay/signup/wepay-signup.blade.php b/resources/views/portal/ninja2020/gateways/wepay/signup/wepay-signup.blade.php new file mode 100644 index 000000000000..5d61f82f9256 --- /dev/null +++ b/resources/views/portal/ninja2020/gateways/wepay/signup/wepay-signup.blade.php @@ -0,0 +1,93 @@ +
+ +
+ @csrf + @method('POST') +
+
+
+
+ + + @error('first_name') +
+ {{ $message }} +
+ @enderror +
+ +
+ + + @error('last_name') +
+ {{ $message }} +
+ @enderror +
+ +
+ + + @error('email') +
+ {{ $message }} +
+ @enderror +
+ +
+ + + @error('company_name') +
+ {{ $message }} +
+ @enderror +
+ +
+ + +
+ + {{ ctrans('texts.country_United States') }} +
+ +
+ + {{ ctrans('texts.country_Canada') }} +
+ +
+ + {{ ctrans('texts.country_United Kingdom') }} +
+ +
+ +
+ +
+ + {{ ctrans('texts.enable_ach')}} +
+
+ +
+ +
+ + {!! ctrans('texts.wepay_payment_tos_agree', ['terms' => $terms, 'privacy_policy' => $privacy_policy]) !!} +
+
+ +
+
+
+ +
+
+
+ +
\ No newline at end of file diff --git a/routes/api.php b/routes/api.php index 2e84a7977cc1..b18c8a593d47 100644 --- a/routes/api.php +++ b/routes/api.php @@ -199,6 +199,8 @@ Route::get('webcron', 'WebCronController@index'); Route::group(['middleware' => ['locale']], function () { Route::get('stripe_connect/{token}', 'StripeConnectController@initialize')->name('stripe_connect.initialization'); Route::get('stripe_connect/completed', 'StripeConnectController@completed')->name('stripe_connect.return'); + Route::get('wepay/signup/{token}', 'WePayController@signup')->name('wepay.signup'); + Route::post('wepay/processSignup', 'WePayController@processSignup')->name('wepay.process_signup'); }); Route::fallback('BaseController@notFound'); diff --git a/routes/client.php b/routes/client.php index fb228c2e7688..e2bc84f8618a 100644 --- a/routes/client.php +++ b/routes/client.php @@ -79,6 +79,7 @@ Route::group(['middleware' => ['auth:contact', 'locale', 'check_client_existence Route::post('upload', 'ClientPortal\UploadController')->name('upload.store'); Route::get('logout', 'Auth\ContactLoginController@logout')->name('logout'); + }); Route::get('client/subscription/{subscription}/purchase/', 'ClientPortal\SubscriptionPurchaseController@index')->name('client.subscription.purchase'); From d61e7f57a2906899715612b3f67b734f23e13beb Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 5 May 2021 16:21:37 +1000 Subject: [PATCH 02/43] Fixes for wepay --- app/Http/Controllers/WePayController.php | 2 +- .../ninja2020/gateways/wepay/signup/wepay-signup.blade.php | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/Http/Controllers/WePayController.php b/app/Http/Controllers/WePayController.php index 6dfe7496fbe8..978360948f88 100644 --- a/app/Http/Controllers/WePayController.php +++ b/app/Http/Controllers/WePayController.php @@ -44,8 +44,8 @@ class WePayController extends BaseController // $data['user_id'] = $this->encodePrimaryKey($hash['user_id']); // $data['company_key'] = $hash['company_key']; - $data['user_id'] = 1; $user = User::first(); + $data['user_id'] = $user->id; $data['company_key'] = $user->account->companies()->first()->company_key; diff --git a/resources/views/portal/ninja2020/gateways/wepay/signup/wepay-signup.blade.php b/resources/views/portal/ninja2020/gateways/wepay/signup/wepay-signup.blade.php index 5d61f82f9256..5a7f470e282d 100644 --- a/resources/views/portal/ninja2020/gateways/wepay/signup/wepay-signup.blade.php +++ b/resources/views/portal/ninja2020/gateways/wepay/signup/wepay-signup.blade.php @@ -82,6 +82,12 @@ +
+ + {{ ctrans('texts.standard_fees_apply')}} + +
+
From eb54e0705a7aedac6cd1b32cd2bd2550dcb4bf1c Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 5 May 2021 16:51:45 +1000 Subject: [PATCH 03/43] We Pay logo --- public/images/wepay.svg | 383 ++++++++++++++++++ .../gateways/wepay/signup/index.blade.php | 5 + 2 files changed, 388 insertions(+) create mode 100644 public/images/wepay.svg diff --git a/public/images/wepay.svg b/public/images/wepay.svg new file mode 100644 index 000000000000..25f541e8c251 --- /dev/null +++ b/public/images/wepay.svg @@ -0,0 +1,383 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/views/portal/ninja2020/gateways/wepay/signup/index.blade.php b/resources/views/portal/ninja2020/gateways/wepay/signup/index.blade.php index f8df912e7ea4..2152b2a1a235 100644 --- a/resources/views/portal/ninja2020/gateways/wepay/signup/index.blade.php +++ b/resources/views/portal/ninja2020/gateways/wepay/signup/index.blade.php @@ -2,6 +2,11 @@ @section('meta_title', ctrans('texts.sign_up_with_wepay')) @section('body') + + +
+ We Pay +
@livewire('wepay-signup', ['user_id' => $user_id, 'company_key' => $company_key]) From b6c538564e7ab796b91fc98c225dde01fa7dc5b9 Mon Sep 17 00:00:00 2001 From: = Date: Wed, 5 May 2021 19:06:55 +1000 Subject: [PATCH 04/43] Background --- .../portal/ninja2020/gateways/wepay/signup/index.blade.php | 4 ++-- routes/api.php | 3 +-- routes/web.php | 3 +++ 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/resources/views/portal/ninja2020/gateways/wepay/signup/index.blade.php b/resources/views/portal/ninja2020/gateways/wepay/signup/index.blade.php index 2152b2a1a235..d3149dc1b4f1 100644 --- a/resources/views/portal/ninja2020/gateways/wepay/signup/index.blade.php +++ b/resources/views/portal/ninja2020/gateways/wepay/signup/index.blade.php @@ -2,7 +2,7 @@ @section('meta_title', ctrans('texts.sign_up_with_wepay')) @section('body') - +
We Pay @@ -10,7 +10,7 @@ @livewire('wepay-signup', ['user_id' => $user_id, 'company_key' => $company_key]) - +
@endsection @push('footer') diff --git a/routes/api.php b/routes/api.php index b18c8a593d47..bf3031e2688a 100644 --- a/routes/api.php +++ b/routes/api.php @@ -199,8 +199,7 @@ Route::get('webcron', 'WebCronController@index'); Route::group(['middleware' => ['locale']], function () { Route::get('stripe_connect/{token}', 'StripeConnectController@initialize')->name('stripe_connect.initialization'); Route::get('stripe_connect/completed', 'StripeConnectController@completed')->name('stripe_connect.return'); - Route::get('wepay/signup/{token}', 'WePayController@signup')->name('wepay.signup'); - Route::post('wepay/processSignup', 'WePayController@processSignup')->name('wepay.process_signup'); + }); Route::fallback('BaseController@notFound'); diff --git a/routes/web.php b/routes/web.php index 119d7cd21921..3b3c57457606 100644 --- a/routes/web.php +++ b/routes/web.php @@ -20,6 +20,9 @@ Route::post('password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail' Route::get('password/reset/{token}', 'Auth\ResetPasswordController@showResetForm')->name('password.reset'); Route::post('password/reset', 'Auth\ResetPasswordController@reset')->name('password.update'); +Route::get('wepay/signup/{token}', 'WePayController@signup')->name('wepay.signup'); +Route::post('wepay/processSignup', 'WePayController@processSignup')->name('wepay.process_signup'); + /* * Social authentication */ From 46d5b84fb702edd53334bd9dab369330ba949c6e Mon Sep 17 00:00:00 2001 From: = Date: Wed, 5 May 2021 19:45:31 +1000 Subject: [PATCH 05/43] Fixes for created_at and updated_at timestamps in migration --- app/Jobs/Util/Import.php | 40 +++++++++++++++++++ .../Migration/InvoiceMigrationRepository.php | 9 ++++- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/app/Jobs/Util/Import.php b/app/Jobs/Util/Import.php index 9fc0a4ec0511..fc8354cd4d3a 100644 --- a/app/Jobs/Util/Import.php +++ b/app/Jobs/Util/Import.php @@ -488,6 +488,14 @@ class Import implements ShouldQueue ) ); + if(array_key_exists('created_at', $modified)) + $client->created_at = $modified['created_at']; + + if(array_key_exists('updated_at', $modified)) + $client->updated_at = $modified['updated_at']; + + $client->save(['timestamps' => false]); + $client->contacts()->forceDelete(); if (array_key_exists('contacts', $resource)) { // need to remove after importing new migration.json @@ -891,6 +899,14 @@ class Import implements ShouldQueue QuoteFactory::create($this->company->id, $modified['user_id']) ); + if(array_key_exists('created_at', $modified)) + $quote->created_at = $modified['created_at']; + + if(array_key_exists('updated_at', $modified)) + $quote->updated_at = $modified['updated_at']; + + $quote->save(['timestamps' => false]); + $old_user_key = array_key_exists('user_id', $resource) ?? $this->user->id; $key = "quotes_{$resource['id']}"; @@ -957,6 +973,14 @@ class Import implements ShouldQueue PaymentFactory::create($this->company->id, $modified['user_id']) ); + if(array_key_exists('created_at', $modified)) + $payment->created_at = $modified['created_at']; + + if(array_key_exists('updated_at', $modified)) + $payment->updated_at = $modified['updated_at']; + + $payment->save(['timestamps' => false]); + if (array_key_exists('company_gateway_id', $resource) && isset($resource['company_gateway_id']) && $resource['company_gateway_id'] != 'NULL') { $payment->company_gateway_id = $this->transformId('company_gateways', $resource['company_gateway_id']); $payment->save(); @@ -1319,6 +1343,14 @@ class Import implements ShouldQueue $task = Task::Create($modified); + if(array_key_exists('created_at', $modified)) + $task->created_at = $modified['created_at']; + + if(array_key_exists('updated_at', $modified)) + $task->updated_at = $modified['updated_at']; + + $task->save(['timestamps' => false]); + $old_user_key = array_key_exists('user_id', $resource) ?? $this->user->id; $this->ids['tasks'] = [ @@ -1399,6 +1431,14 @@ class Import implements ShouldQueue $expense = Expense::Create($modified); + if(array_key_exists('created_at', $modified)) + $expense->created_at = $modified['created_at']; + + if(array_key_exists('updated_at', $modified)) + $expense->updated_at = $modified['updated_at']; + + $expense->save(['timestamps' => false]); + $old_user_key = array_key_exists('user_id', $resource) ?? $this->user->id; $key = "expenses_{$resource['id']}"; diff --git a/app/Repositories/Migration/InvoiceMigrationRepository.php b/app/Repositories/Migration/InvoiceMigrationRepository.php index ee142650f2ba..90d8cb171a76 100644 --- a/app/Repositories/Migration/InvoiceMigrationRepository.php +++ b/app/Repositories/Migration/InvoiceMigrationRepository.php @@ -71,7 +71,14 @@ class InvoiceMigrationRepository extends BaseRepository $model->fill($tmp_data); $model->status_id = $tmp_data['status_id']; - $model->save(); + + if(array_key_exists('created_at', $data)) + $model->created_at = $data['created_at']; + + if(array_key_exists('updated_at', $data)) + $model->updated_at = $data['updated_at']; + + $model->save(['timestamps' => false]); if (array_key_exists('documents', $data)) { $this->saveDocuments($data['documents'], $model); From d3f14887e17d66f6abdd92c9ed269d020bd6cd5a Mon Sep 17 00:00:00 2001 From: = Date: Wed, 5 May 2021 20:50:36 +1000 Subject: [PATCH 06/43] WePay --- app/Http/Livewire/WepaySignup.php | 77 +++++++++++++++++-- app/PaymentDrivers/WePayPaymentDriver.php | 2 + .../wepay/signup/wepay-signup.blade.php | 15 ++-- routes/web.php | 2 +- 4 files changed, 84 insertions(+), 12 deletions(-) diff --git a/app/Http/Livewire/WepaySignup.php b/app/Http/Livewire/WepaySignup.php index d90b77c18d52..0b2636d60769 100644 --- a/app/Http/Livewire/WepaySignup.php +++ b/app/Http/Livewire/WepaySignup.php @@ -13,9 +13,12 @@ namespace App\Http\Livewire; use App\Models\Company; +use App\Models\CompanyGateway; use App\Models\User; +use App\PaymentDrivers\WePayPaymentDriver; use Illuminate\Support\Facades\Hash; use Livewire\Component; +use WePay; class WepaySignup extends Component { @@ -25,6 +28,10 @@ class WepaySignup extends Component public $first_name; public $last_name; public $email; + public $company_name; + public $country; + public $ach; + public $wepay_payment_tos_agree; public $terms; public $privacy_policy; @@ -32,9 +39,13 @@ class WepaySignup extends Component public $saved; protected $rules = [ - 'first_name' => ['sometimes'], - 'last_name' => ['sometimes'], + 'first_name' => ['required'], + 'last_name' => ['required'], 'email' => ['required', 'email'], + 'company_name' => ['required'], + 'country' => ['required'], + 'ach' => ['sometimes'], + 'wepay_payment_tos_agree' => ['accepted'], ]; public function mount() @@ -43,6 +54,9 @@ class WepaySignup extends Component $company = Company::where('company_key', $this->company_key)->first(); $this->fill([ + 'wepay_payment_tos_agree' => '', + 'ach' => '', + 'country' => 'US', 'user' => $user, 'first_name' => $user->first_name, 'last_name' => $user->last_name, @@ -64,10 +78,61 @@ class WepaySignup extends Component $data = $this->validate($this->rules); - // $this->user - // ->fill($data) - // ->save(); + $this->saved = ctrans('texts.processing'); + + $wepay_driver = new WePayPaymentDriver(new CompanyGateway, null, null); + + $wepay_driver->init(); + + $user_details = [ + 'client_id' => config('ninja.wepay.client_id'), + 'client_secret' => config('ninja.wepay.client_secret'), + 'email' => $data['email'], + 'first_name' => $data['first_name'], + 'last_name' => $data['last_name'], + 'original_ip' => request()->ip(), + 'original_device' => request()->server('HTTP_USER_AGENT'), + 'tos_acceptance_time' => time(), + 'redirect_uri' => route('wepay.process_signup'), + 'scope' => 'manage_accounts,collect_payments,view_user,preapprove_payments,send_money', + ]; + + $wepay_user = $wepay_driver->request('user/register/', $user_details); + + $access_token = $wepay_user->access_token; + + $access_token_expires = $wepay_user->expires_in ? (time() + $wepay_user->expires_in) : null; + + $wepay = new WePay($access_token); + + $account_details = [ + 'name' => $data['company_name']), + 'description' => ctrans('texts.wepay_account_description'), + 'theme_object' => json_decode({"name":"Invoice Ninja","primary_color":"0b4d78","secondary_color":"0b4d78","background_color":"f8f8f8","button_color":"33b753"}), + 'callback_uri' => $accountGateway->getWebhookUrl(), + 'rbits' => $account->present()->rBits, + 'country' => $data['country'], + ]; + + if (Input::get('country') == 'CA') { + $accountDetails['currencies'] = ['CAD']; + $accountDetails['country_options'] = ['debit_opt_in' => boolval(Input::get('debit_cards'))]; + } elseif (Input::get('country') == 'GB') { + $accountDetails['currencies'] = ['GBP']; + } + + $wepayAccount = $wepay->request('account/create/', $accountDetails); + + try { + $wepay->request('user/send_confirmation/', []); + $confirmationRequired = true; + } catch (\WePayException $ex) { + if ($ex->getMessage() == 'This access_token is already approved.') { + $confirmationRequired = false; + } else { + throw $ex; + } + } - $this->saved = ctrans('texts.saved_at', ['time' => now()->toTimeString()]); } } diff --git a/app/PaymentDrivers/WePayPaymentDriver.php b/app/PaymentDrivers/WePayPaymentDriver.php index 79253491029c..d6f281893dfe 100644 --- a/app/PaymentDrivers/WePayPaymentDriver.php +++ b/app/PaymentDrivers/WePayPaymentDriver.php @@ -59,6 +59,8 @@ class WePayPaymentDriver extends BaseDriver $this->wepay = new WePay(null); + return $this; + } public function setup(array $data) diff --git a/resources/views/portal/ninja2020/gateways/wepay/signup/wepay-signup.blade.php b/resources/views/portal/ninja2020/gateways/wepay/signup/wepay-signup.blade.php index 5a7f470e282d..74f37735eee4 100644 --- a/resources/views/portal/ninja2020/gateways/wepay/signup/wepay-signup.blade.php +++ b/resources/views/portal/ninja2020/gateways/wepay/signup/wepay-signup.blade.php @@ -50,17 +50,17 @@
- + {{ ctrans('texts.country_United States') }}
- + {{ ctrans('texts.country_Canada') }}
- + {{ ctrans('texts.country_United Kingdom') }}
@@ -69,7 +69,7 @@
- + {{ ctrans('texts.enable_ach')}}
@@ -77,9 +77,14 @@
- + {!! ctrans('texts.wepay_payment_tos_agree', ['terms' => $terms, 'privacy_policy' => $privacy_policy]) !!}
+ @error('wepay_payment_tos_agree') +
+ {{ $message }} +
+ @enderror
diff --git a/routes/web.php b/routes/web.php index 3b3c57457606..60fb651c3a11 100644 --- a/routes/web.php +++ b/routes/web.php @@ -21,7 +21,7 @@ Route::get('password/reset/{token}', 'Auth\ResetPasswordController@showResetForm Route::post('password/reset', 'Auth\ResetPasswordController@reset')->name('password.update'); Route::get('wepay/signup/{token}', 'WePayController@signup')->name('wepay.signup'); -Route::post('wepay/processSignup', 'WePayController@processSignup')->name('wepay.process_signup'); +Route::get('wepay/processSignup', 'WePayController@processSignup')->name('wepay.process_signup'); /* * Social authentication From eff664a586e786556a80b337310231882356cec2 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 6 May 2021 14:39:18 +1000 Subject: [PATCH 07/43] WePayg --- app/Http/Livewire/WepaySignup.php | 29 +++++++++++----------- app/Models/Company.php | 20 +++++++++++++++ app/Models/Presenters/CompanyPresenter.php | 4 +++ app/Models/Presenters/UserPresenter.php | 24 ++++++++++++++++++ 4 files changed, 63 insertions(+), 14 deletions(-) diff --git a/app/Http/Livewire/WepaySignup.php b/app/Http/Livewire/WepaySignup.php index 0b2636d60769..33951d020736 100644 --- a/app/Http/Livewire/WepaySignup.php +++ b/app/Http/Livewire/WepaySignup.php @@ -37,6 +37,7 @@ class WepaySignup extends Component public $privacy_policy; public $saved; + public $company; protected $rules = [ 'first_name' => ['required'], @@ -51,8 +52,8 @@ class WepaySignup extends Component public function mount() { $user = User::find($this->user_id); - $company = Company::where('company_key', $this->company_key)->first(); - + $this->company = Company::where('company_key', $this->company_key)->firstOrFail(); + $this->fill([ 'wepay_payment_tos_agree' => '', 'ach' => '', @@ -61,7 +62,7 @@ class WepaySignup extends Component 'first_name' => $user->first_name, 'last_name' => $user->last_name, 'email' => $user->email, - 'company_name' => $company->present()->name(), + 'company_name' => $this->company->present()->name(), 'saved' => ctrans('texts.confirm'), 'terms' => ''.ctrans('texts.terms_of_service').'', 'privacy_policy' => ''.ctrans('texts.privacy_policy').'', @@ -106,29 +107,29 @@ class WepaySignup extends Component $wepay = new WePay($access_token); $account_details = [ - 'name' => $data['company_name']), + 'name' => $data['company_name'], 'description' => ctrans('texts.wepay_account_description'), - 'theme_object' => json_decode({"name":"Invoice Ninja","primary_color":"0b4d78","secondary_color":"0b4d78","background_color":"f8f8f8","button_color":"33b753"}), + 'theme_object' => json_decode('{"name":"Invoice Ninja","primary_color":"0b4d78","secondary_color":"0b4d78","background_color":"f8f8f8","button_color":"33b753"}'), 'callback_uri' => $accountGateway->getWebhookUrl(), - 'rbits' => $account->present()->rBits, + 'rbits' => $this->company->present()->rBits, 'country' => $data['country'], ]; - if (Input::get('country') == 'CA') { - $accountDetails['currencies'] = ['CAD']; - $accountDetails['country_options'] = ['debit_opt_in' => boolval(Input::get('debit_cards'))]; - } elseif (Input::get('country') == 'GB') { - $accountDetails['currencies'] = ['GBP']; + if ($data['country'] == 'CA') { + $account_details['currencies'] = ['CAD']; + $account_details['country_options'] = ['debit_opt_in' => boolval($data['debit_cards'])]; + } elseif ($data['country'] == 'GB') { + $account_details['currencies'] = ['GBP']; } - $wepayAccount = $wepay->request('account/create/', $accountDetails); + $wepay_account = $wepay->request('account/create/', $account_details); try { $wepay->request('user/send_confirmation/', []); - $confirmationRequired = true; + $confirmation_required = true; } catch (\WePayException $ex) { if ($ex->getMessage() == 'This access_token is already approved.') { - $confirmationRequired = false; + $confirmation_required = false; } else { throw $ex; } diff --git a/app/Models/Company.php b/app/Models/Company.php index c0ed357362dc..b262a63ba3f9 100644 --- a/app/Models/Company.php +++ b/app/Models/Company.php @@ -442,4 +442,24 @@ class Company extends BaseModel { return $this->slack_webhook_url; } + + public function rBits() + { + $account = $this->account; + $user = $this->owner(); + $data = []; + + $data[] = $this->createRBit('business_name', 'user', ['business_name' => $this->present()->name()]); + $data[] = $this->createRBit('industry_code', 'user', ['industry_detail' => $this->industry->name]); + $data[] = $this->createRBit('comment', 'partner_database', ['comment_text' => 'Logo image not present']); + $data[] = $this->createRBit('business_description', 'user', ['business_description' => $company->present()->size]); + + $data[] = $this->createRBit('person', 'user', ['name' => $user->present()->getFullName()]); + $data[] = $this->createRBit('email', 'user', ['email' => $user->email]); + $data[] = $this->createRBit('phone', 'user', ['phone' => $user->phone]); + $data[] = $this->createRBit('website_uri', 'user', ['uri' => $this->settings->website]); + $data[] = $this->createRBit('external_account', 'partner_database', ['is_partner_account' => 'yes', 'account_type' => 'Invoice Ninja', 'create_time' => time()]); + + return $data; + } } diff --git a/app/Models/Presenters/CompanyPresenter.php b/app/Models/Presenters/CompanyPresenter.php index d7cb21a24b56..7251e7fd9f81 100644 --- a/app/Models/Presenters/CompanyPresenter.php +++ b/app/Models/Presenters/CompanyPresenter.php @@ -106,4 +106,8 @@ class CompanyPresenter extends EntityPresenter "SPC\n0200\n1\n{$user_iban}\nK\n{$this->name}\n{$settings->address1}\n{$settings->postal_code} {$settings->city}\n\n\nCH\n\n\n\n\n\n\n\n{$balance_due_raw}\n{$client_currency}\n\n\n\n\n\n\n\nNON\n\n{$invoice_number}\nEPD\n"; } + public function size() + { + return $this->entity->size ? $this->entity->size->name : ''; + } } diff --git a/app/Models/Presenters/UserPresenter.php b/app/Models/Presenters/UserPresenter.php index f7c23d7699f4..b45220fa5512 100644 --- a/app/Models/Presenters/UserPresenter.php +++ b/app/Models/Presenters/UserPresenter.php @@ -26,4 +26,28 @@ class UserPresenter extends EntityPresenter return $first_name.' '.$last_name; } + + + public function getDisplayName() + { + if ($this->getFullName()) { + return $this->getFullName(); + } elseif ($this->entity->email) { + return $this->entity->email; + } else { + return ctrans('texts.guest'); + } + } + + /** + * @return string + */ + public function getFullName() + { + if ($this->entity->first_name || $this->entity->last_name) { + return $this->entity->first_name.' '.$this->entity->last_name; + } else { + return ''; + } + } } From 2b8d7c26dabedf2f6bf493d972975f6fddb88f61 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 6 May 2021 15:38:39 +1000 Subject: [PATCH 08/43] Fixes for checkout gateway --- app/Http/Livewire/WepaySignup.php | 1 + app/Models/Company.php | 4 ++-- database/seeders/PaymentLibrariesSeeder.php | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/Http/Livewire/WepaySignup.php b/app/Http/Livewire/WepaySignup.php index 33951d020736..28a13c8c1857 100644 --- a/app/Http/Livewire/WepaySignup.php +++ b/app/Http/Livewire/WepaySignup.php @@ -76,6 +76,7 @@ class WepaySignup extends Component public function submit() { + //need to create or get a new WePay CompanyGateway $data = $this->validate($this->rules); diff --git a/app/Models/Company.php b/app/Models/Company.php index b262a63ba3f9..a750ad81c89f 100644 --- a/app/Models/Company.php +++ b/app/Models/Company.php @@ -452,12 +452,12 @@ class Company extends BaseModel $data[] = $this->createRBit('business_name', 'user', ['business_name' => $this->present()->name()]); $data[] = $this->createRBit('industry_code', 'user', ['industry_detail' => $this->industry->name]); $data[] = $this->createRBit('comment', 'partner_database', ['comment_text' => 'Logo image not present']); - $data[] = $this->createRBit('business_description', 'user', ['business_description' => $company->present()->size]); + $data[] = $this->createRBit('business_description', 'user', ['business_description' => $company->present()->size()]); $data[] = $this->createRBit('person', 'user', ['name' => $user->present()->getFullName()]); $data[] = $this->createRBit('email', 'user', ['email' => $user->email]); $data[] = $this->createRBit('phone', 'user', ['phone' => $user->phone]); - $data[] = $this->createRBit('website_uri', 'user', ['uri' => $this->settings->website]); + $data[] = $this->createRBit('website_uri', 'user', ['uri' => $this->entity->settings->website]); $data[] = $this->createRBit('external_account', 'partner_database', ['is_partner_account' => 'yes', 'account_type' => 'Invoice Ninja', 'create_time' => time()]); return $data; diff --git a/database/seeders/PaymentLibrariesSeeder.php b/database/seeders/PaymentLibrariesSeeder.php index 1ddbef36d5e3..fafb7eac368d 100644 --- a/database/seeders/PaymentLibrariesSeeder.php +++ b/database/seeders/PaymentLibrariesSeeder.php @@ -62,7 +62,7 @@ class PaymentLibrariesSeeder extends Seeder ['id' => 36, 'name' => 'AGMS', 'provider' => 'Agms', 'key' => '1b3c6f3ccfea4f5e7eadeae188cccd7f', 'fields' => '{"username":"","password":"","apiKey":"","accountNumber":""}'], ['id' => 37, 'name' => 'Barclays', 'provider' => 'BarclaysEpdq\Essential', 'key' => '7cba6ce5c125f9cb47ea8443ae671b68', 'fields' => '{"clientId":"","testMode":false,"language":"en_US","callbackMethod":"POST"}'], ['id' => 38, 'name' => 'Cardgate', 'provider' => 'Cardgate', 'key' => 'b98cfa5f750e16cee3524b7b7e78fbf6', 'fields' => '{"merchantId":"","language":"nl","apiKey":"","siteId":"","notifyUrl":"","returnUrl":"","cancelUrl":"","testMode":false}'], - ['id' => 39, 'name' => 'Checkout.com', 'provider' => 'CheckoutCom', 'key' => '3758e7f7c6f4cecf0f4f348b9a00f456', 'fields' => '{"secretApiKey":"","publicApiKey":"","testMode":false,"threeds:false"}'], + ['id' => 39, 'name' => 'Checkout.com', 'provider' => 'CheckoutCom', 'key' => '3758e7f7c6f4cecf0f4f348b9a00f456', 'fields' => '{"secretApiKey":"","publicApiKey":"","testMode":false,"threeds":false}'], ['id' => 40, 'name' => 'Creditcall', 'provider' => 'Creditcall', 'key' => 'cbc7ef7c99d31ec05492fbcb37208263', 'fields' => '{"terminalId":"","transactionKey":"","testMode":false,"verifyCvv":true,"verifyAddress":false,"verifyZip":false}'], ['id' => 41, 'name' => 'Cybersource', 'provider' => 'Cybersource', 'key' => 'e186a98d3b079028a73390bdc11bdb82', 'fields' => '{"profileId":"","secretKey":"","accessKey":"","testMode":false}'], ['id' => 42, 'name' => 'ecoPayz', 'provider' => 'Ecopayz', 'key' => '761040aca40f685d1ab55e2084b30670', 'fields' => '{"merchantId":"","merchantPassword":"","merchantAccountNumber":"","testMode":false}'], From 1a947d720d65eb8146b83c635755b251bdd028e7 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 6 May 2021 19:39:24 +1000 Subject: [PATCH 09/43] we pay --- app/Http/Livewire/WepaySignup.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/Http/Livewire/WepaySignup.php b/app/Http/Livewire/WepaySignup.php index 28a13c8c1857..783bff6f33da 100644 --- a/app/Http/Livewire/WepaySignup.php +++ b/app/Http/Livewire/WepaySignup.php @@ -32,6 +32,7 @@ class WepaySignup extends Component public $country; public $ach; public $wepay_payment_tos_agree; + public $debit_cards; public $terms; public $privacy_policy; @@ -77,6 +78,13 @@ class WepaySignup extends Component public function submit() { //need to create or get a new WePay CompanyGateway + $cg = CompanyGateway::where('id', 49) + ->where('company_id', $this->company->id) + ->firstOrNew(); + + if(!$cg->id) { + + } $data = $this->validate($this->rules); @@ -111,7 +119,7 @@ class WepaySignup extends Component 'name' => $data['company_name'], 'description' => ctrans('texts.wepay_account_description'), 'theme_object' => json_decode('{"name":"Invoice Ninja","primary_color":"0b4d78","secondary_color":"0b4d78","background_color":"f8f8f8","button_color":"33b753"}'), - 'callback_uri' => $accountGateway->getWebhookUrl(), + 'callback_uri' => route('payment_webhook', ['company_key' => $this->company->company_key, 'company_gateway_id' => $cg->hashed_id]), 'rbits' => $this->company->present()->rBits, 'country' => $data['country'], ]; From 7fbb9c9f0f280e1da69d841e5eb7813951c98ccc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Thu, 6 May 2021 12:03:32 +0200 Subject: [PATCH 10/43] - Set body class on the body - Show the Canada specific output --- public/css/app.css | 2 +- public/mix-manifest.json | 2 +- .../gateways/wepay/signup/index.blade.php | 10 +- .../wepay/signup/wepay-signup.blade.php | 232 ++++++++++++++++-- 4 files changed, 210 insertions(+), 36 deletions(-) diff --git a/public/css/app.css b/public/css/app.css index 9c5b8ef2d717..945e883d62d6 100755 --- a/public/css/app.css +++ b/public/css/app.css @@ -1 +1 @@ -/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}details{display:block}summary{display:list-item}[hidden],template{display:none}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}button{background-color:transparent;background-image:none}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}fieldset,ol,ul{margin:0;padding:0}ol,ul{list-style:none}html{font-family:Open Sans,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}*,:after,:before{box-sizing:border-box;border:0 solid #d2d6dc}hr{border-top-width:1px}img{border-style:solid}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{color:#a0aec0}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#a0aec0}input::placeholder,textarea::placeholder{color:#a0aec0}[role=button],button{cursor:pointer}table{border-collapse:collapse}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}button,input,optgroup,select,textarea{padding:0;line-height:inherit;color:inherit}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}.form-select{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none'%3E%3Cpath d='M7 7l3-3 3 3m0 6l-3 3-3-3' stroke='%239fa6b2' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");-webkit-appearance:none;-moz-appearance:none;appearance:none;-webkit-print-color-adjust:exact;color-adjust:exact;background-repeat:no-repeat;background-color:#fff;border-color:#d2d6dc;border-width:1px;border-radius:.375rem;padding:.5rem 2.5rem .5rem .75rem;font-size:1rem;line-height:1.5;background-position:right .5rem center;background-size:1.5em 1.5em}.form-select::-ms-expand{color:#9fa6b2;border:none}@media not print{.form-select::-ms-expand{display:none}}@media print and (-ms-high-contrast:active),print and (-ms-high-contrast:none){.form-select{padding-right:.75rem}}.form-select:focus{outline:none;box-shadow:0 0 0 3px rgba(164,202,254,.45);border-color:#a4cafe}.form-checkbox:checked{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 16 16' fill='%23fff' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M5.707 7.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4a1 1 0 00-1.414-1.414L7 8.586 5.707 7.293z'/%3E%3C/svg%3E");border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:50%;background-repeat:no-repeat}@media not print{.form-checkbox::-ms-check{border-width:1px;color:transparent;background:inherit;border-color:inherit;border-radius:inherit}}.form-checkbox{-webkit-appearance:none;-moz-appearance:none;appearance:none;-webkit-print-color-adjust:exact;color-adjust:exact;display:inline-block;vertical-align:middle;background-origin:border-box;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;flex-shrink:0;height:1rem;width:1rem;color:#3f83f8;background-color:#fff;border-color:#d2d6dc;border-width:1px;border-radius:.25rem}.form-checkbox:focus{outline:none;box-shadow:0 0 0 3px rgba(164,202,254,.45);border-color:#a4cafe}.form-checkbox:checked:focus,.form-radio:checked{border-color:transparent}.form-radio:checked{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 16 16' fill='%23fff' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='8' cy='8' r='3'/%3E%3C/svg%3E");background-color:currentColor;background-size:100% 100%;background-position:50%;background-repeat:no-repeat}@media not print{.form-radio::-ms-check{border-width:1px;color:transparent;background:inherit;border-color:inherit;border-radius:inherit}}.form-radio{-webkit-appearance:none;-moz-appearance:none;appearance:none;-webkit-print-color-adjust:exact;color-adjust:exact;display:inline-block;vertical-align:middle;background-origin:border-box;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;flex-shrink:0;border-radius:100%;height:1rem;width:1rem;color:#3f83f8;background-color:#fff;border-color:#d2d6dc;border-width:1px}.form-radio:focus{outline:none;box-shadow:0 0 0 3px rgba(164,202,254,.45);border-color:#a4cafe}.form-radio:checked:focus{border-color:transparent}.button{border-radius:.25rem;padding:.75rem 1rem;font-size:.875rem;line-height:1rem;transition-property:background-color,border-color,color,fill,stroke,opacity,box-shadow,transform;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}button:disabled{opacity:.5;cursor:not-allowed}.button-primary{--text-opacity:1;color:#fff;color:rgba(255,255,255,var(--text-opacity))}.button-primary:hover{font-weight:600}.button-block{display:block;width:100%}.button-danger{--bg-opacity:1;background-color:#f05252;background-color:rgba(240,82,82,var(--bg-opacity));--text-opacity:1;color:#fff;color:rgba(255,255,255,var(--text-opacity))}.button-danger:hover{--bg-opacity:1;background-color:#e02424;background-color:rgba(224,36,36,var(--bg-opacity))}.button-secondary{--bg-opacity:1;background-color:#f4f5f7;background-color:rgba(244,245,247,var(--bg-opacity))}.button-secondary:hover{--bg-opacity:1;background-color:#e5e7eb;background-color:rgba(229,231,235,var(--bg-opacity))}.button-link:hover{font-weight:600;text-decoration:underline}.button-link:focus{outline:2px solid transparent;outline-offset:2px;text-decoration:underline}.validation{border-left-width:2px;margin-top:.5rem;margin-bottom:.25rem;--bg-opacity:1;background-color:#f4f5f7;background-color:rgba(244,245,247,var(--bg-opacity));padding:.25rem .75rem}.validation-fail{border-color:#f05252;border-color:rgba(240,82,82,var(--border-opacity))}.validation-fail,.validation-pass{--border-opacity:1;--text-opacity:1;color:#374151;color:rgba(55,65,81,var(--text-opacity));font-size:.875rem}.validation-pass{border-color:#0e9f6e;border-color:rgba(14,159,110,var(--border-opacity))}.input{align-items:center;border-width:1px;--border-opacity:1;border-color:#d2d6dc;border-color:rgba(210,214,220,var(--border-opacity));border-radius:.25rem;margin-top:.5rem;padding:.5rem 1rem;font-size:.875rem}.input:focus{outline:2px solid transparent;outline-offset:2px;--bg-opacity:1;background-color:#f9fafb;background-color:rgba(249,250,251,var(--bg-opacity))}.input-label{font-size:.875rem;--text-opacity:1;color:#4b5563;color:rgba(75,85,99,var(--text-opacity))}.input-slim{padding-top:.5rem;padding-bottom:.5rem}.alert{padding:.75rem 1rem;font-size:.875rem;border-left-width:2px;margin-top:.5rem;margin-bottom:.25rem;--bg-opacity:1;background-color:#f4f5f7;background-color:rgba(244,245,247,var(--bg-opacity));--border-opacity:1;border-color:#9fa6b2;border-color:rgba(159,166,178,var(--border-opacity))}.alert-success{--border-opacity:1;border-color:#0e9f6e;border-color:rgba(14,159,110,var(--border-opacity))}.alert-failure{--border-opacity:1;border-color:#f05252;border-color:rgba(240,82,82,var(--border-opacity))}.badge{display:inline-flex;align-items:center;padding:.125rem .625rem;border-radius:9999px;font-size:.75rem;font-weight:500;line-height:1rem}.badge-light{background-color:#f4f5f7;background-color:rgba(244,245,247,var(--bg-opacity));color:#252f3f;color:rgba(37,47,63,var(--text-opacity))}.badge-light,.badge-primary{--bg-opacity:1;--text-opacity:1}.badge-primary{background-color:#c3ddfd;background-color:rgba(195,221,253,var(--bg-opacity));color:#3f83f8;color:rgba(63,131,248,var(--text-opacity))}.badge-danger{background-color:#fde8e8;background-color:rgba(253,232,232,var(--bg-opacity));color:#f05252;color:rgba(240,82,82,var(--text-opacity))}.badge-danger,.badge-success{--bg-opacity:1;--text-opacity:1}.badge-success{background-color:#def7ec;background-color:rgba(222,247,236,var(--bg-opacity));color:#0e9f6e;color:rgba(14,159,110,var(--text-opacity))}.badge-secondary{--bg-opacity:1;background-color:#252f3f;background-color:rgba(37,47,63,var(--bg-opacity));--text-opacity:1;color:#e5e7eb;color:rgba(229,231,235,var(--text-opacity))}.badge-warning{background-color:#feecdc;background-color:rgba(254,236,220,var(--bg-opacity));color:#ff5a1f;color:rgba(255,90,31,var(--text-opacity))}.badge-info,.badge-warning{--bg-opacity:1;--text-opacity:1}.badge-info{background-color:#e1effe;background-color:rgba(225,239,254,var(--bg-opacity));color:#3f83f8;color:rgba(63,131,248,var(--text-opacity))}@media (min-width:640px){.dataTables_length{margin-top:1.25rem!important;margin-bottom:1.25rem!important}}@media (min-width:1024px){.dataTables_length{margin-top:1rem!important;margin-bottom:1rem!important}}.dataTables_length select{--bg-opacity:1!important;background-color:#fff!important;background-color:rgba(255,255,255,var(--bg-opacity))!important;align-items:center!important;border-width:1px!important;--border-opacity:1!important;border-color:#d2d6dc!important;border-color:rgba(210,214,220,var(--border-opacity))!important;border-radius:.25rem!important;margin-top:.5rem!important;font-size:.875rem!important;margin-left:.5rem!important;margin-right:.5rem!important;padding:.5rem!important}.dataTables_filter{margin-bottom:1rem}.dataTables_filter input{-webkit-appearance:none!important;-moz-appearance:none!important;appearance:none!important;background-color:#fff!important;border-radius:.375rem!important;font-size:1rem!important;line-height:1.5!important;align-items:center!important;border-width:1px!important;--border-opacity:1!important;border-color:#d2d6dc!important;border-color:rgba(210,214,220,var(--border-opacity))!important;border-radius:.25rem!important;margin-top:.5rem!important;padding:.5rem 1rem!important;font-size:.875rem!important}@media (min-width:1024px){.dataTables_filter{margin-top:-3rem!important}}.dataTables_paginate{padding-bottom:1.5rem!important;padding-top:.5rem!important}.dataTables_paginate .paginate_button{transition-property:background-color,border-color,color,fill,stroke,opacity,box-shadow,transform!important;transition-duration:.15s!important;transition-timing-function:cubic-bezier(.4,0,.2,1)!important;--bg-opacity:1!important;background-color:#fff!important;background-color:rgba(255,255,255,var(--bg-opacity))!important;border-width:1px!important;--border-opacity:1!important;border-color:#d2d6dc!important;border-color:rgba(210,214,220,var(--border-opacity))!important;font-size:.875rem!important;line-height:1rem!important;font-weight:500!important;border-radius:.25rem!important;--text-opacity:1!important;color:#374151!important;color:rgba(55,65,81,var(--text-opacity))!important;margin-right:.25rem!important;padding:.5rem 1rem!important;cursor:pointer!important}.dataTables_paginate .current{--bg-opacity:1!important;background-color:#1c64f2!important;background-color:rgba(28,100,242,var(--bg-opacity))!important;--text-opacity:1!important;color:#fff!important;color:rgba(255,255,255,var(--text-opacity))!important}.dataTables_info{font-size:.875rem!important}.dataTables_empty{padding-top:1rem!important;padding-bottom:1rem!important}.pagination{display:flex!important;align-items:center!important}.pagination .page-link{margin-top:-1px!important;border-top-width:2px!important;border-color:transparent!important;padding-top:1rem!important;padding-left:1rem!important;padding-right:1rem!important;display:inline-flex!important;align-items:center!important;font-size:.875rem!important;line-height:1.25rem!important;font-weight:500!important;--text-opacity:1!important;color:#6b7280!important;color:rgba(107,114,128,var(--text-opacity))!important;transition-property:background-color,border-color,color,fill,stroke,opacity,box-shadow,transform!important;transition-timing-function:cubic-bezier(.4,0,.2,1)!important;transition-duration:.15s!important;cursor:pointer!important}.pagination .page-link:hover{--text-opacity:1!important;color:#374151!important;color:rgba(55,65,81,var(--text-opacity))!important;--border-opacity:1!important;border-color:#d2d6dc!important;border-color:rgba(210,214,220,var(--border-opacity))!important}.pagination .page-link:focus{outline:2px solid transparent;outline-offset:2px;--text-opacity:1;color:#374151;color:rgba(55,65,81,var(--text-opacity));--border-opacity:1;border-color:#9fa6b2;border-color:rgba(159,166,178,var(--border-opacity))}.pagination .active>span{--text-opacity:1!important;color:#1c64f2!important;color:rgba(28,100,242,var(--text-opacity))!important;--border-opacity:1!important;border-color:#1c64f2!important;border-color:rgba(28,100,242,var(--border-opacity))!important}.space-x-1>:not(template)~:not(template){--space-x-reverse:0;margin-right:calc(0.25rem*var(--space-x-reverse));margin-left:calc(0.25rem*(1 - var(--space-x-reverse)))}.space-x-2>:not(template)~:not(template){--space-x-reverse:0;margin-right:calc(0.5rem*var(--space-x-reverse));margin-left:calc(0.5rem*(1 - var(--space-x-reverse)))}.bg-white{--bg-opacity:1;background-color:#fff;background-color:rgba(255,255,255,var(--bg-opacity))}.bg-gray-50{--bg-opacity:1;background-color:#f9fafb;background-color:rgba(249,250,251,var(--bg-opacity))}.bg-gray-100{--bg-opacity:1;background-color:#f4f5f7;background-color:rgba(244,245,247,var(--bg-opacity))}.bg-gray-500{--bg-opacity:1;background-color:#6b7280;background-color:rgba(107,114,128,var(--bg-opacity))}.bg-gray-600{--bg-opacity:1;background-color:#4b5563;background-color:rgba(75,85,99,var(--bg-opacity))}.bg-red-100{--bg-opacity:1;background-color:#fde8e8;background-color:rgba(253,232,232,var(--bg-opacity))}.bg-blue-50{--bg-opacity:1;background-color:#ebf5ff;background-color:rgba(235,245,255,var(--bg-opacity))}.bg-blue-600{--bg-opacity:1;background-color:#1c64f2;background-color:rgba(28,100,242,var(--bg-opacity))}.focus\:bg-gray-100:focus,.hover\:bg-gray-100:hover{--bg-opacity:1;background-color:#f4f5f7;background-color:rgba(244,245,247,var(--bg-opacity))}.focus\:bg-gray-600:focus{--bg-opacity:1;background-color:#4b5563;background-color:rgba(75,85,99,var(--bg-opacity))}.active\:bg-gray-50:active{--bg-opacity:1;background-color:#f9fafb;background-color:rgba(249,250,251,var(--bg-opacity))}.border-collapse{border-collapse:collapse}.border-black{--border-opacity:1;border-color:#000;border-color:rgba(0,0,0,var(--border-opacity))}.border-gray-100{--border-opacity:1;border-color:#f4f5f7;border-color:rgba(244,245,247,var(--border-opacity))}.border-gray-200{--border-opacity:1;border-color:#e5e7eb;border-color:rgba(229,231,235,var(--border-opacity))}.border-gray-300{--border-opacity:1;border-color:#d2d6dc;border-color:rgba(210,214,220,var(--border-opacity))}.border-red-300{--border-opacity:1;border-color:#f8b4b4;border-color:rgba(248,180,180,var(--border-opacity))}.border-red-400{--border-opacity:1;border-color:#f98080;border-color:rgba(249,128,128,var(--border-opacity))}.border-green-500{--border-opacity:1;border-color:#0e9f6e;border-color:rgba(14,159,110,var(--border-opacity))}.border-blue-500{--border-opacity:1;border-color:#3f83f8;border-color:rgba(63,131,248,var(--border-opacity))}.group:hover .group-hover\:border-transparent{border-color:transparent}.hover\:border-gray-800:hover{--border-opacity:1;border-color:#252f3f;border-color:rgba(37,47,63,var(--border-opacity))}.hover\:border-blue-600:hover{--border-opacity:1;border-color:#1c64f2;border-color:rgba(28,100,242,var(--border-opacity))}.focus\:border-blue-300:focus{--border-opacity:1;border-color:#a4cafe;border-color:rgba(164,202,254,var(--border-opacity))}.rounded-sm{border-radius:.125rem}.rounded{border-radius:.25rem}.rounded-md{border-radius:.375rem}.rounded-lg{border-radius:.5rem}.rounded-full{border-radius:9999px}.border-dashed{border-style:dashed}.border-none{border-style:none}.border-0{border-width:0}.border-4{border-width:4px}.border{border-width:1px}.border-l-2{border-left-width:2px}.border-t{border-top-width:1px}.border-r{border-right-width:1px}.border-b{border-bottom-width:1px}.cursor-pointer{cursor:pointer}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.font-medium{font-weight:500}.font-semibold{font-weight:600}.font-bold{font-weight:700}.focus\:font-semibold:focus,.hover\:font-semibold:hover{font-weight:600}.h-0{height:0}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-8{height:2rem}.h-12{height:3rem}.h-16{height:4rem}.h-32{height:8rem}.h-auto{height:auto}.h-screen{height:100vh}.text-xs{font-size:.75rem}.text-sm{font-size:.875rem}.text-lg{font-size:1.125rem}.text-xl{font-size:1.25rem}.text-2xl{font-size:1.5rem}.text-3xl{font-size:1.875rem}.leading-4{line-height:1rem}.leading-5{line-height:1.25rem}.leading-6{line-height:1.5rem}.leading-9{line-height:2.25rem}.m-0{margin:0}.m-auto{margin:auto}.mx-2{margin-left:.5rem;margin-right:.5rem}.my-4{margin-top:1rem;margin-bottom:1rem}.mx-4{margin-left:1rem;margin-right:1rem}.my-10{margin-top:2.5rem;margin-bottom:2.5rem}.mx-auto{margin-left:auto;margin-right:auto}.-my-2{margin-top:-.5rem;margin-bottom:-.5rem}.mt-0{margin-top:0}.mb-0{margin-bottom:0}.ml-0{margin-left:0}.mt-1{margin-top:.25rem}.mr-1{margin-right:.25rem}.ml-1{margin-left:.25rem}.mt-2{margin-top:.5rem}.mr-2{margin-right:.5rem}.mb-2{margin-bottom:.5rem}.ml-2{margin-left:.5rem}.mt-3{margin-top:.75rem}.mr-3{margin-right:.75rem}.mb-3{margin-bottom:.75rem}.ml-3{margin-left:.75rem}.mt-4{margin-top:1rem}.mr-4{margin-right:1rem}.mb-4{margin-bottom:1rem}.ml-4{margin-left:1rem}.mt-5{margin-top:1.25rem}.mt-6{margin-top:1.5rem}.mb-6{margin-bottom:1.5rem}.mt-8{margin-top:2rem}.mt-10{margin-top:2.5rem}.mb-10{margin-bottom:2.5rem}.mt-16{margin-top:4rem}.-mr-1{margin-right:-.25rem}.-ml-1{margin-left:-.25rem}.-mt-4{margin-top:-1rem}.-ml-4{margin-left:-1rem}.-mr-14{margin-right:-3.5rem}.max-w-xs{max-width:20rem}.max-w-xl{max-width:36rem}.max-w-2xl{max-width:42rem}.min-h-screen{min-height:100vh}.min-w-full{min-width:100%}.object-cover{-o-object-fit:cover;object-fit:cover}.opacity-0{opacity:0}.opacity-25{opacity:.25}.opacity-75{opacity:.75}.opacity-100{opacity:1}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overflow-y-scroll{overflow-y:scroll}.p-1{padding:.25rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-8{padding:2rem}.p-10{padding:2.5rem}.py-0{padding-top:0;padding-bottom:0}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.px-2{padding-left:.5rem;padding-right:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.px-3{padding-left:.75rem;padding-right:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.px-4{padding-left:1rem;padding-right:1rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.pl-0{padding-left:0}.pt-4{padding-top:1rem}.pr-4{padding-right:1rem}.pb-4{padding-bottom:1rem}.pt-5{padding-top:1.25rem}.pt-6{padding-top:1.5rem}.pb-20{padding-bottom:5rem}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{right:0;left:0}.inset-0,.inset-y-0{top:0;bottom:0}.inset-x-0{right:0;left:0}.top-0{top:0}.right-0{right:0}.bottom-0{bottom:0}.left-0{left:0}.shadow-xs{box-shadow:0 0 0 1px rgba(0,0,0,.05)}.shadow-sm{box-shadow:0 1px 2px 0 rgba(0,0,0,.05)}.shadow{box-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px 0 rgba(0,0,0,.06)}.shadow-lg{box-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -2px rgba(0,0,0,.05)}.shadow-xl{box-shadow:0 20px 25px -5px rgba(0,0,0,.1),0 10px 10px -5px rgba(0,0,0,.04)}.hover\:shadow-lg:hover{box-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -2px rgba(0,0,0,.05)}.focus\:shadow-outline:focus{box-shadow:0 0 0 3px rgba(118,169,250,.45)}.focus\:shadow-outline-blue:focus{box-shadow:0 0 0 3px rgba(164,202,254,.45)}.fill-current{fill:currentColor}.table-auto{table-layout:auto}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.text-white{--text-opacity:1;color:#fff;color:rgba(255,255,255,var(--text-opacity))}.text-black{--text-opacity:1;color:#000;color:rgba(0,0,0,var(--text-opacity))}.text-gray-300{--text-opacity:1;color:#d2d6dc;color:rgba(210,214,220,var(--text-opacity))}.text-gray-400{--text-opacity:1;color:#9fa6b2;color:rgba(159,166,178,var(--text-opacity))}.text-gray-500{--text-opacity:1;color:#6b7280;color:rgba(107,114,128,var(--text-opacity))}.text-gray-600{--text-opacity:1;color:#4b5563;color:rgba(75,85,99,var(--text-opacity))}.text-gray-700{--text-opacity:1;color:#374151;color:rgba(55,65,81,var(--text-opacity))}.text-gray-800{--text-opacity:1;color:#252f3f;color:rgba(37,47,63,var(--text-opacity))}.text-gray-900{--text-opacity:1;color:#161e2e;color:rgba(22,30,46,var(--text-opacity))}.text-red-400{--text-opacity:1;color:#f98080;color:rgba(249,128,128,var(--text-opacity))}.text-red-600{--text-opacity:1;color:#e02424;color:rgba(224,36,36,var(--text-opacity))}.text-green-600{--text-opacity:1;color:#057a55;color:rgba(5,122,85,var(--text-opacity))}.text-blue-600{--text-opacity:1;color:#1c64f2;color:rgba(28,100,242,var(--text-opacity))}.hover\:text-gray-300:hover{--text-opacity:1;color:#d2d6dc;color:rgba(210,214,220,var(--text-opacity))}.hover\:text-gray-500:hover{--text-opacity:1;color:#6b7280;color:rgba(107,114,128,var(--text-opacity))}.hover\:text-gray-600:hover{--text-opacity:1;color:#4b5563;color:rgba(75,85,99,var(--text-opacity))}.hover\:text-gray-700:hover{--text-opacity:1;color:#374151;color:rgba(55,65,81,var(--text-opacity))}.hover\:text-gray-800:hover{--text-opacity:1;color:#252f3f;color:rgba(37,47,63,var(--text-opacity))}.hover\:text-gray-900:hover{--text-opacity:1;color:#161e2e;color:rgba(22,30,46,var(--text-opacity))}.hover\:text-blue-600:hover{--text-opacity:1;color:#1c64f2;color:rgba(28,100,242,var(--text-opacity))}.hover\:text-indigo-900:hover{--text-opacity:1;color:#362f78;color:rgba(54,47,120,var(--text-opacity))}.focus\:text-gray-500:focus{--text-opacity:1;color:#6b7280;color:rgba(107,114,128,var(--text-opacity))}.focus\:text-gray-600:focus{--text-opacity:1;color:#4b5563;color:rgba(75,85,99,var(--text-opacity))}.focus\:text-gray-900:focus{--text-opacity:1;color:#161e2e;color:rgba(22,30,46,var(--text-opacity))}.active\:text-gray-800:active{--text-opacity:1;color:#252f3f;color:rgba(37,47,63,var(--text-opacity))}.uppercase{text-transform:uppercase}.capitalize{text-transform:capitalize}.underline{text-decoration:underline}.line-through{text-decoration:line-through}.focus\:underline:focus{text-decoration:underline}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.tracking-wide{letter-spacing:.025em}.tracking-wider{letter-spacing:.05em}.align-middle{vertical-align:middle}.align-bottom{vertical-align:bottom}.whitespace-no-wrap{white-space:nowrap}.break-words{word-wrap:break-word;overflow-wrap:break-word}.break-all{word-break:break-all}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.w-0{width:0}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-8{width:2rem}.w-12{width:3rem}.w-14{width:3.5rem}.w-48{width:12rem}.w-56{width:14rem}.w-64{width:16rem}.w-auto{width:auto}.w-1\/2{width:50%}.w-full{width:100%}.z-0{z-index:0}.z-10{z-index:10}.z-30{z-index:30}.z-40{z-index:40}.gap-4{grid-gap:1rem;gap:1rem}.gap-5{grid-gap:1.25rem;gap:1.25rem}.gap-6{grid-gap:1.5rem;gap:1.5rem}.gap-8{grid-gap:2rem;gap:2rem}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.col-span-1{grid-column:span 1/span 1}.col-span-2{grid-column:span 2/span 2}.col-span-4{grid-column:span 4/span 4}.col-span-6{grid-column:span 6/span 6}.col-span-7{grid-column:span 7/span 7}.col-span-12{grid-column:span 12/span 12}.col-start-3{grid-column-start:3}.transform{--transform-translate-x:0;--transform-translate-y:0;--transform-rotate:0;--transform-skew-x:0;--transform-skew-y:0;--transform-scale-x:1;--transform-scale-y:1;transform:translateX(var(--transform-translate-x)) translateY(var(--transform-translate-y)) rotate(var(--transform-rotate)) skewX(var(--transform-skew-x)) skewY(var(--transform-skew-y)) scaleX(var(--transform-scale-x)) scaleY(var(--transform-scale-y))}.origin-top-right{transform-origin:top right}.scale-95{--transform-scale-x:.95;--transform-scale-y:.95}.scale-100{--transform-scale-x:1;--transform-scale-y:1}.translate-x-0{--transform-translate-x:0}.-translate-x-full{--transform-translate-x:-100%}.translate-y-0{--transform-translate-y:0}.translate-y-4{--transform-translate-y:1rem}.transition-all{transition-property:all}.transition{transition-property:background-color,border-color,color,fill,stroke,opacity,box-shadow,transform}.transition-opacity{transition-property:opacity}.ease-linear{transition-timing-function:linear}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.duration-75{transition-duration:75ms}.duration-100{transition-duration:.1s}.duration-150{transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}@-webkit-keyframes spin{to{transform:rotate(1turn)}}@keyframes spin{to{transform:rotate(1turn)}}@-webkit-keyframes ping{75%,to{transform:scale(2);opacity:0}}@keyframes ping{75%,to{transform:scale(2);opacity:0}}@-webkit-keyframes pulse{50%{opacity:.5}}@keyframes pulse{50%{opacity:.5}}@-webkit-keyframes bounce{0%,to{transform:translateY(-25%);-webkit-animation-timing-function:cubic-bezier(.8,0,1,1);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:none;-webkit-animation-timing-function:cubic-bezier(0,0,.2,1);animation-timing-function:cubic-bezier(0,0,.2,1)}}@keyframes bounce{0%,to{transform:translateY(-25%);-webkit-animation-timing-function:cubic-bezier(.8,0,1,1);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:none;-webkit-animation-timing-function:cubic-bezier(0,0,.2,1);animation-timing-function:cubic-bezier(0,0,.2,1)}}.animate-spin{-webkit-animation:spin 1s linear infinite;animation:spin 1s linear infinite}@media (min-width:640px){.sm\:rounded-lg{border-radius:.5rem}.sm\:block{display:block}.sm\:inline-block{display:inline-block}.sm\:flex{display:flex}.sm\:grid{display:grid}.sm\:hidden{display:none}.sm\:flex-row-reverse{flex-direction:row-reverse}.sm\:flex-no-wrap{flex-wrap:nowrap}.sm\:items-start{align-items:flex-start}.sm\:items-center{align-items:center}.sm\:justify-center{justify-content:center}.sm\:justify-between{justify-content:space-between}.sm\:flex-shrink-0{flex-shrink:0}.sm\:h-10{height:2.5rem}.sm\:h-screen{height:100vh}.sm\:mx-0{margin-left:0;margin-right:0}.sm\:my-8{margin-top:2rem;margin-bottom:2rem}.sm\:-mx-6{margin-left:-1.5rem;margin-right:-1.5rem}.sm\:mt-0{margin-top:0}.sm\:ml-3{margin-left:.75rem}.sm\:mt-4{margin-top:1rem}.sm\:ml-4{margin-left:1rem}.sm\:mt-6{margin-top:1.5rem}.sm\:ml-6{margin-left:1.5rem}.sm\:max-w-sm{max-width:24rem}.sm\:max-w-lg{max-width:32rem}.sm\:p-0{padding:0}.sm\:p-6{padding:1.5rem}.sm\:px-0{padding-left:0;padding-right:0}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:inset-0{top:0;right:0;bottom:0;left:0}.sm\:text-left{text-align:left}.sm\:align-middle{vertical-align:middle}.sm\:w-10{width:2.5rem}.sm\:w-auto{width:auto}.sm\:w-full{width:100%}.sm\:gap-4{grid-gap:1rem;gap:1rem}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:col-span-2{grid-column:span 2/span 2}.sm\:col-span-3{grid-column:span 3/span 3}.sm\:col-span-4{grid-column:span 4/span 4}.sm\:col-span-6{grid-column:span 6/span 6}.sm\:scale-95{--transform-scale-x:.95;--transform-scale-y:.95}.sm\:scale-100{--transform-scale-x:1;--transform-scale-y:1}.sm\:translate-y-0{--transform-translate-y:0}}@media (min-width:768px){.md\:block{display:block}.md\:flex{display:flex}.md\:grid{display:grid}.md\:hidden{display:none}.md\:justify-between{justify-content:space-between}.md\:flex-shrink-0{flex-shrink:0}.md\:text-sm{font-size:.875rem}.md\:mt-0{margin-top:0}.md\:mr-2{margin-right:.5rem}.md\:ml-2{margin-left:.5rem}.md\:ml-6{margin-left:1.5rem}.md\:mt-10{margin-top:2.5rem}.md\:-mr-1{margin-right:-.25rem}.md\:px-8{padding-left:2rem;padding-right:2rem}.md\:w-1\/2{width:50%}.md\:w-1\/3{width:33.333333%}.md\:gap-6{grid-gap:1.5rem;gap:1.5rem}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.md\:col-span-1{grid-column:span 1/span 1}.md\:col-span-2{grid-column:span 2/span 2}.md\:col-span-4{grid-column:span 4/span 4}.md\:col-span-5{grid-column:span 5/span 5}.md\:col-span-6{grid-column:span 6/span 6}.md\:col-start-2{grid-column-start:2}.md\:col-start-4{grid-column-start:4}}@media (min-width:1024px){.lg\:rounded-lg{border-radius:.5rem}.lg\:block{display:block}.lg\:flex{display:flex}.lg\:grid{display:grid}.lg\:hidden{display:none}.lg\:items-center{align-items:center}.lg\:h-screen{height:100vh}.lg\:-mx-8{margin-left:-2rem;margin-right:-2rem}.lg\:ml-16{margin-left:4rem}.lg\:mt-24{margin-top:6rem}.lg\:mt-48{margin-top:12rem}.lg\:p-0{padding:0}.lg\:px-8{padding-left:2rem;padding-right:2rem}.lg\:w-1\/2{width:50%}.lg\:w-1\/4{width:25%}.lg\:w-1\/5{width:20%}.lg\:gap-4{grid-gap:1rem;gap:1rem}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.lg\:col-span-3{grid-column:span 3/span 3}.lg\:col-span-4{grid-column:span 4/span 4}.lg\:col-span-6{grid-column:span 6/span 6}.lg\:col-start-4{grid-column-start:4}.lg\:col-start-5{grid-column-start:5}} \ No newline at end of file +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}details{display:block}summary{display:list-item}[hidden],template{display:none}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}button{background-color:transparent;background-image:none}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}fieldset,ol,ul{margin:0;padding:0}ol,ul{list-style:none}html{font-family:Open Sans,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}*,:after,:before{box-sizing:border-box;border:0 solid #d2d6dc}hr{border-top-width:1px}img{border-style:solid}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{color:#a0aec0}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#a0aec0}input::placeholder,textarea::placeholder{color:#a0aec0}[role=button],button{cursor:pointer}table{border-collapse:collapse}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}button,input,optgroup,select,textarea{padding:0;line-height:inherit;color:inherit}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}.form-select{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none'%3E%3Cpath d='M7 7l3-3 3 3m0 6l-3 3-3-3' stroke='%239fa6b2' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");-webkit-appearance:none;-moz-appearance:none;appearance:none;-webkit-print-color-adjust:exact;color-adjust:exact;background-repeat:no-repeat;background-color:#fff;border-color:#d2d6dc;border-width:1px;border-radius:.375rem;padding:.5rem 2.5rem .5rem .75rem;font-size:1rem;line-height:1.5;background-position:right .5rem center;background-size:1.5em 1.5em}.form-select::-ms-expand{color:#9fa6b2;border:none}@media not print{.form-select::-ms-expand{display:none}}@media print and (-ms-high-contrast:active),print and (-ms-high-contrast:none){.form-select{padding-right:.75rem}}.form-select:focus{outline:none;box-shadow:0 0 0 3px rgba(164,202,254,.45);border-color:#a4cafe}.form-checkbox:checked{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 16 16' fill='%23fff' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M5.707 7.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4a1 1 0 00-1.414-1.414L7 8.586 5.707 7.293z'/%3E%3C/svg%3E");border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:50%;background-repeat:no-repeat}@media not print{.form-checkbox::-ms-check{border-width:1px;color:transparent;background:inherit;border-color:inherit;border-radius:inherit}}.form-checkbox{-webkit-appearance:none;-moz-appearance:none;appearance:none;-webkit-print-color-adjust:exact;color-adjust:exact;display:inline-block;vertical-align:middle;background-origin:border-box;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;flex-shrink:0;height:1rem;width:1rem;color:#3f83f8;background-color:#fff;border-color:#d2d6dc;border-width:1px;border-radius:.25rem}.form-checkbox:focus{outline:none;box-shadow:0 0 0 3px rgba(164,202,254,.45);border-color:#a4cafe}.form-checkbox:checked:focus,.form-radio:checked{border-color:transparent}.form-radio:checked{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 16 16' fill='%23fff' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='8' cy='8' r='3'/%3E%3C/svg%3E");background-color:currentColor;background-size:100% 100%;background-position:50%;background-repeat:no-repeat}@media not print{.form-radio::-ms-check{border-width:1px;color:transparent;background:inherit;border-color:inherit;border-radius:inherit}}.form-radio{-webkit-appearance:none;-moz-appearance:none;appearance:none;-webkit-print-color-adjust:exact;color-adjust:exact;display:inline-block;vertical-align:middle;background-origin:border-box;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;flex-shrink:0;border-radius:100%;height:1rem;width:1rem;color:#3f83f8;background-color:#fff;border-color:#d2d6dc;border-width:1px}.form-radio:focus{outline:none;box-shadow:0 0 0 3px rgba(164,202,254,.45);border-color:#a4cafe}.form-radio:checked:focus{border-color:transparent}.button{border-radius:.25rem;padding:.75rem 1rem;font-size:.875rem;line-height:1rem;transition-property:background-color,border-color,color,fill,stroke,opacity,box-shadow,transform;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}button:disabled{opacity:.5;cursor:not-allowed}.button-primary{--text-opacity:1;color:#fff;color:rgba(255,255,255,var(--text-opacity))}.button-primary:hover{font-weight:600}.button-block{display:block;width:100%}.button-danger{--bg-opacity:1;background-color:#f05252;background-color:rgba(240,82,82,var(--bg-opacity));--text-opacity:1;color:#fff;color:rgba(255,255,255,var(--text-opacity))}.button-danger:hover{--bg-opacity:1;background-color:#e02424;background-color:rgba(224,36,36,var(--bg-opacity))}.button-secondary{--bg-opacity:1;background-color:#f4f5f7;background-color:rgba(244,245,247,var(--bg-opacity))}.button-secondary:hover{--bg-opacity:1;background-color:#e5e7eb;background-color:rgba(229,231,235,var(--bg-opacity))}.button-link:hover{font-weight:600;text-decoration:underline}.button-link:focus{outline:2px solid transparent;outline-offset:2px;text-decoration:underline}.validation{border-left-width:2px;margin-top:.5rem;margin-bottom:.25rem;--bg-opacity:1;background-color:#f4f5f7;background-color:rgba(244,245,247,var(--bg-opacity));padding:.25rem .75rem}.validation-fail{border-color:#f05252;border-color:rgba(240,82,82,var(--border-opacity))}.validation-fail,.validation-pass{--border-opacity:1;--text-opacity:1;color:#374151;color:rgba(55,65,81,var(--text-opacity));font-size:.875rem}.validation-pass{border-color:#0e9f6e;border-color:rgba(14,159,110,var(--border-opacity))}.input{align-items:center;border-width:1px;--border-opacity:1;border-color:#d2d6dc;border-color:rgba(210,214,220,var(--border-opacity));border-radius:.25rem;margin-top:.5rem;padding:.5rem 1rem;font-size:.875rem}.input:focus{outline:2px solid transparent;outline-offset:2px;--bg-opacity:1;background-color:#f9fafb;background-color:rgba(249,250,251,var(--bg-opacity))}.input-label{font-size:.875rem;--text-opacity:1;color:#4b5563;color:rgba(75,85,99,var(--text-opacity))}.input-slim{padding-top:.5rem;padding-bottom:.5rem}.alert{padding:.75rem 1rem;font-size:.875rem;border-left-width:2px;margin-top:.5rem;margin-bottom:.25rem;--bg-opacity:1;background-color:#f4f5f7;background-color:rgba(244,245,247,var(--bg-opacity));--border-opacity:1;border-color:#9fa6b2;border-color:rgba(159,166,178,var(--border-opacity))}.alert-success{--border-opacity:1;border-color:#0e9f6e;border-color:rgba(14,159,110,var(--border-opacity))}.alert-failure{--border-opacity:1;border-color:#f05252;border-color:rgba(240,82,82,var(--border-opacity))}.badge{display:inline-flex;align-items:center;padding:.125rem .625rem;border-radius:9999px;font-size:.75rem;font-weight:500;line-height:1rem}.badge-light{background-color:#f4f5f7;background-color:rgba(244,245,247,var(--bg-opacity));color:#252f3f;color:rgba(37,47,63,var(--text-opacity))}.badge-light,.badge-primary{--bg-opacity:1;--text-opacity:1}.badge-primary{background-color:#c3ddfd;background-color:rgba(195,221,253,var(--bg-opacity));color:#3f83f8;color:rgba(63,131,248,var(--text-opacity))}.badge-danger{background-color:#fde8e8;background-color:rgba(253,232,232,var(--bg-opacity));color:#f05252;color:rgba(240,82,82,var(--text-opacity))}.badge-danger,.badge-success{--bg-opacity:1;--text-opacity:1}.badge-success{background-color:#def7ec;background-color:rgba(222,247,236,var(--bg-opacity));color:#0e9f6e;color:rgba(14,159,110,var(--text-opacity))}.badge-secondary{--bg-opacity:1;background-color:#252f3f;background-color:rgba(37,47,63,var(--bg-opacity));--text-opacity:1;color:#e5e7eb;color:rgba(229,231,235,var(--text-opacity))}.badge-warning{background-color:#feecdc;background-color:rgba(254,236,220,var(--bg-opacity));color:#ff5a1f;color:rgba(255,90,31,var(--text-opacity))}.badge-info,.badge-warning{--bg-opacity:1;--text-opacity:1}.badge-info{background-color:#e1effe;background-color:rgba(225,239,254,var(--bg-opacity));color:#3f83f8;color:rgba(63,131,248,var(--text-opacity))}@media (min-width:640px){.dataTables_length{margin-top:1.25rem!important;margin-bottom:1.25rem!important}}@media (min-width:1024px){.dataTables_length{margin-top:1rem!important;margin-bottom:1rem!important}}.dataTables_length select{--bg-opacity:1!important;background-color:#fff!important;background-color:rgba(255,255,255,var(--bg-opacity))!important;align-items:center!important;border-width:1px!important;--border-opacity:1!important;border-color:#d2d6dc!important;border-color:rgba(210,214,220,var(--border-opacity))!important;border-radius:.25rem!important;margin-top:.5rem!important;font-size:.875rem!important;margin-left:.5rem!important;margin-right:.5rem!important;padding:.5rem!important}.dataTables_filter{margin-bottom:1rem}.dataTables_filter input{-webkit-appearance:none!important;-moz-appearance:none!important;appearance:none!important;background-color:#fff!important;border-radius:.375rem!important;font-size:1rem!important;line-height:1.5!important;align-items:center!important;border-width:1px!important;--border-opacity:1!important;border-color:#d2d6dc!important;border-color:rgba(210,214,220,var(--border-opacity))!important;border-radius:.25rem!important;margin-top:.5rem!important;padding:.5rem 1rem!important;font-size:.875rem!important}@media (min-width:1024px){.dataTables_filter{margin-top:-3rem!important}}.dataTables_paginate{padding-bottom:1.5rem!important;padding-top:.5rem!important}.dataTables_paginate .paginate_button{transition-property:background-color,border-color,color,fill,stroke,opacity,box-shadow,transform!important;transition-duration:.15s!important;transition-timing-function:cubic-bezier(.4,0,.2,1)!important;--bg-opacity:1!important;background-color:#fff!important;background-color:rgba(255,255,255,var(--bg-opacity))!important;border-width:1px!important;--border-opacity:1!important;border-color:#d2d6dc!important;border-color:rgba(210,214,220,var(--border-opacity))!important;font-size:.875rem!important;line-height:1rem!important;font-weight:500!important;border-radius:.25rem!important;--text-opacity:1!important;color:#374151!important;color:rgba(55,65,81,var(--text-opacity))!important;margin-right:.25rem!important;padding:.5rem 1rem!important;cursor:pointer!important}.dataTables_paginate .current{--bg-opacity:1!important;background-color:#1c64f2!important;background-color:rgba(28,100,242,var(--bg-opacity))!important;--text-opacity:1!important;color:#fff!important;color:rgba(255,255,255,var(--text-opacity))!important}.dataTables_info{font-size:.875rem!important}.dataTables_empty{padding-top:1rem!important;padding-bottom:1rem!important}.pagination{display:flex!important;align-items:center!important}.pagination .page-link{margin-top:-1px!important;border-top-width:2px!important;border-color:transparent!important;padding-top:1rem!important;padding-left:1rem!important;padding-right:1rem!important;display:inline-flex!important;align-items:center!important;font-size:.875rem!important;line-height:1.25rem!important;font-weight:500!important;--text-opacity:1!important;color:#6b7280!important;color:rgba(107,114,128,var(--text-opacity))!important;transition-property:background-color,border-color,color,fill,stroke,opacity,box-shadow,transform!important;transition-timing-function:cubic-bezier(.4,0,.2,1)!important;transition-duration:.15s!important;cursor:pointer!important}.pagination .page-link:hover{--text-opacity:1!important;color:#374151!important;color:rgba(55,65,81,var(--text-opacity))!important;--border-opacity:1!important;border-color:#d2d6dc!important;border-color:rgba(210,214,220,var(--border-opacity))!important}.pagination .page-link:focus{outline:2px solid transparent;outline-offset:2px;--text-opacity:1;color:#374151;color:rgba(55,65,81,var(--text-opacity));--border-opacity:1;border-color:#9fa6b2;border-color:rgba(159,166,178,var(--border-opacity))}.pagination .active>span{--text-opacity:1!important;color:#1c64f2!important;color:rgba(28,100,242,var(--text-opacity))!important;--border-opacity:1!important;border-color:#1c64f2!important;border-color:rgba(28,100,242,var(--border-opacity))!important}.space-x-1>:not(template)~:not(template){--space-x-reverse:0;margin-right:calc(0.25rem*var(--space-x-reverse));margin-left:calc(0.25rem*(1 - var(--space-x-reverse)))}.space-x-2>:not(template)~:not(template){--space-x-reverse:0;margin-right:calc(0.5rem*var(--space-x-reverse));margin-left:calc(0.5rem*(1 - var(--space-x-reverse)))}.bg-white{--bg-opacity:1;background-color:#fff;background-color:rgba(255,255,255,var(--bg-opacity))}.bg-gray-50{--bg-opacity:1;background-color:#f9fafb;background-color:rgba(249,250,251,var(--bg-opacity))}.bg-gray-100{--bg-opacity:1;background-color:#f4f5f7;background-color:rgba(244,245,247,var(--bg-opacity))}.bg-gray-500{--bg-opacity:1;background-color:#6b7280;background-color:rgba(107,114,128,var(--bg-opacity))}.bg-gray-600{--bg-opacity:1;background-color:#4b5563;background-color:rgba(75,85,99,var(--bg-opacity))}.bg-red-100{--bg-opacity:1;background-color:#fde8e8;background-color:rgba(253,232,232,var(--bg-opacity))}.bg-blue-50{--bg-opacity:1;background-color:#ebf5ff;background-color:rgba(235,245,255,var(--bg-opacity))}.bg-blue-600{--bg-opacity:1;background-color:#1c64f2;background-color:rgba(28,100,242,var(--bg-opacity))}.focus\:bg-gray-100:focus,.hover\:bg-gray-100:hover{--bg-opacity:1;background-color:#f4f5f7;background-color:rgba(244,245,247,var(--bg-opacity))}.focus\:bg-gray-600:focus{--bg-opacity:1;background-color:#4b5563;background-color:rgba(75,85,99,var(--bg-opacity))}.active\:bg-gray-50:active{--bg-opacity:1;background-color:#f9fafb;background-color:rgba(249,250,251,var(--bg-opacity))}.border-collapse{border-collapse:collapse}.border-black{--border-opacity:1;border-color:#000;border-color:rgba(0,0,0,var(--border-opacity))}.border-gray-100{--border-opacity:1;border-color:#f4f5f7;border-color:rgba(244,245,247,var(--border-opacity))}.border-gray-200{--border-opacity:1;border-color:#e5e7eb;border-color:rgba(229,231,235,var(--border-opacity))}.border-gray-300{--border-opacity:1;border-color:#d2d6dc;border-color:rgba(210,214,220,var(--border-opacity))}.border-red-300{--border-opacity:1;border-color:#f8b4b4;border-color:rgba(248,180,180,var(--border-opacity))}.border-red-400{--border-opacity:1;border-color:#f98080;border-color:rgba(249,128,128,var(--border-opacity))}.border-green-500{--border-opacity:1;border-color:#0e9f6e;border-color:rgba(14,159,110,var(--border-opacity))}.border-blue-500{--border-opacity:1;border-color:#3f83f8;border-color:rgba(63,131,248,var(--border-opacity))}.group:hover .group-hover\:border-transparent{border-color:transparent}.hover\:border-gray-800:hover{--border-opacity:1;border-color:#252f3f;border-color:rgba(37,47,63,var(--border-opacity))}.hover\:border-blue-600:hover{--border-opacity:1;border-color:#1c64f2;border-color:rgba(28,100,242,var(--border-opacity))}.focus\:border-blue-300:focus{--border-opacity:1;border-color:#a4cafe;border-color:rgba(164,202,254,var(--border-opacity))}.rounded-sm{border-radius:.125rem}.rounded{border-radius:.25rem}.rounded-md{border-radius:.375rem}.rounded-lg{border-radius:.5rem}.rounded-full{border-radius:9999px}.border-dashed{border-style:dashed}.border-none{border-style:none}.border-0{border-width:0}.border-4{border-width:4px}.border{border-width:1px}.border-l-2{border-left-width:2px}.border-t{border-top-width:1px}.border-r{border-right-width:1px}.border-b{border-bottom-width:1px}.cursor-pointer{cursor:pointer}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.font-medium{font-weight:500}.font-semibold{font-weight:600}.font-bold{font-weight:700}.focus\:font-semibold:focus,.hover\:font-semibold:hover{font-weight:600}.h-0{height:0}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-8{height:2rem}.h-12{height:3rem}.h-16{height:4rem}.h-32{height:8rem}.h-auto{height:auto}.h-screen{height:100vh}.text-xs{font-size:.75rem}.text-sm{font-size:.875rem}.text-lg{font-size:1.125rem}.text-xl{font-size:1.25rem}.text-2xl{font-size:1.5rem}.text-3xl{font-size:1.875rem}.leading-4{line-height:1rem}.leading-5{line-height:1.25rem}.leading-6{line-height:1.5rem}.leading-9{line-height:2.25rem}.m-0{margin:0}.m-auto{margin:auto}.mx-2{margin-left:.5rem;margin-right:.5rem}.my-4{margin-top:1rem;margin-bottom:1rem}.mx-4{margin-left:1rem;margin-right:1rem}.my-10{margin-top:2.5rem;margin-bottom:2.5rem}.mx-auto{margin-left:auto;margin-right:auto}.-my-2{margin-top:-.5rem;margin-bottom:-.5rem}.mt-0{margin-top:0}.mb-0{margin-bottom:0}.ml-0{margin-left:0}.mt-1{margin-top:.25rem}.mr-1{margin-right:.25rem}.ml-1{margin-left:.25rem}.mt-2{margin-top:.5rem}.mr-2{margin-right:.5rem}.mb-2{margin-bottom:.5rem}.ml-2{margin-left:.5rem}.mt-3{margin-top:.75rem}.mr-3{margin-right:.75rem}.mb-3{margin-bottom:.75rem}.ml-3{margin-left:.75rem}.mt-4{margin-top:1rem}.mr-4{margin-right:1rem}.mb-4{margin-bottom:1rem}.ml-4{margin-left:1rem}.mt-5{margin-top:1.25rem}.mt-6{margin-top:1.5rem}.mb-6{margin-bottom:1.5rem}.mt-8{margin-top:2rem}.mt-10{margin-top:2.5rem}.mb-10{margin-bottom:2.5rem}.mt-16{margin-top:4rem}.-mr-1{margin-right:-.25rem}.-ml-1{margin-left:-.25rem}.-mt-4{margin-top:-1rem}.-ml-4{margin-left:-1rem}.-mr-14{margin-right:-3.5rem}.max-w-xs{max-width:20rem}.max-w-xl{max-width:36rem}.max-w-2xl{max-width:42rem}.max-w-4xl{max-width:56rem}.min-h-screen{min-height:100vh}.min-w-full{min-width:100%}.object-cover{-o-object-fit:cover;object-fit:cover}.opacity-0{opacity:0}.opacity-25{opacity:.25}.opacity-75{opacity:.75}.opacity-100{opacity:1}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overflow-y-scroll{overflow-y:scroll}.p-1{padding:.25rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-8{padding:2rem}.p-10{padding:2.5rem}.py-0{padding-top:0;padding-bottom:0}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.px-2{padding-left:.5rem;padding-right:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.px-3{padding-left:.75rem;padding-right:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.px-4{padding-left:1rem;padding-right:1rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.pl-0{padding-left:0}.pt-4{padding-top:1rem}.pr-4{padding-right:1rem}.pb-4{padding-bottom:1rem}.pt-5{padding-top:1.25rem}.pt-6{padding-top:1.5rem}.pb-20{padding-bottom:5rem}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{right:0;left:0}.inset-0,.inset-y-0{top:0;bottom:0}.inset-x-0{right:0;left:0}.top-0{top:0}.right-0{right:0}.bottom-0{bottom:0}.left-0{left:0}.shadow-xs{box-shadow:0 0 0 1px rgba(0,0,0,.05)}.shadow-sm{box-shadow:0 1px 2px 0 rgba(0,0,0,.05)}.shadow{box-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px 0 rgba(0,0,0,.06)}.shadow-lg{box-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -2px rgba(0,0,0,.05)}.shadow-xl{box-shadow:0 20px 25px -5px rgba(0,0,0,.1),0 10px 10px -5px rgba(0,0,0,.04)}.hover\:shadow-lg:hover{box-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -2px rgba(0,0,0,.05)}.focus\:shadow-outline:focus{box-shadow:0 0 0 3px rgba(118,169,250,.45)}.focus\:shadow-outline-blue:focus{box-shadow:0 0 0 3px rgba(164,202,254,.45)}.fill-current{fill:currentColor}.table-auto{table-layout:auto}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.text-white{--text-opacity:1;color:#fff;color:rgba(255,255,255,var(--text-opacity))}.text-black{--text-opacity:1;color:#000;color:rgba(0,0,0,var(--text-opacity))}.text-gray-300{--text-opacity:1;color:#d2d6dc;color:rgba(210,214,220,var(--text-opacity))}.text-gray-400{--text-opacity:1;color:#9fa6b2;color:rgba(159,166,178,var(--text-opacity))}.text-gray-500{--text-opacity:1;color:#6b7280;color:rgba(107,114,128,var(--text-opacity))}.text-gray-600{--text-opacity:1;color:#4b5563;color:rgba(75,85,99,var(--text-opacity))}.text-gray-700{--text-opacity:1;color:#374151;color:rgba(55,65,81,var(--text-opacity))}.text-gray-800{--text-opacity:1;color:#252f3f;color:rgba(37,47,63,var(--text-opacity))}.text-gray-900{--text-opacity:1;color:#161e2e;color:rgba(22,30,46,var(--text-opacity))}.text-red-400{--text-opacity:1;color:#f98080;color:rgba(249,128,128,var(--text-opacity))}.text-red-600{--text-opacity:1;color:#e02424;color:rgba(224,36,36,var(--text-opacity))}.text-green-600{--text-opacity:1;color:#057a55;color:rgba(5,122,85,var(--text-opacity))}.text-blue-600{--text-opacity:1;color:#1c64f2;color:rgba(28,100,242,var(--text-opacity))}.hover\:text-gray-300:hover{--text-opacity:1;color:#d2d6dc;color:rgba(210,214,220,var(--text-opacity))}.hover\:text-gray-500:hover{--text-opacity:1;color:#6b7280;color:rgba(107,114,128,var(--text-opacity))}.hover\:text-gray-600:hover{--text-opacity:1;color:#4b5563;color:rgba(75,85,99,var(--text-opacity))}.hover\:text-gray-700:hover{--text-opacity:1;color:#374151;color:rgba(55,65,81,var(--text-opacity))}.hover\:text-gray-800:hover{--text-opacity:1;color:#252f3f;color:rgba(37,47,63,var(--text-opacity))}.hover\:text-gray-900:hover{--text-opacity:1;color:#161e2e;color:rgba(22,30,46,var(--text-opacity))}.hover\:text-blue-600:hover{--text-opacity:1;color:#1c64f2;color:rgba(28,100,242,var(--text-opacity))}.hover\:text-indigo-900:hover{--text-opacity:1;color:#362f78;color:rgba(54,47,120,var(--text-opacity))}.focus\:text-gray-500:focus{--text-opacity:1;color:#6b7280;color:rgba(107,114,128,var(--text-opacity))}.focus\:text-gray-600:focus{--text-opacity:1;color:#4b5563;color:rgba(75,85,99,var(--text-opacity))}.focus\:text-gray-900:focus{--text-opacity:1;color:#161e2e;color:rgba(22,30,46,var(--text-opacity))}.active\:text-gray-800:active{--text-opacity:1;color:#252f3f;color:rgba(37,47,63,var(--text-opacity))}.uppercase{text-transform:uppercase}.capitalize{text-transform:capitalize}.underline{text-decoration:underline}.line-through{text-decoration:line-through}.focus\:underline:focus,.hover\:underline:hover{text-decoration:underline}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.tracking-wide{letter-spacing:.025em}.tracking-wider{letter-spacing:.05em}.align-middle{vertical-align:middle}.align-bottom{vertical-align:bottom}.whitespace-no-wrap{white-space:nowrap}.break-words{word-wrap:break-word;overflow-wrap:break-word}.break-all{word-break:break-all}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.w-0{width:0}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-8{width:2rem}.w-12{width:3rem}.w-14{width:3.5rem}.w-48{width:12rem}.w-56{width:14rem}.w-64{width:16rem}.w-auto{width:auto}.w-1\/2{width:50%}.w-full{width:100%}.z-0{z-index:0}.z-10{z-index:10}.z-30{z-index:30}.z-40{z-index:40}.gap-4{grid-gap:1rem;gap:1rem}.gap-5{grid-gap:1.25rem;gap:1.25rem}.gap-6{grid-gap:1.5rem;gap:1.5rem}.gap-8{grid-gap:2rem;gap:2rem}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.col-span-1{grid-column:span 1/span 1}.col-span-2{grid-column:span 2/span 2}.col-span-4{grid-column:span 4/span 4}.col-span-6{grid-column:span 6/span 6}.col-span-7{grid-column:span 7/span 7}.col-span-12{grid-column:span 12/span 12}.col-start-3{grid-column-start:3}.transform{--transform-translate-x:0;--transform-translate-y:0;--transform-rotate:0;--transform-skew-x:0;--transform-skew-y:0;--transform-scale-x:1;--transform-scale-y:1;transform:translateX(var(--transform-translate-x)) translateY(var(--transform-translate-y)) rotate(var(--transform-rotate)) skewX(var(--transform-skew-x)) skewY(var(--transform-skew-y)) scaleX(var(--transform-scale-x)) scaleY(var(--transform-scale-y))}.origin-top-right{transform-origin:top right}.scale-95{--transform-scale-x:.95;--transform-scale-y:.95}.scale-100{--transform-scale-x:1;--transform-scale-y:1}.translate-x-0{--transform-translate-x:0}.-translate-x-full{--transform-translate-x:-100%}.translate-y-0{--transform-translate-y:0}.translate-y-4{--transform-translate-y:1rem}.transition-all{transition-property:all}.transition{transition-property:background-color,border-color,color,fill,stroke,opacity,box-shadow,transform}.transition-opacity{transition-property:opacity}.ease-linear{transition-timing-function:linear}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.duration-75{transition-duration:75ms}.duration-100{transition-duration:.1s}.duration-150{transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}@-webkit-keyframes spin{to{transform:rotate(1turn)}}@keyframes spin{to{transform:rotate(1turn)}}@-webkit-keyframes ping{75%,to{transform:scale(2);opacity:0}}@keyframes ping{75%,to{transform:scale(2);opacity:0}}@-webkit-keyframes pulse{50%{opacity:.5}}@keyframes pulse{50%{opacity:.5}}@-webkit-keyframes bounce{0%,to{transform:translateY(-25%);-webkit-animation-timing-function:cubic-bezier(.8,0,1,1);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:none;-webkit-animation-timing-function:cubic-bezier(0,0,.2,1);animation-timing-function:cubic-bezier(0,0,.2,1)}}@keyframes bounce{0%,to{transform:translateY(-25%);-webkit-animation-timing-function:cubic-bezier(.8,0,1,1);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:none;-webkit-animation-timing-function:cubic-bezier(0,0,.2,1);animation-timing-function:cubic-bezier(0,0,.2,1)}}.animate-spin{-webkit-animation:spin 1s linear infinite;animation:spin 1s linear infinite}@media (min-width:640px){.sm\:rounded-lg{border-radius:.5rem}.sm\:block{display:block}.sm\:inline-block{display:inline-block}.sm\:flex{display:flex}.sm\:grid{display:grid}.sm\:hidden{display:none}.sm\:flex-row-reverse{flex-direction:row-reverse}.sm\:flex-no-wrap{flex-wrap:nowrap}.sm\:items-start{align-items:flex-start}.sm\:items-center{align-items:center}.sm\:justify-center{justify-content:center}.sm\:justify-between{justify-content:space-between}.sm\:flex-shrink-0{flex-shrink:0}.sm\:h-10{height:2.5rem}.sm\:h-screen{height:100vh}.sm\:mx-0{margin-left:0;margin-right:0}.sm\:my-8{margin-top:2rem;margin-bottom:2rem}.sm\:-mx-6{margin-left:-1.5rem;margin-right:-1.5rem}.sm\:mt-0{margin-top:0}.sm\:ml-3{margin-left:.75rem}.sm\:mt-4{margin-top:1rem}.sm\:ml-4{margin-left:1rem}.sm\:mt-6{margin-top:1.5rem}.sm\:ml-6{margin-left:1.5rem}.sm\:max-w-sm{max-width:24rem}.sm\:max-w-lg{max-width:32rem}.sm\:p-0{padding:0}.sm\:p-6{padding:1.5rem}.sm\:px-0{padding-left:0;padding-right:0}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:inset-0{top:0;right:0;bottom:0;left:0}.sm\:text-left{text-align:left}.sm\:align-middle{vertical-align:middle}.sm\:w-10{width:2.5rem}.sm\:w-auto{width:auto}.sm\:w-full{width:100%}.sm\:gap-4{grid-gap:1rem;gap:1rem}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:col-span-2{grid-column:span 2/span 2}.sm\:col-span-3{grid-column:span 3/span 3}.sm\:col-span-4{grid-column:span 4/span 4}.sm\:col-span-6{grid-column:span 6/span 6}.sm\:scale-95{--transform-scale-x:.95;--transform-scale-y:.95}.sm\:scale-100{--transform-scale-x:1;--transform-scale-y:1}.sm\:translate-y-0{--transform-translate-y:0}}@media (min-width:768px){.md\:block{display:block}.md\:flex{display:flex}.md\:grid{display:grid}.md\:hidden{display:none}.md\:justify-between{justify-content:space-between}.md\:flex-shrink-0{flex-shrink:0}.md\:text-sm{font-size:.875rem}.md\:mt-0{margin-top:0}.md\:mr-2{margin-right:.5rem}.md\:ml-2{margin-left:.5rem}.md\:ml-6{margin-left:1.5rem}.md\:mt-10{margin-top:2.5rem}.md\:-mr-1{margin-right:-.25rem}.md\:px-8{padding-left:2rem;padding-right:2rem}.md\:w-1\/2{width:50%}.md\:w-1\/3{width:33.333333%}.md\:gap-6{grid-gap:1.5rem;gap:1.5rem}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.md\:col-span-1{grid-column:span 1/span 1}.md\:col-span-2{grid-column:span 2/span 2}.md\:col-span-4{grid-column:span 4/span 4}.md\:col-span-5{grid-column:span 5/span 5}.md\:col-span-6{grid-column:span 6/span 6}.md\:col-start-2{grid-column-start:2}.md\:col-start-4{grid-column-start:4}}@media (min-width:1024px){.lg\:rounded-lg{border-radius:.5rem}.lg\:block{display:block}.lg\:flex{display:flex}.lg\:grid{display:grid}.lg\:hidden{display:none}.lg\:items-center{align-items:center}.lg\:h-screen{height:100vh}.lg\:-mx-8{margin-left:-2rem;margin-right:-2rem}.lg\:ml-16{margin-left:4rem}.lg\:mt-24{margin-top:6rem}.lg\:mt-48{margin-top:12rem}.lg\:p-0{padding:0}.lg\:px-8{padding-left:2rem;padding-right:2rem}.lg\:w-1\/2{width:50%}.lg\:w-1\/4{width:25%}.lg\:w-1\/5{width:20%}.lg\:gap-4{grid-gap:1rem;gap:1rem}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.lg\:col-span-3{grid-column:span 3/span 3}.lg\:col-span-4{grid-column:span 4/span 4}.lg\:col-span-6{grid-column:span 6/span 6}.lg\:col-start-4{grid-column-start:4}.lg\:col-start-5{grid-column-start:5}} \ No newline at end of file diff --git a/public/mix-manifest.json b/public/mix-manifest.json index 58147fee3905..20cd5528984a 100755 --- a/public/mix-manifest.json +++ b/public/mix-manifest.json @@ -1,6 +1,6 @@ { "/js/app.js": "/js/app.js?id=696e8203d5e8e7cf5ff5", - "/css/app.css": "/css/app.css?id=2163e6d43930f4ad9253", + "/css/app.css": "/css/app.css?id=73da599a7719bcc433d2", "/js/clients/invoices/action-selectors.js": "/js/clients/invoices/action-selectors.js?id=a09bb529b8e1826f13b4", "/js/clients/invoices/payment.js": "/js/clients/invoices/payment.js?id=8ce8955ba775ea5f47d1", "/js/clients/linkify-urls.js": "/js/clients/linkify-urls.js?id=0dc8c34010d09195d2f7", diff --git a/resources/views/portal/ninja2020/gateways/wepay/signup/index.blade.php b/resources/views/portal/ninja2020/gateways/wepay/signup/index.blade.php index d3149dc1b4f1..e2aaaf5d7d04 100644 --- a/resources/views/portal/ninja2020/gateways/wepay/signup/index.blade.php +++ b/resources/views/portal/ninja2020/gateways/wepay/signup/index.blade.php @@ -1,19 +1,15 @@ -@extends('portal.ninja2020.layout.clean') +@extends('portal.ninja2020.layout.clean', ['custom_body_class' => 'bg-gray-50']) @section('meta_title', ctrans('texts.sign_up_with_wepay')) @section('body') -
-
We Pay
- @livewire('wepay-signup', ['user_id' => $user_id, 'company_key' => $company_key]) - -
+ @livewire('wepay-signup', ['user_id' => $user_id, 'company_key' => $company_key]) @endsection @push('footer') -@endpush \ No newline at end of file +@endpush diff --git a/resources/views/portal/ninja2020/gateways/wepay/signup/wepay-signup.blade.php b/resources/views/portal/ninja2020/gateways/wepay/signup/wepay-signup.blade.php index 74f37735eee4..93f13f435579 100644 --- a/resources/views/portal/ninja2020/gateways/wepay/signup/wepay-signup.blade.php +++ b/resources/views/portal/ninja2020/gateways/wepay/signup/wepay-signup.blade.php @@ -1,14 +1,14 @@ -
+
-
+ @csrf @method('POST')
-
+
- + @error('first_name')
{{ $message }} @@ -18,7 +18,7 @@
- + @error('last_name')
{{ $message }} @@ -28,7 +28,8 @@
- + @error('email')
{{ $message }} @@ -38,7 +39,8 @@
- + @error('company_name')
{{ $message }} @@ -46,39 +48,54 @@ @enderror
-
- - -
- - {{ ctrans('texts.country_United States') }} +
+ + +
+ + {{ ctrans('texts.country_United States') }}
-
- - {{ ctrans('texts.country_Canada') }} +
+ + {{ ctrans('texts.country_Canada') }}
-
- - {{ ctrans('texts.country_United Kingdom') }} +
+ + {{ ctrans('texts.country_United Kingdom') }}
+ @if($country == 'CA') +
+ + +
+ + {{ ctrans('texts.accept_debit_cards') }} +
+
+ @endif +
- - {{ ctrans('texts.enable_ach')}} + + {{ ctrans('texts.enable_ach')}}
- - {!! ctrans('texts.wepay_payment_tos_agree', ['terms' => $terms, 'privacy_policy' => $privacy_policy]) !!} + + {!! ctrans('texts.wepay_payment_tos_agree', ['terms' => $terms, 'privacy_policy' => $privacy_policy]) !!}
@error('wepay_payment_tos_agree')
@@ -87,18 +104,179 @@ @enderror
-
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Fees Disclosure Box +
+

Payment Card Type

+ (These are the most common domestically issued card types + and processing methods. They do not represent all the + possible fees and variations that are charged to the + merchants.) +
+

Processing Method: Card Not Present

+ (Means that the card/device was not + electronically read. Generally, the card + information is manually key-entered, e.g. online + payment) +
+ Visa Consumer Credit + + 2.9% + CA$0.30 +
+ Visa Infinite + + 2.9% + CA$0.30 +
+ Visa Infinite Privilege + + 2.9% + CA$0.30 +
+ Visa Business + + 2.9% + CA$0.30 +
+ Visa Business Premium + + 2.9% + CA$0.30 +
+ Visa Corporate + + 2.9% + CA$0.30 +
+ Visa Prepaid + + 2.9% + CA$0.30 +
+ Visa Debit + + 2.9% + CA$0.30 +
+ MasterCard Consumer Credit + + 2.9% + CA$0.30 +
+ MasterCard World + + 2.9% + CA$0.30 +
+ MasterCard World Elite + + 2.9% + CA$0.30 +
+ MasterCard Business/Corporate + + 2.9% + CA$0.30 +
+ MasterCard Debit + + 2.9% + CA$0.30 +
+ MasterCard Prepaid + + 2.9% + CA$0.30 +
+ American Express + + 2.9% + CA$0.30 +
+ Other Fees Disclosure Box - {{ ctrans('texts.standard_fees_apply')}} - +
+ Chargeback + + CA$15.00 +
+
+ {{ ctrans('texts.standard_fees_apply')}} +
+
-
\ No newline at end of file +
From 9bc287b9cc5f40ce8d361e107439b80a0d6955df Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 7 May 2021 08:42:41 +1000 Subject: [PATCH 11/43] Fixes for lightlogs --- app/DataMapper/Analytics/AccountCreated.php | 4 +++- app/DataMapper/Analytics/AccountDeleted.php | 4 +++- app/DataMapper/Analytics/DbQuery.php | 4 +++- app/DataMapper/Analytics/EmailFailure.php | 4 +++- app/DataMapper/Analytics/EmailInvoiceFailure.php | 4 +++- app/DataMapper/Analytics/EmailSuccess.php | 4 +++- app/DataMapper/Analytics/LoginFailure.php | 4 +++- app/DataMapper/Analytics/LoginSuccess.php | 4 +++- app/DataMapper/Analytics/Mail/EmailBounce.php | 4 +++- app/DataMapper/Analytics/Mail/EmailSpam.php | 4 +++- app/DataMapper/Analytics/MigrationFailure.php | 4 +++- app/DataMapper/Analytics/SendRecurringFailure.php | 4 +++- 12 files changed, 36 insertions(+), 12 deletions(-) diff --git a/app/DataMapper/Analytics/AccountCreated.php b/app/DataMapper/Analytics/AccountCreated.php index b0df3fa6ddd0..5d82945eb8f9 100644 --- a/app/DataMapper/Analytics/AccountCreated.php +++ b/app/DataMapper/Analytics/AccountCreated.php @@ -11,7 +11,9 @@ namespace App\DataMapper\Analytics; -class AccountCreated +use Turbo124\Beacon\ExampleMetric\GenericCounter; + +class AccountCreated extends GenericCounter { /** * The type of Sample. diff --git a/app/DataMapper/Analytics/AccountDeleted.php b/app/DataMapper/Analytics/AccountDeleted.php index 1ea0cc27aa34..342796254f50 100644 --- a/app/DataMapper/Analytics/AccountDeleted.php +++ b/app/DataMapper/Analytics/AccountDeleted.php @@ -11,7 +11,9 @@ namespace App\DataMapper\Analytics; -class AccountDeleted +use Turbo124\Beacon\ExampleMetric\GenericCounter; + +class AccountDeleted extends GenericCounter { /** * The type of Sample. diff --git a/app/DataMapper/Analytics/DbQuery.php b/app/DataMapper/Analytics/DbQuery.php index e802c64c4f2c..1abd42ec9df8 100644 --- a/app/DataMapper/Analytics/DbQuery.php +++ b/app/DataMapper/Analytics/DbQuery.php @@ -11,7 +11,9 @@ namespace App\DataMapper\Analytics; -class DbQuery +use Turbo124\Beacon\ExampleMetric\GenericMixedMetric; + +class DbQuery extends GenericMixedMetric { /** * The type of Sample. diff --git a/app/DataMapper/Analytics/EmailFailure.php b/app/DataMapper/Analytics/EmailFailure.php index d91e37725b37..473b7c9bd0c9 100644 --- a/app/DataMapper/Analytics/EmailFailure.php +++ b/app/DataMapper/Analytics/EmailFailure.php @@ -11,7 +11,9 @@ namespace App\DataMapper\Analytics; -class EmailFailure +use Turbo124\Beacon\ExampleMetric\GenericMixedMetric; + +class EmailFailure extends GenericMixedMetric { /** * The type of Sample. diff --git a/app/DataMapper/Analytics/EmailInvoiceFailure.php b/app/DataMapper/Analytics/EmailInvoiceFailure.php index 8637f87a05f5..9d259f2d38ce 100644 --- a/app/DataMapper/Analytics/EmailInvoiceFailure.php +++ b/app/DataMapper/Analytics/EmailInvoiceFailure.php @@ -11,7 +11,9 @@ namespace App\DataMapper\Analytics; -class EmailInvoiceFailure +use Turbo124\Beacon\ExampleMetric\GenericMixedMetric; + +class EmailInvoiceFailure extends GenericMixedMetric { /** * The type of Sample. diff --git a/app/DataMapper/Analytics/EmailSuccess.php b/app/DataMapper/Analytics/EmailSuccess.php index bd39f7cd695b..14dfbb6ca89b 100644 --- a/app/DataMapper/Analytics/EmailSuccess.php +++ b/app/DataMapper/Analytics/EmailSuccess.php @@ -11,7 +11,9 @@ namespace App\DataMapper\Analytics; -class EmailSuccess +use Turbo124\Beacon\ExampleMetric\GenericMixedMetric; + +class EmailSuccess extends GenericMixedMetric { /** diff --git a/app/DataMapper/Analytics/LoginFailure.php b/app/DataMapper/Analytics/LoginFailure.php index 3fd8f943bb0e..584a5894aaec 100644 --- a/app/DataMapper/Analytics/LoginFailure.php +++ b/app/DataMapper/Analytics/LoginFailure.php @@ -11,7 +11,9 @@ namespace App\DataMapper\Analytics; -class LoginFailure +use Turbo124\Beacon\ExampleMetric\GenericCounter; + +class LoginFailure extends GenericCounter { /** * The type of Sample. diff --git a/app/DataMapper/Analytics/LoginSuccess.php b/app/DataMapper/Analytics/LoginSuccess.php index 5d489e93911f..2da5daece414 100644 --- a/app/DataMapper/Analytics/LoginSuccess.php +++ b/app/DataMapper/Analytics/LoginSuccess.php @@ -11,7 +11,9 @@ namespace App\DataMapper\Analytics; -class LoginSuccess +use Turbo124\Beacon\ExampleMetric\GenericCounter; + +class LoginSuccess extends GenericCounter { /** * The type of Sample. diff --git a/app/DataMapper/Analytics/Mail/EmailBounce.php b/app/DataMapper/Analytics/Mail/EmailBounce.php index 5a8aaf10af3d..15340b5a6ff5 100644 --- a/app/DataMapper/Analytics/Mail/EmailBounce.php +++ b/app/DataMapper/Analytics/Mail/EmailBounce.php @@ -11,7 +11,9 @@ namespace App\DataMapper\Analytics\Mail; -class EmailBounce +use Turbo124\Beacon\ExampleMetric\GenericMixedMetric; + +class EmailBounce extends GenericMixedMetric { /** * The type of Sample. diff --git a/app/DataMapper/Analytics/Mail/EmailSpam.php b/app/DataMapper/Analytics/Mail/EmailSpam.php index c420327ddc78..ff7acbd750c2 100644 --- a/app/DataMapper/Analytics/Mail/EmailSpam.php +++ b/app/DataMapper/Analytics/Mail/EmailSpam.php @@ -11,7 +11,9 @@ namespace App\DataMapper\Analytics\Mail; -class EmailSpam +use Turbo124\Beacon\ExampleMetric\GenericMixedMetric; + +class EmailSpam extends GenericMixedMetric { /** * The type of Sample. diff --git a/app/DataMapper/Analytics/MigrationFailure.php b/app/DataMapper/Analytics/MigrationFailure.php index c57421d1f52f..b4cf68fe3588 100644 --- a/app/DataMapper/Analytics/MigrationFailure.php +++ b/app/DataMapper/Analytics/MigrationFailure.php @@ -11,7 +11,9 @@ namespace App\DataMapper\Analytics; -class MigrationFailure +use Turbo124\Beacon\ExampleMetric\GenericMixedMetric; + +class MigrationFailure extends GenericMixedMetric { /** * The type of Sample. diff --git a/app/DataMapper/Analytics/SendRecurringFailure.php b/app/DataMapper/Analytics/SendRecurringFailure.php index 26affa205a3c..7b000464c88b 100644 --- a/app/DataMapper/Analytics/SendRecurringFailure.php +++ b/app/DataMapper/Analytics/SendRecurringFailure.php @@ -11,7 +11,9 @@ namespace App\DataMapper\Analytics; -class SendRecurringFailure +use Turbo124\Beacon\ExampleMetric\GenericMixedMetric; + +class SendRecurringFailure extends GenericMixedMetric { /** * The type of Sample. From 3c9b096f01c07f8dc61f9e946087db2f12bf51b4 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 7 May 2021 15:39:45 +1000 Subject: [PATCH 12/43] WePay --- app/Http/Livewire/WepaySignup.php | 16 ++++++++++++++-- app/PaymentDrivers/WePayPaymentDriver.php | 2 +- .../gateways/wepay/signup/wepay-signup.blade.php | 15 ++++++++------- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/app/Http/Livewire/WepaySignup.php b/app/Http/Livewire/WepaySignup.php index 783bff6f33da..ba7c915e0ce7 100644 --- a/app/Http/Livewire/WepaySignup.php +++ b/app/Http/Livewire/WepaySignup.php @@ -12,6 +12,7 @@ namespace App\Http\Livewire; +use App\Factory\CompanyGatewayFactory; use App\Models\Company; use App\Models\CompanyGateway; use App\Models\User; @@ -48,6 +49,7 @@ class WepaySignup extends Component 'country' => ['required'], 'ach' => ['sometimes'], 'wepay_payment_tos_agree' => ['accepted'], + 'debit_cards' => ['sometimes'], ]; public function mount() @@ -83,14 +85,24 @@ class WepaySignup extends Component ->firstOrNew(); if(!$cg->id) { - + $cg = CompanyGatewayFactory::create($this->company->id, $this->user->id); + $cg->gateway_key = '8fdeed552015b3c7b44ed6c8ebd9e992'; + $cg->require_cvv = false; + $cg->require_billing_address = false; + $cg->require_shipping_address = false; + $cg->update_details = false; + $cg->config = encrypt(config('ninja.testvars.checkout')); + $cg->save(); } $data = $this->validate($this->rules); + +// nlog($data); + $this->saved = ctrans('texts.processing'); - $wepay_driver = new WePayPaymentDriver(new CompanyGateway, null, null); + $wepay_driver = new WePayPaymentDriver($cg, null, null); $wepay_driver->init(); diff --git a/app/PaymentDrivers/WePayPaymentDriver.php b/app/PaymentDrivers/WePayPaymentDriver.php index d6f281893dfe..414d519b591f 100644 --- a/app/PaymentDrivers/WePayPaymentDriver.php +++ b/app/PaymentDrivers/WePayPaymentDriver.php @@ -48,7 +48,7 @@ class WePayPaymentDriver extends BaseDriver if (WePay::getEnvironment() == 'none') { if(config('ninja.wepay.environment') == 'staging') - WePay::useStaing(config('ninja.wepay.client_id'), config('ninja.wepay.client_secret')); + WePay::useStaging(config('ninja.wepay.client_id'), config('ninja.wepay.client_secret')); else WePay::useProduction(config('ninja.wepay.client_id'), config('ninja.wepay.client_secret')); diff --git a/resources/views/portal/ninja2020/gateways/wepay/signup/wepay-signup.blade.php b/resources/views/portal/ninja2020/gateways/wepay/signup/wepay-signup.blade.php index 93f13f435579..b87f25d3ad8c 100644 --- a/resources/views/portal/ninja2020/gateways/wepay/signup/wepay-signup.blade.php +++ b/resources/views/portal/ninja2020/gateways/wepay/signup/wepay-signup.blade.php @@ -76,7 +76,7 @@
- + {{ ctrans('texts.accept_debit_cards') }}
@@ -85,7 +85,7 @@
- + {{ ctrans('texts.enable_ach')}}
@@ -93,8 +93,7 @@
- + {!! ctrans('texts.wepay_payment_tos_agree', ['terms' => $terms, 'privacy_policy' => $privacy_policy]) !!}
@error('wepay_payment_tos_agree') @@ -104,6 +103,10 @@ @enderror
+
+ {{ ctrans('texts.standard_fees_apply')}} +
+
@@ -267,9 +270,7 @@
-
- {{ ctrans('texts.standard_fees_apply')}} -
+
From bdda63a2147a82b619ea7e8e1eb3a609ac1d7497 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 7 May 2021 17:07:49 +1000 Subject: [PATCH 13/43] We Pay --- app/Http/Livewire/WepaySignup.php | 6 +++--- app/Models/Company.php | 23 +++++++++++++++++++---- app/PaymentDrivers/WePayPaymentDriver.php | 2 +- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/app/Http/Livewire/WepaySignup.php b/app/Http/Livewire/WepaySignup.php index ba7c915e0ce7..94925e2d6c3b 100644 --- a/app/Http/Livewire/WepaySignup.php +++ b/app/Http/Livewire/WepaySignup.php @@ -104,7 +104,7 @@ class WepaySignup extends Component $wepay_driver = new WePayPaymentDriver($cg, null, null); - $wepay_driver->init(); + $wepay = $wepay_driver->init()->wepay; $user_details = [ 'client_id' => config('ninja.wepay.client_id'), @@ -119,7 +119,7 @@ class WepaySignup extends Component 'scope' => 'manage_accounts,collect_payments,view_user,preapprove_payments,send_money', ]; - $wepay_user = $wepay_driver->request('user/register/', $user_details); + $wepay_user = $wepay->request('user/register/', $user_details); $access_token = $wepay_user->access_token; @@ -132,7 +132,7 @@ class WepaySignup extends Component 'description' => ctrans('texts.wepay_account_description'), 'theme_object' => json_decode('{"name":"Invoice Ninja","primary_color":"0b4d78","secondary_color":"0b4d78","background_color":"f8f8f8","button_color":"33b753"}'), 'callback_uri' => route('payment_webhook', ['company_key' => $this->company->company_key, 'company_gateway_id' => $cg->hashed_id]), - 'rbits' => $this->company->present()->rBits, + 'rbits' => $this->company->rBits(), 'country' => $data['country'], ]; diff --git a/app/Models/Company.php b/app/Models/Company.php index a750ad81c89f..f35d33d649bb 100644 --- a/app/Models/Company.php +++ b/app/Models/Company.php @@ -445,21 +445,36 @@ class Company extends BaseModel public function rBits() { - $account = $this->account; $user = $this->owner(); $data = []; $data[] = $this->createRBit('business_name', 'user', ['business_name' => $this->present()->name()]); - $data[] = $this->createRBit('industry_code', 'user', ['industry_detail' => $this->industry->name]); + $data[] = $this->createRBit('industry_code', 'user', ['industry_detail' => $this->industry ? $this->industry->name : '']); $data[] = $this->createRBit('comment', 'partner_database', ['comment_text' => 'Logo image not present']); - $data[] = $this->createRBit('business_description', 'user', ['business_description' => $company->present()->size()]); + $data[] = $this->createRBit('business_description', 'user', ['business_description' => $this->present()->size()]); $data[] = $this->createRBit('person', 'user', ['name' => $user->present()->getFullName()]); $data[] = $this->createRBit('email', 'user', ['email' => $user->email]); $data[] = $this->createRBit('phone', 'user', ['phone' => $user->phone]); - $data[] = $this->createRBit('website_uri', 'user', ['uri' => $this->entity->settings->website]); + $data[] = $this->createRBit('website_uri', 'user', ['uri' => $this->settings->website]); $data[] = $this->createRBit('external_account', 'partner_database', ['is_partner_account' => 'yes', 'account_type' => 'Invoice Ninja', 'create_time' => time()]); return $data; } + + + private function createRBit($type, $source, $properties) + { + $data = new \stdClass; + $data->receive_time = time(); + $data->type = $type; + $data->source = $source; + $data->properties = new \stdClass; + + foreach ($properties as $key => $val) { + $data->properties->$key = $val; + } + + return $data; + } } diff --git a/app/PaymentDrivers/WePayPaymentDriver.php b/app/PaymentDrivers/WePayPaymentDriver.php index 414d519b591f..2b21cc6880af 100644 --- a/app/PaymentDrivers/WePayPaymentDriver.php +++ b/app/PaymentDrivers/WePayPaymentDriver.php @@ -55,7 +55,7 @@ class WePayPaymentDriver extends BaseDriver } if ($this->company_gateway) - $this->wepay = new WePay($this->company_gateway->getConfig()->accessToken); + $this->wepay = new WePay($this->company_gateway->getConfigField('accessToken')); $this->wepay = new WePay(null); From 31db5b44c11f64446bb24b1e87c4d1a2e1a1b615 Mon Sep 17 00:00:00 2001 From: = Date: Fri, 7 May 2021 18:03:37 +1000 Subject: [PATCH 14/43] WePay --- app/Http/Livewire/WepaySignup.php | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/app/Http/Livewire/WepaySignup.php b/app/Http/Livewire/WepaySignup.php index 94925e2d6c3b..58dc7c58190f 100644 --- a/app/Http/Livewire/WepaySignup.php +++ b/app/Http/Livewire/WepaySignup.php @@ -80,7 +80,7 @@ class WepaySignup extends Component public function submit() { //need to create or get a new WePay CompanyGateway - $cg = CompanyGateway::where('id', 49) + $cg = CompanyGateway::where('gateway_key', '8fdeed552015b3c7b44ed6c8ebd9e992') ->where('company_id', $this->company->id) ->firstOrNew(); @@ -97,9 +97,6 @@ class WepaySignup extends Component $data = $this->validate($this->rules); - -// nlog($data); - $this->saved = ctrans('texts.processing'); $wepay_driver = new WePayPaymentDriver($cg, null, null); @@ -156,5 +153,19 @@ class WepaySignup extends Component } } + $config = [ + 'userId' => $wepay_user->user_id, + 'accessToken' => $access_token, + 'tokenType' => $wepay_user->token_type, + 'tokenExpires' => $access_token_expires, + 'accountId' => $wepay_account->account_id, + 'state' => $wepay_account->state, + 'testMode' => config('ninja.wepay.environment') == 'staging', + 'country' => $data['country'], + ]; + + $cg->setConfig($config); + $cg->save(); } + } From 2a90476f88b2c4b7b0583aa68103a1d6e78a3b86 Mon Sep 17 00:00:00 2001 From: = Date: Fri, 7 May 2021 20:53:59 +1000 Subject: [PATCH 15/43] Fixes for cache --- app/Utils/Statics.php | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/app/Utils/Statics.php b/app/Utils/Statics.php index 9d5da011f5ae..1543c8e08f7c 100644 --- a/app/Utils/Statics.php +++ b/app/Utils/Statics.php @@ -12,6 +12,7 @@ namespace App\Utils; use Illuminate\Support\Facades\Cache; +use Illuminate\Support\Facades\Schema; use Illuminate\Support\Str; /** @@ -69,6 +70,28 @@ class Statics $data = []; foreach (config('ninja.cached_tables') as $name => $class) { + + if (!Cache::has($name)) { + + // check that the table exists in case the migration is pending + if (!Schema::hasTable((new $class())->getTable())) { + continue; + } + if ($name == 'payment_terms') { + $orderBy = 'num_days'; + } elseif ($name == 'fonts') { + $orderBy = 'sort_order'; + } elseif (in_array($name, ['currencies', 'industries', 'languages', 'countries', 'banks'])) { + $orderBy = 'name'; + } else { + $orderBy = 'id'; + } + $tableData = $class::orderBy($orderBy)->get(); + if ($tableData->count()) { + Cache::forever($name, $tableData); + } + } + $data[$name] = Cache::get($name); } From 76dfb475addc8e120a665ab8c17d1750f87691a5 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 8 May 2021 12:07:42 +1000 Subject: [PATCH 16/43] We Pay --- composer.lock | 84 +++++++++---------- .../wepay/signup/wepay-signup.blade.php | 2 + 2 files changed, 44 insertions(+), 42 deletions(-) diff --git a/composer.lock b/composer.lock index ada9fd8b8449..6171f7333c35 100644 --- a/composer.lock +++ b/composer.lock @@ -103,16 +103,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.180.1", + "version": "3.180.5", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "7801112fd8be227954a6ecfbfd85b01ee4a7cae4" + "reference": "948a4defbe2a571cc4460725015b8e98b7060f2d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/7801112fd8be227954a6ecfbfd85b01ee4a7cae4", - "reference": "7801112fd8be227954a6ecfbfd85b01ee4a7cae4", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/948a4defbe2a571cc4460725015b8e98b7060f2d", + "reference": "948a4defbe2a571cc4460725015b8e98b7060f2d", "shasum": "" }, "require": { @@ -187,9 +187,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.180.1" + "source": "https://github.com/aws/aws-sdk-php/tree/3.180.5" }, - "time": "2021-05-04T18:14:38+00:00" + "time": "2021-05-07T18:12:43+00:00" }, { "name": "bacon/bacon-qr-code", @@ -6811,16 +6811,16 @@ }, { "name": "sentry/sentry", - "version": "3.2.1", + "version": "3.2.2", "source": { "type": "git", "url": "https://github.com/getsentry/sentry-php.git", - "reference": "fb4f83e6e2d718d1e5fbfe3a20cced83f47f040f" + "reference": "02237728bdc5b82b0a14c37417644e3f3606db9b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/getsentry/sentry-php/zipball/fb4f83e6e2d718d1e5fbfe3a20cced83f47f040f", - "reference": "fb4f83e6e2d718d1e5fbfe3a20cced83f47f040f", + "url": "https://api.github.com/repos/getsentry/sentry-php/zipball/02237728bdc5b82b0a14c37417644e3f3606db9b", + "reference": "02237728bdc5b82b0a14c37417644e3f3606db9b", "shasum": "" }, "require": { @@ -6899,7 +6899,7 @@ ], "support": { "issues": "https://github.com/getsentry/sentry-php/issues", - "source": "https://github.com/getsentry/sentry-php/tree/3.2.1" + "source": "https://github.com/getsentry/sentry-php/tree/3.2.2" }, "funding": [ { @@ -6911,20 +6911,20 @@ "type": "custom" } ], - "time": "2021-04-06T07:55:41+00:00" + "time": "2021-05-06T10:15:01+00:00" }, { "name": "sentry/sentry-laravel", - "version": "2.5.1", + "version": "2.5.2", "source": { "type": "git", "url": "https://github.com/getsentry/sentry-laravel.git", - "reference": "2af8a531f202f0ac014f5fad532815ed34f730a9" + "reference": "368a2701b3bd370a2892f1f67bfbf0cbecbaca17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/getsentry/sentry-laravel/zipball/2af8a531f202f0ac014f5fad532815ed34f730a9", - "reference": "2af8a531f202f0ac014f5fad532815ed34f730a9", + "url": "https://api.github.com/repos/getsentry/sentry-laravel/zipball/368a2701b3bd370a2892f1f67bfbf0cbecbaca17", + "reference": "368a2701b3bd370a2892f1f67bfbf0cbecbaca17", "shasum": "" }, "require": { @@ -6987,7 +6987,7 @@ ], "support": { "issues": "https://github.com/getsentry/sentry-laravel/issues", - "source": "https://github.com/getsentry/sentry-laravel/tree/2.5.1" + "source": "https://github.com/getsentry/sentry-laravel/tree/2.5.2" }, "funding": [ { @@ -6999,20 +6999,20 @@ "type": "custom" } ], - "time": "2021-04-29T11:10:22+00:00" + "time": "2021-05-06T07:49:08+00:00" }, { "name": "stripe/stripe-php", - "version": "v7.77.0", + "version": "v7.78.0", "source": { "type": "git", "url": "https://github.com/stripe/stripe-php.git", - "reference": "f6724447481f6fb8c2e714165e092adad9ca470a" + "reference": "6ec33fa8e9de2322be93d28dfd685661c67ca549" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/stripe/stripe-php/zipball/f6724447481f6fb8c2e714165e092adad9ca470a", - "reference": "f6724447481f6fb8c2e714165e092adad9ca470a", + "url": "https://api.github.com/repos/stripe/stripe-php/zipball/6ec33fa8e9de2322be93d28dfd685661c67ca549", + "reference": "6ec33fa8e9de2322be93d28dfd685661c67ca549", "shasum": "" }, "require": { @@ -7058,9 +7058,9 @@ ], "support": { "issues": "https://github.com/stripe/stripe-php/issues", - "source": "https://github.com/stripe/stripe-php/tree/v7.77.0" + "source": "https://github.com/stripe/stripe-php/tree/v7.78.0" }, - "time": "2021-04-12T17:19:16+00:00" + "time": "2021-05-05T23:55:32+00:00" }, { "name": "swiftmailer/swiftmailer", @@ -9809,16 +9809,16 @@ }, { "name": "turbo124/laravel-gmail", - "version": "v5.0.1", + "version": "v5.0.2", "source": { "type": "git", "url": "https://github.com/turbo124/laravel-gmail.git", - "reference": "55ca0271a54a568ebaa26febbe0790b2ce5ac966" + "reference": "f8b8806d0b37ab5b2d4c3f402f8094d9021bd342" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/turbo124/laravel-gmail/zipball/55ca0271a54a568ebaa26febbe0790b2ce5ac966", - "reference": "55ca0271a54a568ebaa26febbe0790b2ce5ac966", + "url": "https://api.github.com/repos/turbo124/laravel-gmail/zipball/f8b8806d0b37ab5b2d4c3f402f8094d9021bd342", + "reference": "f8b8806d0b37ab5b2d4c3f402f8094d9021bd342", "shasum": "" }, "require": { @@ -9872,9 +9872,9 @@ "laravel" ], "support": { - "source": "https://github.com/turbo124/laravel-gmail/tree/v5.0.1" + "source": "https://github.com/turbo124/laravel-gmail/tree/v5.0.2" }, - "time": "2021-04-06T00:53:48+00:00" + "time": "2021-05-08T00:17:52+00:00" }, { "name": "vlucas/phpdotenv", @@ -10792,21 +10792,21 @@ }, { "name": "darkaonline/l5-swagger", - "version": "8.0.4", + "version": "8.0.5", "source": { "type": "git", "url": "https://github.com/DarkaOnLine/L5-Swagger.git", - "reference": "3477e7013daf8b6fc142c45fdcb9fe6c74d7398d" + "reference": "238e5d318b3d48a77c76b6950c51146578563853" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/DarkaOnLine/L5-Swagger/zipball/3477e7013daf8b6fc142c45fdcb9fe6c74d7398d", - "reference": "3477e7013daf8b6fc142c45fdcb9fe6c74d7398d", + "url": "https://api.github.com/repos/DarkaOnLine/L5-Swagger/zipball/238e5d318b3d48a77c76b6950c51146578563853", + "reference": "238e5d318b3d48a77c76b6950c51146578563853", "shasum": "" }, "require": { "ext-json": "*", - "laravel/framework": "^8.0 || ^7.0", + "laravel/framework": ">=8.40.0 || ^7.0", "php": "^7.2 || ^8.0", "swagger-api/swagger-ui": "^3.0", "symfony/yaml": "^5.0", @@ -10859,7 +10859,7 @@ ], "support": { "issues": "https://github.com/DarkaOnLine/L5-Swagger/issues", - "source": "https://github.com/DarkaOnLine/L5-Swagger/tree/8.0.4" + "source": "https://github.com/DarkaOnLine/L5-Swagger/tree/8.0.5" }, "funding": [ { @@ -10867,7 +10867,7 @@ "type": "github" } ], - "time": "2020-12-08T13:29:20+00:00" + "time": "2021-05-07T09:57:00+00:00" }, { "name": "dnoegel/php-xdg-base-dir", @@ -11112,16 +11112,16 @@ }, { "name": "facade/ignition", - "version": "2.8.4", + "version": "2.9.0", "source": { "type": "git", "url": "https://github.com/facade/ignition.git", - "reference": "87fb348dab0ae1a7c206c3e902a5a44ba541742f" + "reference": "e7db3b601ce742568b92648818ef903904d20164" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/facade/ignition/zipball/87fb348dab0ae1a7c206c3e902a5a44ba541742f", - "reference": "87fb348dab0ae1a7c206c3e902a5a44ba541742f", + "url": "https://api.github.com/repos/facade/ignition/zipball/e7db3b601ce742568b92648818ef903904d20164", + "reference": "e7db3b601ce742568b92648818ef903904d20164", "shasum": "" }, "require": { @@ -11185,7 +11185,7 @@ "issues": "https://github.com/facade/ignition/issues", "source": "https://github.com/facade/ignition" }, - "time": "2021-04-29T13:55:26+00:00" + "time": "2021-05-05T06:45:12+00:00" }, { "name": "facade/ignition-contracts", diff --git a/resources/views/portal/ninja2020/gateways/wepay/signup/wepay-signup.blade.php b/resources/views/portal/ninja2020/gateways/wepay/signup/wepay-signup.blade.php index b87f25d3ad8c..10cef317d17e 100644 --- a/resources/views/portal/ninja2020/gateways/wepay/signup/wepay-signup.blade.php +++ b/resources/views/portal/ninja2020/gateways/wepay/signup/wepay-signup.blade.php @@ -82,6 +82,7 @@
@endif +
From 1740f11715500d5c8994acb864d249da0db5a60e Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 8 May 2021 16:46:14 +1000 Subject: [PATCH 17/43] We Pay --- app/Http/Livewire/WepaySignup.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Http/Livewire/WepaySignup.php b/app/Http/Livewire/WepaySignup.php index 58dc7c58190f..a069997f6fed 100644 --- a/app/Http/Livewire/WepaySignup.php +++ b/app/Http/Livewire/WepaySignup.php @@ -79,6 +79,8 @@ class WepaySignup extends Component public function submit() { + $data = $this->validate($this->rules); + //need to create or get a new WePay CompanyGateway $cg = CompanyGateway::where('gateway_key', '8fdeed552015b3c7b44ed6c8ebd9e992') ->where('company_id', $this->company->id) @@ -95,8 +97,6 @@ class WepaySignup extends Component $cg->save(); } - $data = $this->validate($this->rules); - $this->saved = ctrans('texts.processing'); $wepay_driver = new WePayPaymentDriver($cg, null, null); From b111483ef194fef5c753f0fb201968283c46865d Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 11 Jun 2021 17:39:51 +1000 Subject: [PATCH 18/43] We Pay Account Signup --- app/Http/Controllers/WePayController.php | 9 +++-- app/Http/Livewire/WepaySignup.php | 35 +++++++++++++++++-- app/Models/Gateway.php | 4 +++ app/PaymentDrivers/WePay/Setup.php | 2 +- .../gateways/wepay/signup/finished.blade.php | 24 +++++++++++++ .../gateways/wepay/signup/index.blade.php | 2 +- routes/web.php | 3 +- 7 files changed, 71 insertions(+), 8 deletions(-) create mode 100644 resources/views/portal/ninja2020/gateways/wepay/signup/finished.blade.php diff --git a/app/Http/Controllers/WePayController.php b/app/Http/Controllers/WePayController.php index 978360948f88..be2285ae1b5d 100644 --- a/app/Http/Controllers/WePayController.php +++ b/app/Http/Controllers/WePayController.php @@ -44,10 +44,10 @@ class WePayController extends BaseController // $data['user_id'] = $this->encodePrimaryKey($hash['user_id']); // $data['company_key'] = $hash['company_key']; + /* Mock Data - in production we will be passed the correct company*/ $user = User::first(); $data['user_id'] = $user->id; - - $data['company_key'] = $user->account->companies()->first()->company_key; + $data['company'] = $user->account->companies()->first(); $wepay_driver = new WePayPaymentDriver(new CompanyGateway, null, null); @@ -59,4 +59,9 @@ class WePayController extends BaseController { } + + public function finished() + { + return render('gateways.wepay.signup.finished'); + } } diff --git a/app/Http/Livewire/WepaySignup.php b/app/Http/Livewire/WepaySignup.php index a069997f6fed..0a471672bd20 100644 --- a/app/Http/Livewire/WepaySignup.php +++ b/app/Http/Livewire/WepaySignup.php @@ -12,9 +12,12 @@ namespace App\Http\Livewire; +use App\DataMapper\FeesAndLimits; use App\Factory\CompanyGatewayFactory; +use App\Libraries\MultiDB; use App\Models\Company; use App\Models\CompanyGateway; +use App\Models\GatewayType; use App\Models\User; use App\PaymentDrivers\WePayPaymentDriver; use Illuminate\Support\Facades\Hash; @@ -54,8 +57,10 @@ class WepaySignup extends Component public function mount() { + MultiDB::setDb($this->company->db); + $user = User::find($this->user_id); - $this->company = Company::where('company_key', $this->company_key)->firstOrFail(); + $this->company = Company::where('company_key', $this->company->company_key)->first(); $this->fill([ 'wepay_payment_tos_agree' => '', @@ -87,6 +92,11 @@ class WepaySignup extends Component ->firstOrNew(); if(!$cg->id) { + + $fees_and_limits = new \stdClass; + $fees_and_limits->{GatewayType::CREDIT_CARD} = new FeesAndLimits; + $fees_and_limits->{GatewayType::BANK_TRANSFER} = new FeesAndLimits; + $cg = CompanyGatewayFactory::create($this->company->id, $this->user->id); $cg->gateway_key = '8fdeed552015b3c7b44ed6c8ebd9e992'; $cg->require_cvv = false; @@ -94,7 +104,9 @@ class WepaySignup extends Component $cg->require_shipping_address = false; $cg->update_details = false; $cg->config = encrypt(config('ninja.testvars.checkout')); + $cg->fees_and_limits = $fees_and_limits; $cg->save(); + } $this->saved = ctrans('texts.processing'); @@ -139,7 +151,7 @@ class WepaySignup extends Component } elseif ($data['country'] == 'GB') { $account_details['currencies'] = ['GBP']; } - + $wepay_account = $wepay->request('account/create/', $account_details); try { @@ -149,8 +161,11 @@ class WepaySignup extends Component if ($ex->getMessage() == 'This access_token is already approved.') { $confirmation_required = false; } else { - throw $ex; + request()->session()->flash('message', $ex->getMessage()); } + + nlog("failed in try catch "); + nlog($ex->getMessage()); } $config = [ @@ -166,6 +181,20 @@ class WepaySignup extends Component $cg->setConfig($config); $cg->save(); + + if ($confirmation_required) { + request()->session()->flash('message', trans('texts.created_wepay_confirmation_required')); + } else { + $update_uri = $wepay->request('/account/get_update_uri', [ + 'account_id' => $wepay_account->account_id, + 'redirect_uri' => config('ninja.app_url'), + ]); + + return redirect($update_uri->uri); + } + + + return redirect()->to('/wepay/finished'); } } diff --git a/app/Models/Gateway.php b/app/Models/Gateway.php index c182f4e1d7ad..e2e789f295a8 100644 --- a/app/Models/Gateway.php +++ b/app/Models/Gateway.php @@ -95,6 +95,10 @@ class Gateway extends StaticModel case 39: return [GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true]]; //Checkout break; + case 49: + return [GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true], + GatewayType::BANK_TRANSFER => ['refund' => true, 'token_billing' => true]]; //WePay + break; default: return []; break; diff --git a/app/PaymentDrivers/WePay/Setup.php b/app/PaymentDrivers/WePay/Setup.php index 740ef8025520..6b0a062a156c 100644 --- a/app/PaymentDrivers/WePay/Setup.php +++ b/app/PaymentDrivers/WePay/Setup.php @@ -28,7 +28,7 @@ class Setup { /* 'user_id', - 'company_key', + 'company', */ return render('gateways.wepay.signup.index', $data); diff --git a/resources/views/portal/ninja2020/gateways/wepay/signup/finished.blade.php b/resources/views/portal/ninja2020/gateways/wepay/signup/finished.blade.php new file mode 100644 index 000000000000..c0c67077a6d1 --- /dev/null +++ b/resources/views/portal/ninja2020/gateways/wepay/signup/finished.blade.php @@ -0,0 +1,24 @@ +@extends('portal.ninja2020.layout.clean', ['custom_body_class' => 'bg-gray-50']) +@section('meta_title', ctrans('texts.sign_up_with_wepay')) + +@section('body') +
+ We Pay +
+ +
+

Wepay setup complete:

+
+ +
+ @if(isset($message)) + {{ $message ?? '' }} + @endif +
+
+@endsection + +@push('footer') + +@endpush diff --git a/resources/views/portal/ninja2020/gateways/wepay/signup/index.blade.php b/resources/views/portal/ninja2020/gateways/wepay/signup/index.blade.php index e2aaaf5d7d04..9c30c242083c 100644 --- a/resources/views/portal/ninja2020/gateways/wepay/signup/index.blade.php +++ b/resources/views/portal/ninja2020/gateways/wepay/signup/index.blade.php @@ -6,7 +6,7 @@ We Pay
- @livewire('wepay-signup', ['user_id' => $user_id, 'company_key' => $company_key]) + @livewire('wepay-signup', ['user_id' => $user_id, 'company' => $company]) @endsection @push('footer') diff --git a/routes/web.php b/routes/web.php index 60fb651c3a11..e9ea7bcd8b06 100644 --- a/routes/web.php +++ b/routes/web.php @@ -21,7 +21,8 @@ Route::get('password/reset/{token}', 'Auth\ResetPasswordController@showResetForm Route::post('password/reset', 'Auth\ResetPasswordController@reset')->name('password.update'); Route::get('wepay/signup/{token}', 'WePayController@signup')->name('wepay.signup'); -Route::get('wepay/processSignup', 'WePayController@processSignup')->name('wepay.process_signup'); +Route::post('wepay/process_signup', 'WePayController@processSignup')->name('wepay.process_signup'); +Route::get('wepay/finished', 'WePayController@finished')->name('wepay.finished'); /* * Social authentication From f268b7adcb71757c0a1575dc54e5b7ae77052668 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 11 Jun 2021 17:45:41 +1000 Subject: [PATCH 19/43] Clean up --- app/Http/Controllers/WePayController.php | 27 +--- app/PaymentDrivers/WePay/Setup.php | 171 +---------------------- routes/web.php | 1 - 3 files changed, 8 insertions(+), 191 deletions(-) diff --git a/app/Http/Controllers/WePayController.php b/app/Http/Controllers/WePayController.php index be2285ae1b5d..d2e5363e446d 100644 --- a/app/Http/Controllers/WePayController.php +++ b/app/Http/Controllers/WePayController.php @@ -23,31 +23,23 @@ use Illuminate\Support\Facades\Cache; class WePayController extends BaseController { use MakesHash; + /** * Initialize WePay Signup. */ public function signup(string $token) { - // $hash = [ - // 'user_id' => auth()->user()->id, - // 'company_key'=> auth()->user()->company()->company_key, - // 'context' => $request->input('context'), - // ]; - $hash = Cache::get($token); - //temporarily comment this out - // if(!$hash) - // abort(400, 'Link expired'); - // MultiDB::findAndSetDbByCompanyKey($hash['company_key']); - // $data['user_id'] = $this->encodePrimaryKey($hash['user_id']); - // $data['company_key'] = $hash['company_key']; + MultiDB::findAndSetDbByCompanyKey($hash['company_key']); + + $user = User::findOrFail($hash['user_id']); + + $company = Company::where('company_key', $hash['company_key'])->firstOrFail(); - /* Mock Data - in production we will be passed the correct company*/ - $user = User::first(); $data['user_id'] = $user->id; - $data['company'] = $user->account->companies()->first(); + $data['company'] = $company; $wepay_driver = new WePayPaymentDriver(new CompanyGateway, null, null); @@ -55,11 +47,6 @@ class WePayController extends BaseController } - public function processSignup(Request $request) - { - - } - public function finished() { return render('gateways.wepay.signup.finished'); diff --git a/app/PaymentDrivers/WePay/Setup.php b/app/PaymentDrivers/WePay/Setup.php index 6b0a062a156c..8c926dac4e54 100644 --- a/app/PaymentDrivers/WePay/Setup.php +++ b/app/PaymentDrivers/WePay/Setup.php @@ -34,173 +34,4 @@ class Setup return render('gateways.wepay.signup.index', $data); } - - public function processSignup(Request $request) - { - - } -} - - -/* -protected function setupWePay($accountGateway, &$response) - { - $user = Auth::user(); - $account = $user->account; - - $rules = [ - 'company_name' => 'required', - 'tos_agree' => 'required', - 'first_name' => 'required', - 'last_name' => 'required', - 'email' => 'required|email', - 'country' => 'required|in:US,CA,GB', - ]; - - $validator = Validator::make(Input::all(), $rules); - - if ($validator->fails()) { - return Redirect::to('gateways/create') - ->withErrors($validator) - ->withInput(); - } - - if (! $user->email) { - $user->email = trim(Input::get('email')); - $user->first_name = trim(Input::get('first_name')); - $user->last_name = trim(Input::get('last_name')); - $user->save(); - } - - try { - $wepay = Utils::setupWePay(); - - $userDetails = [ - 'client_id' => WEPAY_CLIENT_ID, - 'client_secret' => WEPAY_CLIENT_SECRET, - 'email' => Input::get('email'), - 'first_name' => Input::get('first_name'), - 'last_name' => Input::get('last_name'), - 'original_ip' => \Request::getClientIp(true), - 'original_device' => \Request::server('HTTP_USER_AGENT'), - 'tos_acceptance_time' => time(), - 'redirect_uri' => URL::to('gateways'), - 'scope' => 'manage_accounts,collect_payments,view_user,preapprove_payments,send_money', - ]; - - $wepayUser = $wepay->request('user/register/', $userDetails); - - $accessToken = $wepayUser->access_token; - $accessTokenExpires = $wepayUser->expires_in ? (time() + $wepayUser->expires_in) : null; - - $wepay = new WePay($accessToken); - - $accountDetails = [ - 'name' => Input::get('company_name'), - 'description' => trans('texts.wepay_account_description'), - 'theme_object' => json_decode(WEPAY_THEME), - 'callback_uri' => $accountGateway->getWebhookUrl(), - 'rbits' => $account->present()->rBits, - 'country' => Input::get('country'), - ]; - - if (Input::get('country') == 'CA') { - $accountDetails['currencies'] = ['CAD']; - $accountDetails['country_options'] = ['debit_opt_in' => boolval(Input::get('debit_cards'))]; - } elseif (Input::get('country') == 'GB') { - $accountDetails['currencies'] = ['GBP']; - } - - $wepayAccount = $wepay->request('account/create/', $accountDetails); - - try { - $wepay->request('user/send_confirmation/', []); - $confirmationRequired = true; - } catch (\WePayException $ex) { - if ($ex->getMessage() == 'This access_token is already approved.') { - $confirmationRequired = false; - } else { - throw $ex; - } - } - - $accountGateway->gateway_id = GATEWAY_WEPAY; - $accountGateway->setConfig([ - 'userId' => $wepayUser->user_id, - 'accessToken' => $accessToken, - 'tokenType' => $wepayUser->token_type, - 'tokenExpires' => $accessTokenExpires, - 'accountId' => $wepayAccount->account_id, - 'state' => $wepayAccount->state, - 'testMode' => WEPAY_ENVIRONMENT == WEPAY_STAGE, - 'country' => Input::get('country'), - ]); - - if ($confirmationRequired) { - Session::flash('message', trans('texts.created_wepay_confirmation_required')); - } else { - $updateUri = $wepay->request('/account/get_update_uri', [ - 'account_id' => $wepayAccount->account_id, - 'redirect_uri' => URL::to('gateways'), - ]); - - $response = Redirect::to($updateUri->uri); - - return true; - } - - $response = Redirect::to("gateways/{$accountGateway->public_id}/edit"); - - return true; - } catch (\WePayException $e) { - Session::flash('error', $e->getMessage()); - $response = Redirect::to('gateways/create') - ->withInput(); - - return false; - } - } - */ - - -/* - - -rbits - - - private function createRBit($type, $source, $properties) - { - $data = new stdClass(); - $data->receive_time = time(); - $data->type = $type; - $data->source = $source; - $data->properties = new stdClass(); - - foreach ($properties as $key => $val) { - $data->properties->$key = $val; - } - - return $data; - } - - public function rBits() - { - $account = $this->entity; - $user = $account->users()->first(); - $data = []; - - $data[] = $this->createRBit('business_name', 'user', ['business_name' => $account->name]); - $data[] = $this->createRBit('industry_code', 'user', ['industry_detail' => $account->present()->industry]); - $data[] = $this->createRBit('comment', 'partner_database', ['comment_text' => 'Logo image not present']); - $data[] = $this->createRBit('business_description', 'user', ['business_description' => $account->present()->size]); - - $data[] = $this->createRBit('person', 'user', ['name' => $user->getFullName()]); - $data[] = $this->createRBit('email', 'user', ['email' => $user->email]); - $data[] = $this->createRBit('phone', 'user', ['phone' => $user->phone]); - $data[] = $this->createRBit('website_uri', 'user', ['uri' => $account->website]); - $data[] = $this->createRBit('external_account', 'partner_database', ['is_partner_account' => 'yes', 'account_type' => 'Invoice Ninja', 'create_time' => time()]); - - return $data; - } - */ \ No newline at end of file +} \ No newline at end of file diff --git a/routes/web.php b/routes/web.php index e9ea7bcd8b06..1d2d340c7366 100644 --- a/routes/web.php +++ b/routes/web.php @@ -21,7 +21,6 @@ Route::get('password/reset/{token}', 'Auth\ResetPasswordController@showResetForm Route::post('password/reset', 'Auth\ResetPasswordController@reset')->name('password.update'); Route::get('wepay/signup/{token}', 'WePayController@signup')->name('wepay.signup'); -Route::post('wepay/process_signup', 'WePayController@processSignup')->name('wepay.process_signup'); Route::get('wepay/finished', 'WePayController@finished')->name('wepay.finished'); /* From a441b3f79eaee3551c97272bc7fef348aff0e864 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 11 Jun 2021 18:30:39 +1000 Subject: [PATCH 20/43] Working on authorization --- app/PaymentDrivers/WePay/ACH.php | 31 ++++++ app/PaymentDrivers/WePay/CreditCard.php | 19 +++- app/PaymentDrivers/WePayPaymentDriver.php | 54 +++++++--- .../wepay/authorize/authorize.blade.php | 98 +++++++++++++++++++ 4 files changed, 189 insertions(+), 13 deletions(-) create mode 100644 app/PaymentDrivers/WePay/ACH.php create mode 100644 resources/views/portal/ninja2020/gateways/wepay/authorize/authorize.blade.php diff --git a/app/PaymentDrivers/WePay/ACH.php b/app/PaymentDrivers/WePay/ACH.php new file mode 100644 index 000000000000..6ff5e32c5de6 --- /dev/null +++ b/app/PaymentDrivers/WePay/ACH.php @@ -0,0 +1,31 @@ +wepay = $wepay; + } + + public function authorizeView($data) + { + + } + +} diff --git a/app/PaymentDrivers/WePay/CreditCard.php b/app/PaymentDrivers/WePay/CreditCard.php index 2b54246784d2..0c7151909752 100644 --- a/app/PaymentDrivers/WePay/CreditCard.php +++ b/app/PaymentDrivers/WePay/CreditCard.php @@ -25,7 +25,22 @@ class CreditCard public function authorizeView($data) { - + return render('gateways.wepay.authorize.authorize', $data); } - + + + public function authorizeResponse($data) + { + //https://developer.wepay.com/api/api-calls/credit_card#authorize + + // authorize the credit card + $response = $this->wepay->request('credit_card/authorize', array( + 'client_id' => $account_id, + 'client_secret' => 'A vacation home rental', + 'credit_card_id' => 'goods', + )); + + // display the response + print_r($response); + } } diff --git a/app/PaymentDrivers/WePayPaymentDriver.php b/app/PaymentDrivers/WePayPaymentDriver.php index 2b21cc6880af..2d2855cce4ed 100644 --- a/app/PaymentDrivers/WePayPaymentDriver.php +++ b/app/PaymentDrivers/WePayPaymentDriver.php @@ -16,6 +16,7 @@ use App\Models\GatewayType; use App\Models\Payment; use App\Models\PaymentHash; use App\Models\SystemLog; +use App\PaymentDrivers\WePay\ACH; use App\PaymentDrivers\WePay\CreditCard; use App\PaymentDrivers\WePay\Setup; use App\Utils\Traits\MakesHash; @@ -26,18 +27,25 @@ class WePayPaymentDriver extends BaseDriver { use MakesHash; - public $refundable = true; //does this gateway support refunds? + /* Does this gateway support refunds? */ + public $refundable = true; - public $token_billing = true; //does this gateway support token billing? + /* Does this gateway support token billing? */ + public $token_billing = true; - public $can_authorise_credit_card = true; //does this gateway support authorizations? + /* Does this gateway support authorizations? */ + public $can_authorise_credit_card = true; - public $wepay; //initialized gateway + /* Initialized gateway */ + public $wepay; - public $payment_method; //initialized payment method + /* Initialized payment method */ + public $payment_method; + /* Maps the Payment Gateway Type - to its implementation */ public static $methods = [ - GatewayType::CREDIT_CARD => CreditCard::class, //maps GatewayType => Implementation class + GatewayType::CREDIT_CARD => CreditCard::class, + GatewayType::BANK_TRANSFER => ACH::class, ]; const SYSTEM_LOG_TYPE = SystemLog::TYPE_WEPAY; @@ -63,16 +71,40 @@ class WePayPaymentDriver extends BaseDriver } + /** + * Return the gateway types that have been enabled + * + * @return array + */ + public function gatewayTypes(): array + { + $types = []; + + if($this->company_gateway->fees_and_limits->{GatewayType::BANK_TRANSFER}->is_enabled) + $types[] = GatewayType::CREDIT_CARD; + + if($this->company_gateway->fees_and_limits->{GatewayType::BANK_TRANSFER}->is_enabled) + $types[] = GatewayType::BANK_TRANSFER; + + return $types; + } + + /** + * Setup the gateway + * + * @param array $data user_id + company + * @return view + */ public function setup(array $data) { return (new Setup($this))->boot($data); } - public function processSetup(Request $request) - { - return (new Setup($this))->processSignup($request); - } - + /** + * Set the payment method + * + * @param int $payment_method_id Alias of GatewayType + */ public function setPaymentMethod($payment_method_id) { $class = self::$methods[$payment_method_id]; diff --git a/resources/views/portal/ninja2020/gateways/wepay/authorize/authorize.blade.php b/resources/views/portal/ninja2020/gateways/wepay/authorize/authorize.blade.php new file mode 100644 index 000000000000..639ed8984db8 --- /dev/null +++ b/resources/views/portal/ninja2020/gateways/wepay/authorize/authorize.blade.php @@ -0,0 +1,98 @@ +@extends('portal.ninja2020.layout.payments', ['gateway_title' => ctrans('texts.credit_card'), 'card_title' => ctrans('texts.credit_card')]) + +@section('gateway_head') + + + + + +@endsection + +@section('gateway_content') + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Name:
Email:
Credit Card Number:
Expiration Month:
Expiration Year:
CVV:
Postal Code:
+ +@endsection + +@section('gateway_footer') + + + + +@endsection \ No newline at end of file From 62811d968ba0116f246061b692d159dfa4df72db Mon Sep 17 00:00:00 2001 From: = Date: Sun, 13 Jun 2021 20:37:37 +1000 Subject: [PATCH 21/43] WePay --- app/PaymentDrivers/WePayPaymentDriver.php | 4 ++++ .../gateways/wepay/authorize/authorize.blade.php | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/app/PaymentDrivers/WePayPaymentDriver.php b/app/PaymentDrivers/WePayPaymentDriver.php index 2d2855cce4ed..b561e419d0d1 100644 --- a/app/PaymentDrivers/WePayPaymentDriver.php +++ b/app/PaymentDrivers/WePayPaymentDriver.php @@ -114,6 +114,10 @@ class WePayPaymentDriver extends BaseDriver public function authorizeView(array $data) { + $data['gateway'] = $this->wepay; + // $data['public_client_id'] = $this->authorize->init()->getPublicClientKey(); + // $data['api_login_id'] = $this->authorize->company_gateway->getConfigField('apiLoginId'); + return $this->payment_method->authorizeView($data); //this is your custom implementation from here } diff --git a/resources/views/portal/ninja2020/gateways/wepay/authorize/authorize.blade.php b/resources/views/portal/ninja2020/gateways/wepay/authorize/authorize.blade.php index 639ed8984db8..43a5650f72cb 100644 --- a/resources/views/portal/ninja2020/gateways/wepay/authorize/authorize.blade.php +++ b/resources/views/portal/ninja2020/gateways/wepay/authorize/authorize.blade.php @@ -9,7 +9,15 @@ @endsection @section('gateway_content') +
+ @csrf + + + + +
From 0bc3ec111dbe0169421b27d24046b17d4967db44 Mon Sep 17 00:00:00 2001 From: = Date: Sun, 13 Jun 2021 22:49:43 +1000 Subject: [PATCH 22/43] Authorize WePAy --- app/Http/Controllers/WePayController.php | 1 + app/Http/Livewire/WepaySignup.php | 2 +- app/PaymentDrivers/WePay/CreditCard.php | 2 + .../wepay/authorize/authorize.blade.php | 68 +++++++------------ .../wepay/includes/credit_card.blade.php | 12 ++++ 5 files changed, 42 insertions(+), 43 deletions(-) create mode 100644 resources/views/portal/ninja2020/gateways/wepay/includes/credit_card.blade.php diff --git a/app/Http/Controllers/WePayController.php b/app/Http/Controllers/WePayController.php index d2e5363e446d..7813e94ccf70 100644 --- a/app/Http/Controllers/WePayController.php +++ b/app/Http/Controllers/WePayController.php @@ -13,6 +13,7 @@ namespace App\Http\Controllers; use App\Libraries\MultiDB; +use App\Models\Company; use App\Models\CompanyGateway; use App\Models\User; use App\PaymentDrivers\WePayPaymentDriver; diff --git a/app/Http/Livewire/WepaySignup.php b/app/Http/Livewire/WepaySignup.php index 0a471672bd20..93623e54f10c 100644 --- a/app/Http/Livewire/WepaySignup.php +++ b/app/Http/Livewire/WepaySignup.php @@ -124,7 +124,7 @@ class WepaySignup extends Component 'original_ip' => request()->ip(), 'original_device' => request()->server('HTTP_USER_AGENT'), 'tos_acceptance_time' => time(), - 'redirect_uri' => route('wepay.process_signup'), + 'redirect_uri' => route('wepay.finished'), 'scope' => 'manage_accounts,collect_payments,view_user,preapprove_payments,send_money', ]; diff --git a/app/PaymentDrivers/WePay/CreditCard.php b/app/PaymentDrivers/WePay/CreditCard.php index 0c7151909752..10f9a020826a 100644 --- a/app/PaymentDrivers/WePay/CreditCard.php +++ b/app/PaymentDrivers/WePay/CreditCard.php @@ -25,6 +25,8 @@ class CreditCard public function authorizeView($data) { + $data['gateway'] = $this->wepay; + return render('gateways.wepay.authorize.authorize', $data); } diff --git a/resources/views/portal/ninja2020/gateways/wepay/authorize/authorize.blade.php b/resources/views/portal/ninja2020/gateways/wepay/authorize/authorize.blade.php index 43a5650f72cb..489e09618cb2 100644 --- a/resources/views/portal/ninja2020/gateways/wepay/authorize/authorize.blade.php +++ b/resources/views/portal/ninja2020/gateways/wepay/authorize/authorize.blade.php @@ -1,6 +1,9 @@ @extends('portal.ninja2020.layout.payments', ['gateway_title' => ctrans('texts.credit_card'), 'card_title' => ctrans('texts.credit_card')]) @section('gateway_head') + + + @@ -18,41 +21,22 @@ -
Name:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name:
Email:
Credit Card Number:
Expiration Month:
Expiration Year:
CVV:
Postal Code:
+ @if(!Request::isSecure()) +

{{ ctrans('texts.https_required') }}

+ @endif + + + + @component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.method')]) + {{ ctrans('texts.credit_card') }} + @endcomponent + + @include('portal.ninja2020.gateways.wepay.includes.credit_card') + + @component('portal.ninja2020.gateways.includes.pay_now', ['id' => 'card_button']) + {{ ctrans('texts.add_payment_method') }} + @endcomponent @endsection @section('gateway_footer') @@ -76,16 +60,16 @@ }; // Attach the event to the DOM - addEvent(d.id('cc-submit'), 'click', function() { - var userName = [valueById('name')].join(' '); + addEvent(d.id('card_button'), 'click', function() { + var userName = [valueById('cardholder_name')].join(' '); response = WePay.credit_card.create({ - "client_id": 118711, - "user_name": valueById('name'), + "client_id": valueById('client_id'), + "user_name": valueById('cardholder_name'), "email": valueById('email'), - "cc_number": valueById('cc-number'), - "cvv": valueById('cc-cvv'), - "expiration_month": valueById('cc-month'), - "expiration_year": valueById('cc-year'), + "cc_number": valueById('card-number'), + "cvv": valueById('cvv'), + "expiration_month": valueById('expiration_month'), + "expiration_year": valueById('expiration_year'), "address": { "postal_code": valueById('postal_code') } diff --git a/resources/views/portal/ninja2020/gateways/wepay/includes/credit_card.blade.php b/resources/views/portal/ninja2020/gateways/wepay/includes/credit_card.blade.php new file mode 100644 index 000000000000..49da5ff160fd --- /dev/null +++ b/resources/views/portal/ninja2020/gateways/wepay/includes/credit_card.blade.php @@ -0,0 +1,12 @@ +
+
+ + + + + +
+ +
+
From dc7f4507cdeb877e65c17096f0dee5ab6c60ca47 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 14 Jun 2021 08:33:56 +1000 Subject: [PATCH 23/43] Update .env vars for wepay --- config/ninja.php | 2 +- .../gateways/wepay/authorize/authorize.blade.php | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/config/ninja.php b/config/ninja.php index b2ef34d1616d..3fdea7ac7069 100644 --- a/config/ninja.php +++ b/config/ninja.php @@ -149,7 +149,7 @@ return [ 'invoiceninja_hosted_pdf_generation' => env('NINJA_HOSTED_PDF', false), 'ninja_stripe_key' => env('NINJA_STRIPE_KEY', null), 'wepay' => [ - 'environment' => env('WEPAY_ENVIRONMENT', 'staging'), + 'environment' => env('WEPAY_ENVIRONMENT', 'stage'), 'client_id' => env('WEPAY_CLIENT_ID', ''), 'client_secret' => env('WEPAY_CLIENT_SECRET',''), ] diff --git a/resources/views/portal/ninja2020/gateways/wepay/authorize/authorize.blade.php b/resources/views/portal/ninja2020/gateways/wepay/authorize/authorize.blade.php index 489e09618cb2..13b3fa7a71bd 100644 --- a/resources/views/portal/ninja2020/gateways/wepay/authorize/authorize.blade.php +++ b/resources/views/portal/ninja2020/gateways/wepay/authorize/authorize.blade.php @@ -44,7 +44,7 @@ - @endsection @@ -20,6 +19,7 @@ + @if(!Request::isSecure()) @@ -66,6 +66,7 @@ addEvent(document.getElementById('card_button'), 'click', function() { var myCard = $('#my-card'); + var userName = [valueById('cardholder_name')].join(' '); response = WePay.credit_card.create({ "client_id": "{{ config('ninja.wepay.client_id') }}", @@ -81,15 +82,22 @@ }, function(data) { if (data.error) { console.log(data); - // handle error response + // handle error response error_description + let errors = document.getElementById('errors'); + errors.textContent = ''; + errors.textContent = data.error_description; + errors.hidden = false; } else { // call your own app's API to save the token inside the data; // show a success page - var token = response.credit_card_id; + var token = data.credit_card_id; // Insert the token into the form so it gets submitted to the server - $form.append($('').val(token)); - // and submit - $form.get(0).submit(); + // console.log(data); + + document.querySelector('input[name="credit_card_id"]').value = token; + + document.getElementById('server_response').submit(); + } }); }); From ca7631e950376692b782013a9b11ecce8ee5c88a Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 15 Jun 2021 23:08:59 +1000 Subject: [PATCH 28/43] Minor fixes for company exporter --- app/Jobs/Company/CompanyExport.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/Jobs/Company/CompanyExport.php b/app/Jobs/Company/CompanyExport.php index f9447a1357e2..ac3ba263dfcf 100644 --- a/app/Jobs/Company/CompanyExport.php +++ b/app/Jobs/Company/CompanyExport.php @@ -480,10 +480,11 @@ class CompanyExport implements ShouldQueue $file_name = date('Y-m-d').'_'.str_replace(' ', '_', $this->company->present()->name() . '_' . $this->company->company_key .'.zip'); - $path = public_path('storage/backups/'); - - if(!Storage::exists($path)) - Storage::makeDirectory($path, 0775); + //$path = public_path('storage/backups/'); + $path = 'backups'; + + if(!Storage::disk(config('filesystems.default'))->exists($path)) + Storage::disk(config('filesystems.default'))->makeDirectory($path, 0775); $zip_path = public_path('storage/backups/'.$file_name); $zip = new \ZipArchive(); From d4f27bb3eea5ed23a39fc2671b6eb3240379c9b4 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 16 Jun 2021 13:26:33 +1000 Subject: [PATCH 29/43] WePay authorize for Bank Transfer --- app/PaymentDrivers/WePay/CreditCard.php | 72 ++++++++++++++++-- .../wepay/authorize/authorize.blade.php | 76 ++++++++++++++----- .../wepay/includes/credit_card.blade.php | 10 +-- 3 files changed, 130 insertions(+), 28 deletions(-) diff --git a/app/PaymentDrivers/WePay/CreditCard.php b/app/PaymentDrivers/WePay/CreditCard.php index 5d8bc96d4a9f..2c1d76878db1 100644 --- a/app/PaymentDrivers/WePay/CreditCard.php +++ b/app/PaymentDrivers/WePay/CreditCard.php @@ -12,6 +12,8 @@ namespace App\PaymentDrivers\WePay; +use App\Exceptions\PaymentFailed; +use App\Models\GatewayType; use App\PaymentDrivers\WePayPaymentDriver; class CreditCard @@ -33,21 +35,79 @@ class CreditCard public function authorizeResponse($request) { //https://developer.wepay.com/api/api-calls/credit_card#authorize - $data = $request->all(); // authorize the credit card nlog($data); + /* + '_token' => '1Fk5CRj34up5ntKPvrFyMIAJhDdUNF3boqT3iIN3', + 'company_gateway_id' => '39', + 'payment_method_id' => '1', + 'gateway_response' => NULL, + 'is_default' => NULL, + 'credit_card_id' => '180642154638', + 'q' => '/client/payment_methods', + 'method' => '1', + */ $response = $this->wepay_payment_driver->wepay->request('credit_card/authorize', array( - 'account_id' => $this->wepay_payment_driver->company_gateway->getConfigField('accountId'), 'client_id' => config('ninja.wepay.client_id'), 'client_secret' => config('ninja.wepay.client_secret'), - 'credit_card_id' => $data['credit_card_id'], + 'credit_card_id' => (int)$data['credit_card_id'], )); // display the response - print_r($response); - nlog($response); - } + //nlog($response); + + if(in_array($response->state, ['new', 'authorized'])){ + + $this->storePaymentMethod($response, GatewayType::CREDIT_CARD); + + return redirect()->route('client.payment_methods.index'); + } + + throw new PaymentFailed("There was a problem adding this payment method.", 400); + + /* + [credit_card_id] => 348084962473 + [credit_card_name] => Visa xxxxxx4018 + [state] => authorized + [user_name] => Joey Diaz + [email] => user@example.com + [create_time] => 1623798172 + [expiration_month] => 10 + [expiration_year] => 2023 + [last_four] => 4018 + [input_source] => card_keyed + [virtual_terminal_mode] => none + [card_on_file] => + [recurring] => + [cvv_provided] => 1 + [auto_update] => + */ + + } + + + private function storePaymentMethod($response, $payment_method_id) + { + + $payment_meta = new \stdClass; + $payment_meta->exp_month = (string) $response->expiration_month; + $payment_meta->exp_year = (string) $response->expiration_year; + $payment_meta->brand = (string) $response->credit_card_name; + $payment_meta->last4 = (string) $response->last_four; + $payment_meta->type = GatewayType::CREDIT_CARD; + + $data = [ + 'payment_meta' => $payment_meta, + 'token' => $response->credit_card_id, + 'payment_method_id' => $payment_method_id, + ]; + + $this->wepay_payment_driver->storeGatewayToken($data); + + } + } + diff --git a/resources/views/portal/ninja2020/gateways/wepay/authorize/authorize.blade.php b/resources/views/portal/ninja2020/gateways/wepay/authorize/authorize.blade.php index 160fdd847c94..8be02b9c9b17 100644 --- a/resources/views/portal/ninja2020/gateways/wepay/authorize/authorize.blade.php +++ b/resources/views/portal/ninja2020/gateways/wepay/authorize/authorize.blade.php @@ -44,10 +44,11 @@ + + + +@endsection \ No newline at end of file diff --git a/resources/views/portal/ninja2020/gateways/wepay/authorize/verify.blade.php b/resources/views/portal/ninja2020/gateways/wepay/authorize/verify.blade.php new file mode 100644 index 000000000000..aa8328add490 --- /dev/null +++ b/resources/views/portal/ninja2020/gateways/wepay/authorize/verify.blade.php @@ -0,0 +1,24 @@ +@extends('portal.ninja2020.layout.payments', ['gateway_title' => 'ACH (Verification)', 'card_title' => 'ACH (Verification)']) + +@section('gateway_content') + @if(session()->has('error')) +
{{ session('error') }}
+ @endif + +
+ @csrf + + + @component('portal.ninja2020.components.general.card-element', ['title' => '#1 ' . ctrans('texts.amount')]) + + @endcomponent + + @component('portal.ninja2020.components.general.card-element', ['title' => '#2 ' . ctrans('texts.amount')]) + + @endcomponent + + @component('portal.ninja2020.gateways.includes.pay_now', ['type' => 'submit']) + {{ ctrans('texts.complete_verification')}} + @endcomponent +
+@endsection diff --git a/resources/views/portal/ninja2020/gateways/wepay/bank_transfer.blade.php b/resources/views/portal/ninja2020/gateways/wepay/bank_transfer.blade.php new file mode 100644 index 000000000000..0d38a0a16fe2 --- /dev/null +++ b/resources/views/portal/ninja2020/gateways/wepay/bank_transfer.blade.php @@ -0,0 +1,47 @@ +@extends('portal.ninja2020.layout.payments', ['gateway_title' => 'ACH', 'card_title' => 'ACH']) + +@section('gateway_content') + @if(count($tokens) > 0) + + + @include('portal.ninja2020.gateways.includes.payment_details') + +
+ @csrf + + + + + + +
+ + @component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.pay_with')]) + @if(count($tokens) > 0) + @foreach($tokens as $token) + + @endforeach + @endisset + @endcomponent + + @else + @component('portal.ninja2020.components.general.card-element-single', ['title' => 'ACH', 'show_title' => false]) + {{ ctrans('texts.bank_account_not_linked') }} + {{ ctrans('texts.add_payment_method') }} + @endcomponent + @endif + + @include('portal.ninja2020.gateways.includes.pay_now') +@endsection + +@push('footer') + +@endpush From c076998366007b09a7c904623b5756b41c1cd2e4 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 16 Jun 2021 20:12:04 +1000 Subject: [PATCH 31/43] Wepay Payments --- app/DataMapper/PaymentMethodMeta.php | 3 + .../ClientPortal/PaymentMethodController.php | 14 +-- app/PaymentDrivers/Stripe/ACH.php | 2 +- app/PaymentDrivers/WePay/ACH.php | 88 +++++++++++++++---- app/PaymentDrivers/WePay/CreditCard.php | 30 +++++++ app/PaymentDrivers/WePayPaymentDriver.php | 14 +++ resources/lang/en/texts.php | 1 + .../gateways/stripe/ach/verify.blade.php | 4 +- .../gateways/wepay/authorize/verify.blade.php | 4 +- .../gateways/wepay/bank_transfer.blade.php | 10 +++ .../gateways/wepay/credit_card/pay.blade.php | 0 11 files changed, 142 insertions(+), 28 deletions(-) create mode 100644 resources/views/portal/ninja2020/gateways/wepay/credit_card/pay.blade.php diff --git a/app/DataMapper/PaymentMethodMeta.php b/app/DataMapper/PaymentMethodMeta.php index 3745fa299e3d..451870fd42b4 100644 --- a/app/DataMapper/PaymentMethodMeta.php +++ b/app/DataMapper/PaymentMethodMeta.php @@ -27,4 +27,7 @@ class PaymentMethodMeta /** @var int */ public $type; + + /** @var string */ + public $state; } diff --git a/app/Http/Controllers/ClientPortal/PaymentMethodController.php b/app/Http/Controllers/ClientPortal/PaymentMethodController.php index 5e554d2783d1..0f6bd432b217 100644 --- a/app/Http/Controllers/ClientPortal/PaymentMethodController.php +++ b/app/Http/Controllers/ClientPortal/PaymentMethodController.php @@ -92,9 +92,9 @@ class PaymentMethodController extends Controller public function verify(ClientGatewayToken $payment_method) { - $gateway = $this->getClientGateway(); - - return $gateway +// $gateway = $this->getClientGateway(); + + return $payment_method->gateway ->driver(auth()->user()->client) ->setPaymentMethod(request()->query('method')) ->verificationView($payment_method); @@ -102,9 +102,9 @@ class PaymentMethodController extends Controller public function processVerification(Request $request, ClientGatewaytoken $payment_method) { - $gateway = $this->getClientGateway(); + // $gateway = $this->getClientGateway(); - return $gateway + return $payment_method->gateway ->driver(auth()->user()->client) ->setPaymentMethod(request()->query('method')) ->processVerification($request, $payment_method); @@ -118,9 +118,9 @@ class PaymentMethodController extends Controller */ public function destroy(ClientGatewayToken $payment_method) { - $gateway = $this->getClientGateway(); + // $gateway = $this->getClientGateway(); - $gateway + $payment_method->gateway ->driver(auth()->user()->client) ->setPaymentMethod(request()->query('method')) ->detach($payment_method); diff --git a/app/PaymentDrivers/Stripe/ACH.php b/app/PaymentDrivers/Stripe/ACH.php index 3dfe09459170..5ee3c8602479 100644 --- a/app/PaymentDrivers/Stripe/ACH.php +++ b/app/PaymentDrivers/Stripe/ACH.php @@ -167,7 +167,7 @@ class ACH return $this->processUnsuccessfulPayment($state); } catch (Exception $e) { if ($e instanceof CardException) { - return redirect()->route('client.payment_methods.verification', ['id' => ClientGatewayToken::first()->hashed_id, 'method' => GatewayType::BANK_TRANSFER]); + return redirect()->route('client.payment_methods.verification', ['payment_method' => ClientGatewayToken::first()->hashed_id, 'method' => GatewayType::BANK_TRANSFER]); } throw new PaymentFailed($e->getMessage(), $e->getCode()); diff --git a/app/PaymentDrivers/WePay/ACH.php b/app/PaymentDrivers/WePay/ACH.php index 521f853b08ad..23f8cb37c37c 100644 --- a/app/PaymentDrivers/WePay/ACH.php +++ b/app/PaymentDrivers/WePay/ACH.php @@ -13,13 +13,16 @@ namespace App\PaymentDrivers\WePay; use App\Exceptions\PaymentFailed; -use Illuminate\Http\Request; use App\Models\ClientGatewayToken; use App\Models\GatewayType; use App\PaymentDrivers\WePayPaymentDriver; +use App\Utils\Traits\MakesHash; +use Illuminate\Http\Request; class ACH { + use MakesHash; + public $wepay_payment_driver; public function __construct(WePayPaymentDriver $wepay_payment_driver) @@ -115,15 +118,30 @@ class ACH */ public function processVerification(Request $request, ClientGatewayToken $token) { + $transactions = $request->input('transactions'); - $response = $this->wepay_payment_driver->wepay->request('payment_bank/verify', [ - 'client_id' => config('ninja.wepay.client_id'), - 'client_secret' => config('ninja.wepay.client_secret'), - 'payment_bank_id' => $token->token, - 'type' => 'microdeposits', - 'microdeposits' => $request->input('transactions'), - ]); + $transformed_transactions = []; + foreach($transactions as $transaction) + $transformed_transactions[] = (int)$transaction; + + try { + + $response = $this->wepay_payment_driver->wepay->request('payment_bank/verify', [ + 'client_id' => config('ninja.wepay.client_id'), + 'client_secret' => config('ninja.wepay.client_secret'), + 'payment_bank_id' => $token->token, + 'type' => 'microdeposits', + 'microdeposits' => $transformed_transactions, + ]); + + } + catch(\Exception $e){ + + return redirect()->route('client.payment_methods.verification', ['payment_method' => $token->hashed_id, 'method' => GatewayType::BANK_TRANSFER]) + ->with('error', $e->getMessage()); + + } /* { "payment_bank_id": 12345, @@ -132,20 +150,23 @@ class ACH "state": "authorized" } */ - + nlog($response); + //$meta = $token->meta; if($response->state == "authorized") { - - $token->routing_number = $response->state; + $meta = $token->meta; + $meta->state = $response->state; + $token->meta; $token->save(); - redirect()->route('client.payment_methods.index'); + return redirect()->route('client.payment_methods.index'); } else{ - redirect()->route('client.payment_methods.verification', ['id' => $token->hashed_id, 'method' => GatewayType::BANK_TRANSFER]) - ->withErrors(['errors', ctrans('verification_failed')]); + + return redirect()->route('client.payment_methods.verification', ['payment_method' => $token->hashed_id, 'method' => GatewayType::BANK_TRANSFER]) + ->with('error', ctrans('texts.verification_failed')); } } @@ -166,7 +187,42 @@ class ACH { nlog($request->all()); - redirect()->route('client.payment_methods.verification', ['id' => $token->hashed_id, 'method' => GatewayType::BANK_TRANSFER]); + $token = ClientGatewayToken::find($this->decodePrimaryKey($request->input('source'))); + $token_meta = $token->meta; + + if($token_meta->state != "authorized") + return redirect()->route('client.payment_methods.verification', ['payment_method' => $token->hashed_id, 'method' => GatewayType::BANK_TRANSFER]); + + + + + + +// response = wepay.call('/checkout/create', { +// 'account_id': account_id, +// 'amount': '25.50', +// 'short_description': 'A vacation home rental', +// 'type': 'GOODS', +// 'payment_method': { +// 'type': 'payment_bank', +// 'payment_bank': { +// 'id': 20000061 +// } +// } +// }) + + + + + + + + + + + + + // $this->stripe->init(); @@ -242,12 +298,12 @@ class ACH $payment_meta->brand = (string) $response->bank_name; $payment_meta->last4 = (string) $response->account_last_four; $payment_meta->type = GatewayType::BANK_TRANSFER; + $payment_meta->state = $response->state; $data = [ 'payment_meta' => $payment_meta, 'token' => $response->payment_bank_id, 'payment_method_id' => $payment_method_id, - 'routing_number' => $response->state, ]; $this->wepay_payment_driver->storeGatewayToken($data); diff --git a/app/PaymentDrivers/WePay/CreditCard.php b/app/PaymentDrivers/WePay/CreditCard.php index e75038976648..fb5b1fe56c75 100644 --- a/app/PaymentDrivers/WePay/CreditCard.php +++ b/app/PaymentDrivers/WePay/CreditCard.php @@ -89,6 +89,34 @@ class CreditCard } + public function paymentView(array $data) + { + $data['gateway'] = $this->wepay_payment_driver; + + return render('gateways.wepay.credit_card.pay', $data); + } + + public function paymentResponse(PaymentResponseRequest $request) + { + + + // // charge the credit card + // $response = $wepay->request('checkout/create', array( + // 'account_id' => $account_id, + // 'amount' => '25.50', + // 'currency' => 'USD', + // 'short_description' => 'A vacation home rental', + // 'type' => 'goods', + // 'payment_method' => array( + // 'type' => 'credit_card', + // 'credit_card' => array( + // 'id' => $credit_card_id + // ) + // ) + // )); + + } + private function storePaymentMethod($response, $payment_method_id) { @@ -109,5 +137,7 @@ class CreditCard } + + } diff --git a/app/PaymentDrivers/WePayPaymentDriver.php b/app/PaymentDrivers/WePayPaymentDriver.php index 7245528a9771..de7c6caf3df9 100644 --- a/app/PaymentDrivers/WePayPaymentDriver.php +++ b/app/PaymentDrivers/WePayPaymentDriver.php @@ -134,6 +134,20 @@ class WePayPaymentDriver extends BaseDriver return $this->payment_method->authorizeResponse($request); //this is your custom implementation from here } + public function verificationView(ClientGatewayToken $cgt) + { + $this->init(); + + return $this->payment_method->verificationView($cgt); + } + + public function processVerification(Request $request, ClientGatewayToken $cgt) + { + $this->init(); + + return $this->payment_method->processVerification($request, $cgt); + } + public function processPaymentView(array $data) { return $this->payment_method->paymentView($data); //this is your custom implementation from here diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index 9670fe609788..742103e460c2 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -4256,6 +4256,7 @@ $LANG = array( 'user_cross_linked_error' => 'User exists but cannot be crossed linked to multiple accounts', 'company_import_failure_subject' => 'Error importing :company', 'company_import_failure_body' => 'There was an error importing the company data, the error message was:', + 'amount_cents' => 'Amount in pennies,pence or cents' ); return $LANG; diff --git a/resources/views/portal/ninja2020/gateways/stripe/ach/verify.blade.php b/resources/views/portal/ninja2020/gateways/stripe/ach/verify.blade.php index 9cdcf2803c4e..e173a2dd84a9 100644 --- a/resources/views/portal/ninja2020/gateways/stripe/ach/verify.blade.php +++ b/resources/views/portal/ninja2020/gateways/stripe/ach/verify.blade.php @@ -10,11 +10,11 @@ - @component('portal.ninja2020.components.general.card-element', ['title' => '#1 ' . ctrans('texts.amount')]) + @component('portal.ninja2020.components.general.card-element', ['title' => '#1 ' . ctrans('texts.amount_cents')]) @endcomponent - @component('portal.ninja2020.components.general.card-element', ['title' => '#2 ' . ctrans('texts.amount')]) + @component('portal.ninja2020.components.general.card-element', ['title' => '#2 ' . ctrans('texts.amount_cents')]) @endcomponent diff --git a/resources/views/portal/ninja2020/gateways/wepay/authorize/verify.blade.php b/resources/views/portal/ninja2020/gateways/wepay/authorize/verify.blade.php index aa8328add490..8087eb3b6ce2 100644 --- a/resources/views/portal/ninja2020/gateways/wepay/authorize/verify.blade.php +++ b/resources/views/portal/ninja2020/gateways/wepay/authorize/verify.blade.php @@ -9,11 +9,11 @@ @csrf - @component('portal.ninja2020.components.general.card-element', ['title' => '#1 ' . ctrans('texts.amount')]) + @component('portal.ninja2020.components.general.card-element', ['title' => '#1 ' . ctrans('texts.amount_cents')]) @endcomponent - @component('portal.ninja2020.components.general.card-element', ['title' => '#2 ' . ctrans('texts.amount')]) + @component('portal.ninja2020.components.general.card-element', ['title' => '#2 ' . ctrans('texts.amount_cents')]) @endcomponent diff --git a/resources/views/portal/ninja2020/gateways/wepay/bank_transfer.blade.php b/resources/views/portal/ninja2020/gateways/wepay/bank_transfer.blade.php index 0d38a0a16fe2..3c77a2519dfa 100644 --- a/resources/views/portal/ninja2020/gateways/wepay/bank_transfer.blade.php +++ b/resources/views/portal/ninja2020/gateways/wepay/bank_transfer.blade.php @@ -43,5 +43,15 @@ @endsection @push('footer') + @endpush diff --git a/resources/views/portal/ninja2020/gateways/wepay/credit_card/pay.blade.php b/resources/views/portal/ninja2020/gateways/wepay/credit_card/pay.blade.php new file mode 100644 index 000000000000..e69de29bb2d1 From 6435725d0f37dfd327ac6bf0ac68add1ea81445d Mon Sep 17 00:00:00 2001 From: = Date: Sat, 19 Jun 2021 11:18:57 +1000 Subject: [PATCH 32/43] Payment layout for wepay credit card --- app/PaymentDrivers/WePay/CreditCard.php | 2 +- .../gateways/wepay/credit_card/pay.blade.php | 169 ++++++++++++++++++ 2 files changed, 170 insertions(+), 1 deletion(-) diff --git a/app/PaymentDrivers/WePay/CreditCard.php b/app/PaymentDrivers/WePay/CreditCard.php index fb5b1fe56c75..b88da429fa89 100644 --- a/app/PaymentDrivers/WePay/CreditCard.php +++ b/app/PaymentDrivers/WePay/CreditCard.php @@ -99,7 +99,7 @@ class CreditCard public function paymentResponse(PaymentResponseRequest $request) { - +nlog($request->all()); // // charge the credit card // $response = $wepay->request('checkout/create', array( // 'account_id' => $account_id, diff --git a/resources/views/portal/ninja2020/gateways/wepay/credit_card/pay.blade.php b/resources/views/portal/ninja2020/gateways/wepay/credit_card/pay.blade.php index e69de29bb2d1..dc0163c5bfd9 100644 --- a/resources/views/portal/ninja2020/gateways/wepay/credit_card/pay.blade.php +++ b/resources/views/portal/ninja2020/gateways/wepay/credit_card/pay.blade.php @@ -0,0 +1,169 @@ +@extends('portal.ninja2020.layout.payments', ['gateway_title' => ctrans('texts.credit_card'), 'card_title' => ctrans('texts.credit_card')]) + +@section('gateway_head') + + + +@endsection + +@section('gateway_content') +
+ @csrf + + + + + + + + +
+ + + + @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')]) + @if(count($tokens) > 0) + @foreach($tokens as $token) + + @endforeach + @endisset + + + @endcomponent + + @include('portal.ninja2020.gateways.includes.save_card') + + @include('portal.ninja2020.gateways.wepay.includes.credit_card') + + @include('portal.ninja2020.gateways.includes.pay_now') +@endsection + +@section('gateway_footer') + + + +@endsection From d16895401cd378ff873f7f67f022ac71d0b53937 Mon Sep 17 00:00:00 2001 From: = Date: Sat, 19 Jun 2021 11:47:36 +1000 Subject: [PATCH 33/43] Handle Wepay Events --- app/PaymentDrivers/WePayPaymentDriver.php | 97 ++++++++++++++++++++++- 1 file changed, 94 insertions(+), 3 deletions(-) diff --git a/app/PaymentDrivers/WePayPaymentDriver.php b/app/PaymentDrivers/WePayPaymentDriver.php index de7c6caf3df9..0ef6c063bdad 100644 --- a/app/PaymentDrivers/WePayPaymentDriver.php +++ b/app/PaymentDrivers/WePayPaymentDriver.php @@ -11,6 +11,7 @@ namespace App\PaymentDrivers; +use App\Http\Requests\Payments\PaymentWebhookRequest; use App\Models\ClientGatewayToken; use App\Models\GatewayType; use App\Models\Payment; @@ -120,9 +121,6 @@ class WePayPaymentDriver extends BaseDriver $client = $data['client']; $contact = $client->primary_contact()->first() ? $client->primary_contact()->first() : $lient->contacts->first(); $data['contact'] = $contact; - // $data['contact'] = $this->company_gateway - // $data['public_client_id'] = $this->authorize->init()->getPublicClientKey(); - // $data['api_login_id'] = $this->authorize->company_gateway->getConfigField('apiLoginId'); return $this->payment_method->authorizeView($data); //this is your custom implementation from here } @@ -168,6 +166,99 @@ class WePayPaymentDriver extends BaseDriver return $this->payment_method->yourTokenBillingImplmentation(); //this is your custom implementation from here } + public function processWebhookRequest(PaymentWebhookRequest $request, Payment $payment = null) + { + $this->init(); + + $input = $request->all(); + + $accountId = $this->company_gateway->getConfigField('accountId'); + + foreach (array_keys($input) as $key) { + if ('_id' == substr($key, -3)) { + $objectType = substr($key, 0, -3); + $objectId = $input[$key]; + break; + } + } + + if (! isset($objectType)) { + throw new Exception('Could not find object id parameter'); + } + + if ($objectType == 'credit_card') { + $payment_method = ClientGatewayToken::where('token', $objectId)->first(); + + if (! $paymentMethod) + throw new \Exception('Unknown payment method'); + + $source = $this->wepay->request('credit_card', array( + 'client_id' => config('ninja.wepay.client_id'), + 'client_secret' => config('ninja.wepay.client_secret'), + 'credit_card_id' => (int)$objectId, + )); + + if ($source->state == 'deleted') { + $payment_method->delete(); + } else { + //$this->paymentService->convertPaymentMethodFromWePay($source, null, $paymentMethod)->save(); + } + + return 'Processed successfully'; + } elseif ($objectType == 'account') { + if ($accountId != $objectId) { + throw new \Exception('Unknown account'); + } + + $wepayAccount = $this->wepay->request('account', array( + 'account_id' => (int)$objectId, + )); + + if ($wepayAccount->state == 'deleted') { + $this->company_gateway->delete(); + } else { + $config->state = $wepayAccount->state; + $this->company_gateway->setConfig($config); + $this->company_gateway->save(); + } + + return ['message' => 'Processed successfully']; + } elseif ($objectType == 'checkout') { + $payment = Payment::where('company_id', $this->company_gateway->company_id) + ->where('transaction_reference', '=', $objectId) + ->first(); + + if (! $payment) { + throw new Exception('Unknown payment'); + } + + if ($payment->is_deleted) { + throw new \Exception('Payment is deleted'); + } + + $checkout = $this->wepay->request('checkout', array( + 'checkout_id' => intval($objectId), + )); + + if ($checkout->state == 'captured') { + $payment->status_id = Payment::STATUS_COMPLETED; + $payment->save(); + } elseif ($checkout->state == 'cancelled') { + $payment->service()->deletePayment()->save(); + } elseif ($checkout->state == 'failed') { + $payment->status_id = Payment::STATUS_FAILED; + $payment->save(); + } + + return 'Processed successfully'; + } else { + return 'Ignoring event'; + } + + + + return true; + } From 3911a58d986e3fdfff998111001f9c8a4538de9f Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 19 Jun 2021 14:05:45 +1000 Subject: [PATCH 34/43] WePay JS --- app/Repositories/BaseRepository.php | 8 +- .../gateways/wepay/credit_card/pay.blade.php | 77 ++++++++++++++++++- .../wepay/includes/credit_card.blade.php | 2 +- 3 files changed, 82 insertions(+), 5 deletions(-) diff --git a/app/Repositories/BaseRepository.php b/app/Repositories/BaseRepository.php index 578cf0741b86..e6dfa1f00c6c 100644 --- a/app/Repositories/BaseRepository.php +++ b/app/Repositories/BaseRepository.php @@ -330,8 +330,6 @@ class BaseRepository $model = $model->calc()->getCredit(); - // $model->ledger()->updateCreditBalance(-1*($state['finished_amount'] - $state['starting_amount'])); - if (! $model->design_id) $model->design_id = $this->decodePrimaryKey($client->getSetting('credit_design_id')); @@ -339,12 +337,18 @@ class BaseRepository if ($model instanceof Quote) { + if (! $model->design_id) + $model->design_id = $this->decodePrimaryKey($client->getSetting('quote_design_id')); + $model = $model->calc()->getQuote(); } if ($model instanceof RecurringInvoice) { + if (! $model->design_id) + $model->design_id = $this->decodePrimaryKey($client->getSetting('invoice_design_id')); + $model = $model->calc()->getRecurringInvoice(); } diff --git a/resources/views/portal/ninja2020/gateways/wepay/credit_card/pay.blade.php b/resources/views/portal/ninja2020/gateways/wepay/credit_card/pay.blade.php index dc0163c5bfd9..ad470d9d3cf8 100644 --- a/resources/views/portal/ninja2020/gateways/wepay/credit_card/pay.blade.php +++ b/resources/views/portal/ninja2020/gateways/wepay/credit_card/pay.blade.php @@ -84,9 +84,82 @@ }; let errors = document.getElementById('errors'); + + /* handle the switch between token and cc */ + Array + .from(document.getElementsByClassName('toggle-payment-with-token')) + .forEach((element) => element.addEventListener('click', (e) => { + document + .getElementById('save-card--container').style.display = 'none'; + document + .getElementById('wepay--credit-card-container').style.display = 'none'; + + document + .getElementById('token').value = e.target.dataset.token; + })); + + let payWithCreditCardToggle = document.getElementById('toggle-payment-with-credit-card'); + + if (payWithCreditCardToggle) { + payWithCreditCardToggle + .addEventListener('click', () => { + document + .getElementById('save-card--container').style.display = 'grid'; + document + .getElementById('wepay--credit-card-container').style.display = 'flex'; + + document + .getElementById('token').value = null; + }); + } + /* handle the switch between token and cc */ + + /* Attach store card value to form */ + let storeCard = document.querySelector('input[name=token-billing-checkbox]:checked'); + + if (storeCard) { + document.getElementById("store_card").value = storeCard.value; + } + /* Attach store card value to form */ + + /* Pay Now Button */ + let payNowButton = document.getElementById('pay-now'); + + if (payNowButton) { + payNowButton + .addEventListener('click', (e) => { + let token = document.getElementById('token').value; + + if(token){ + handleTokenPayment($token) + } + else{ + handleCardPayment(); + } + }); + + } + /* Pay Now Button */ + + + + + function handleTokenPayment($token) + { + + document.querySelector('input[name="credit_card_id"]').value = token; + document.getElementById('server_response').submit(); + + } + + + + + + // Attach the event to the DOM - addEvent(document.getElementById('card_button'), 'click', function() { + function handleCardPayment(){ var myCard = $('#my-card'); @@ -162,7 +235,7 @@ } }); - }); + } })(); diff --git a/resources/views/portal/ninja2020/gateways/wepay/includes/credit_card.blade.php b/resources/views/portal/ninja2020/gateways/wepay/includes/credit_card.blade.php index a2c546846347..94c621a6f4f3 100644 --- a/resources/views/portal/ninja2020/gateways/wepay/includes/credit_card.blade.php +++ b/resources/views/portal/ninja2020/gateways/wepay/includes/credit_card.blade.php @@ -1,5 +1,5 @@
+ style="display: flex!important; justify-content: center!important;" id="wepay--credit-card-container">
From d90b8849226adaa298feab79b606f1efc000ed87 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 19 Jun 2021 14:58:45 +1000 Subject: [PATCH 35/43] WePay process credit/card --- app/PaymentDrivers/WePay/CreditCard.php | 88 +++++++++++++++---- .../gateways/wepay/credit_card/pay.blade.php | 32 +++---- 2 files changed, 88 insertions(+), 32 deletions(-) diff --git a/app/PaymentDrivers/WePay/CreditCard.php b/app/PaymentDrivers/WePay/CreditCard.php index b88da429fa89..b3249cf5e647 100644 --- a/app/PaymentDrivers/WePay/CreditCard.php +++ b/app/PaymentDrivers/WePay/CreditCard.php @@ -13,6 +13,7 @@ namespace App\PaymentDrivers\WePay; use App\Exceptions\PaymentFailed; +use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest; use App\Models\GatewayType; use App\PaymentDrivers\WePayPaymentDriver; @@ -98,25 +99,80 @@ class CreditCard public function paymentResponse(PaymentResponseRequest $request) { - -nlog($request->all()); - // // charge the credit card - // $response = $wepay->request('checkout/create', array( - // 'account_id' => $account_id, - // 'amount' => '25.50', - // 'currency' => 'USD', - // 'short_description' => 'A vacation home rental', - // 'type' => 'goods', - // 'payment_method' => array( - // 'type' => 'credit_card', - // 'credit_card' => array( - // 'id' => $credit_card_id - // ) - // ) - // )); + // USD, CAD, and GBP. + nlog($request->all()); + // charge the credit card + $response = $this->wepay_payment_driver->wepay->request('checkout/create', array( + 'account_id' => $this->wepay_payment_driver->company_gateway->getConfigField('accountId'), + 'amount' => $this->wepay_payment_driver->payment_hash->data->amount_with_fee, + 'currency' => $this->wepay_payment_driver->client->getCurrencyCode(), + 'short_description' => 'A vacation home rental', + 'type' => 'goods', + 'payment_method' => array( + 'type' => 'credit_card', + 'credit_card' => array( + 'id' => $data['token'] + ) + ) + )); } +/* +https://developer.wepay.com/api/api-calls/checkout +{ + "checkout_id": 649945633, + "account_id": 1548718026, + "type": "donation", + "short_description": "test checkout", + "currency": "USD", + "amount": 20, + "state": "authorized", + "soft_descriptor": "WPY*Wolverine", + "auto_release": true, + "create_time": 1463589958, + "gross": 20.88, + "reference_id": null, + "callback_uri": null, + "long_description": null, + "delivery_type": null, + "initiated_by": "merchant", + "in_review": false, + "fee": { + "app_fee": 0, + "processing_fee": 0.88, + "fee_payer": "payer" + }, + "chargeback": { + "amount_charged_back": 0, + "dispute_uri": null + }, + "refund": { + "amount_refunded": 0, + "refund_reason": null + }, + "payment_method": { + "type": "credit_card", + "credit_card": { + "id": 1684847614, + "data": { + "emv_receipt": null, + "signature_url": null + }, + "auto_release": false + } + }, + "hosted_checkout": null, + "payer": { + "email": "test@example.com", + "name": "Mr Smith", + "home_address": null + }, + "npo_information": null, + "payment_error": null +} + */ + private function storePaymentMethod($response, $payment_method_id) { diff --git a/resources/views/portal/ninja2020/gateways/wepay/credit_card/pay.blade.php b/resources/views/portal/ninja2020/gateways/wepay/credit_card/pay.blade.php index ad470d9d3cf8..99ada5117688 100644 --- a/resources/views/portal/ninja2020/gateways/wepay/credit_card/pay.blade.php +++ b/resources/views/portal/ninja2020/gateways/wepay/credit_card/pay.blade.php @@ -10,13 +10,13 @@
@csrf - + - +
@@ -114,14 +114,6 @@ } /* handle the switch between token and cc */ - /* Attach store card value to form */ - let storeCard = document.querySelector('input[name=token-billing-checkbox]:checked'); - - if (storeCard) { - document.getElementById("store_card").value = storeCard.value; - } - /* Attach store card value to form */ - /* Pay Now Button */ let payNowButton = document.getElementById('pay-now'); @@ -130,8 +122,16 @@ .addEventListener('click', (e) => { let token = document.getElementById('token').value; + /* Attach store card value to form */ + let storeCard = document.querySelector('input[name=token-billing-checkbox]:checked'); + + if (storeCard) { + document.getElementById("store_card").value = storeCard.value; + } + /* Attach store card value to form */ + if(token){ - handleTokenPayment($token) + handleTokenPayment(token) } else{ handleCardPayment(); @@ -144,11 +144,11 @@ - function handleTokenPayment($token) + function handleTokenPayment(token) { - document.querySelector('input[name="credit_card_id"]').value = token; - document.getElementById('server_response').submit(); + document.querySelector('input[name="token"]').value = token; + document.getElementById('server-response').submit(); } @@ -230,8 +230,8 @@ var token = data.credit_card_id; - document.querySelector('input[name="credit_card_id"]').value = token; - document.getElementById('server_response').submit(); + document.querySelector('input[name="token"]').value = token; + document.getElementById('server-response').submit(); } }); From e63a7f9875e1b99b40db40144fe3f8b3bc3e675d Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 19 Jun 2021 17:31:05 +1000 Subject: [PATCH 36/43] WePay checkout --- app/PaymentDrivers/WePay/CreditCard.php | 57 ++++++++++++++++++- app/PaymentDrivers/WePayPaymentDriver.php | 8 ++- .../gateways/wepay/credit_card/pay.blade.php | 3 + 3 files changed, 65 insertions(+), 3 deletions(-) diff --git a/app/PaymentDrivers/WePay/CreditCard.php b/app/PaymentDrivers/WePay/CreditCard.php index b3249cf5e647..a927692648f1 100644 --- a/app/PaymentDrivers/WePay/CreditCard.php +++ b/app/PaymentDrivers/WePay/CreditCard.php @@ -99,9 +99,40 @@ class CreditCard public function paymentResponse(PaymentResponseRequest $request) { + nlog("payment response"); + $this->wepay_payment_driver->init(); + + //it could be an existing token or a new credit_card_id that needs to be converted into a wepay token + if($request->has('credit_card_id') && $request->input('credit_card_id')) + { + nlog("authorize the card first!"); + + $response = $this->wepay_payment_driver->wepay->request('credit_card/authorize', array( + 'client_id' => config('ninja.wepay.client_id'), + 'client_secret' => config('ninja.wepay.client_secret'), + 'credit_card_id' => (int)$request->input('credit_card_id'), + )); + + $credit_card_id = (int)$response->credit_card_id; + + if(in_array($response->state, ['new', 'authorized']) && boolval($request->input('store_card'))){ + + $this->storePaymentMethod($response, GatewayType::CREDIT_CARD); + + } + + } + else { + + $credit_card_id = (int)$request->input('token'); + + } + // USD, CAD, and GBP. nlog($request->all()); // charge the credit card + nlog($this->wepay_payment_driver->wepay); + $response = $this->wepay_payment_driver->wepay->request('checkout/create', array( 'account_id' => $this->wepay_payment_driver->company_gateway->getConfigField('accountId'), 'amount' => $this->wepay_payment_driver->payment_hash->data->amount_with_fee, @@ -111,13 +142,37 @@ class CreditCard 'payment_method' => array( 'type' => 'credit_card', 'credit_card' => array( - 'id' => $data['token'] + 'id' => $credit_card_id ) ) )); + print_r($response); + + if(in_array($response->state, ['authorized', 'captured'])){ + //success + nlog("success"); + } + + if(in_array($response->state, ['released', 'cancelled', 'failed', 'expired'])){ + //some type of failure + nlog("failure"); + } + } +/* +new The checkout was created by the application. This state typically indicates that checkouts created in WePay's hosted checkout flow are waiting for the payer to submit their information. +authorized The payer entered their payment info and confirmed the payment on WePay. WePay has successfully charged the card. +captured The payment has been reserved from the payer. +released The payment has been credited to the payee account. Note that the released state may be active although there are active partial refunds or partial chargebacks. +cancelled The payment has been cancelled by the payer, payee, or application. +refunded The payment was captured and then refunded by the payer, payee, or application. The payment has been debited from the payee account. +charged back The payment has been charged back by the payer and the payment has been debited from the payee account. +failed The payment has failed. +expired Checkouts expire if they remain in the new state for more than 30 minutes (e.g., they have been abandoned). + */ + /* https://developer.wepay.com/api/api-calls/checkout { diff --git a/app/PaymentDrivers/WePayPaymentDriver.php b/app/PaymentDrivers/WePayPaymentDriver.php index 0ef6c063bdad..d5f976bef28f 100644 --- a/app/PaymentDrivers/WePayPaymentDriver.php +++ b/app/PaymentDrivers/WePayPaymentDriver.php @@ -65,8 +65,8 @@ class WePayPaymentDriver extends BaseDriver if ($this->company_gateway) $this->wepay = new WePay($this->company_gateway->getConfigField('accessToken')); - - $this->wepay = new WePay(null); + else + $this->wepay = new WePay(null); return $this; @@ -148,11 +148,15 @@ class WePayPaymentDriver extends BaseDriver public function processPaymentView(array $data) { + $this->init(); + return $this->payment_method->paymentView($data); //this is your custom implementation from here } public function processPaymentResponse($request) { + $this->init(); + return $this->payment_method->paymentResponse($request); //this is your custom implementation from here } diff --git a/resources/views/portal/ninja2020/gateways/wepay/credit_card/pay.blade.php b/resources/views/portal/ninja2020/gateways/wepay/credit_card/pay.blade.php index 99ada5117688..572fd27af20e 100644 --- a/resources/views/portal/ninja2020/gateways/wepay/credit_card/pay.blade.php +++ b/resources/views/portal/ninja2020/gateways/wepay/credit_card/pay.blade.php @@ -17,6 +17,7 @@ + @@ -147,6 +148,7 @@ function handleTokenPayment(token) { + document.querySelector('input[name="credit_card_id"]').value = null; document.querySelector('input[name="token"]').value = token; document.getElementById('server-response').submit(); @@ -230,6 +232,7 @@ var token = data.credit_card_id; + document.querySelector('input[name="credit_card_id"]').value = null; document.querySelector('input[name="token"]').value = token; document.getElementById('server-response').submit(); From 886e006de7850ceb88f3b06b98c30b9539195b02 Mon Sep 17 00:00:00 2001 From: = Date: Sun, 20 Jun 2021 09:57:19 +1000 Subject: [PATCH 37/43] Handle payment success/failure with WePay --- app/Models/SystemLog.php | 2 +- app/PaymentDrivers/WePay/CreditCard.php | 77 ++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 4 deletions(-) diff --git a/app/Models/SystemLog.php b/app/Models/SystemLog.php index 7e0c3b3407a7..7933b7f13c3b 100644 --- a/app/Models/SystemLog.php +++ b/app/Models/SystemLog.php @@ -65,9 +65,9 @@ class SystemLog extends Model const TYPE_CHECKOUT = 304; const TYPE_AUTHORIZE = 305; const TYPE_CUSTOM = 306; + const TYPE_BRAINTREE = 307; const TYPE_WEPAY = 309; - const TYPE_BRAINTREE = 307; const TYPE_QUOTA_EXCEEDED = 400; const TYPE_UPSTREAM_FAILURE = 401; diff --git a/app/PaymentDrivers/WePay/CreditCard.php b/app/PaymentDrivers/WePay/CreditCard.php index a927692648f1..104efa8e28a0 100644 --- a/app/PaymentDrivers/WePay/CreditCard.php +++ b/app/PaymentDrivers/WePay/CreditCard.php @@ -14,7 +14,12 @@ namespace App\PaymentDrivers\WePay; use App\Exceptions\PaymentFailed; use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest; +use App\Jobs\Mail\PaymentFailureMailer; +use App\Jobs\Util\SystemLogger; use App\Models\GatewayType; +use App\Models\Payment; +use App\Models\PaymentType; +use App\Models\SystemLog; use App\PaymentDrivers\WePayPaymentDriver; class CreditCard @@ -131,8 +136,6 @@ class CreditCard // USD, CAD, and GBP. nlog($request->all()); // charge the credit card - nlog($this->wepay_payment_driver->wepay); - $response = $this->wepay_payment_driver->wepay->request('checkout/create', array( 'account_id' => $this->wepay_payment_driver->company_gateway->getConfigField('accountId'), 'amount' => $this->wepay_payment_driver->payment_hash->data->amount_with_fee, @@ -147,16 +150,32 @@ class CreditCard ) )); - print_r($response); + /* Merge all data and store in the payment hash*/ + $state = [ + 'server_response' => $response, + 'payment_hash' => $request->payment_hash, + ]; + + $state = array_merge($state, $request->all()); + $this->wepay_payment_driver->payment_hash->data = array_merge((array) $this->wepay_payment_driver->payment_hash->data, $state); + $this->wepay_payment_driver->payment_hash->save(); + if(in_array($response->state, ['authorized', 'captured'])){ //success nlog("success"); + $payment_status = ($response->status == 'authorized') ? Payment::STATUS_COMPLETED : Payment:STATUS_PENDING; + + $this->processSuccessfulPayment($response, $payment_status); } if(in_array($response->state, ['released', 'cancelled', 'failed', 'expired'])){ //some type of failure nlog("failure"); + + $payment_status = ($response->status == 'cancelled') ? Payment::STATUS_CANCELLED : Payment:STATUS_FAILED; + + $this->processUnSuccessfulPayment($response, $payment_status); } } @@ -228,6 +247,58 @@ https://developer.wepay.com/api/api-calls/checkout } */ + private function processSuccessfulPayment($response, $payment_status) + { + + $data = [ + 'payment_type' => PaymentType::CREDIT_CARD_OTHER, + 'amount' => $response->gross, + 'transaction_reference' => $response->checkout_id, + 'gateway_type_id' => GatewayType::CREDIT_CARD, + ]; + + $payment = $this->wepay_payment_driver->createPayment($data, $payment_status); + + SystemLogger::dispatch( + ['response' => $this->wepay_payment_driver->payment_hash->data->server_response, 'data' => $data], + SystemLog::CATEGORY_GATEWAY_RESPONSE, + SystemLog::EVENT_GATEWAY_SUCCESS, + SystemLog::TYPE_WEPAY, + $this->wepay_payment_driver->client, + $this->wepay_payment_driver->client->company, + ); + + return redirect()->route('client.payments.show', ['payment' => $this->wepay_payment_driver->encodePrimaryKey($payment->id)]); + } + + private function processUnSuccessfulPayment($response, $payment_status) + { + PaymentFailureMailer::dispatch($this->wepay_payment_driver->client, $response->state, $this->wepay_payment_driver->client->company, $response->gross); + + PaymentFailureMailer::dispatch( + $this->wepay_payment_driver->client, + $response, + $this->wepay_payment_driver->client->company, + $response->gross + ); + + $message = [ + 'server_response' => $response, + 'data' => $this->wepay_payment_driver->payment_hash->data, + ]; + + SystemLogger::dispatch( + $message, + SystemLog::CATEGORY_GATEWAY_RESPONSE, + SystemLog::EVENT_GATEWAY_FAILURE, + SystemLog::TYPE_WEPAY, + $this->wepay_payment_driver->client, + $this->wepay_payment_driver->client->company, + ); + + throw new PaymentFailed('Failed to process the payment.', 500); + } + private function storePaymentMethod($response, $payment_method_id) { From 1cea863e7741db535a1512a4275f416fde0674e7 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sun, 20 Jun 2021 13:06:17 +1000 Subject: [PATCH 38/43] Fixes for credit card payment --- app/PaymentDrivers/WePay/CreditCard.php | 4 ++-- .../gateways/wepay/credit_card/pay.blade.php | 20 +++++++++++-------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/app/PaymentDrivers/WePay/CreditCard.php b/app/PaymentDrivers/WePay/CreditCard.php index 104efa8e28a0..acbd2540835f 100644 --- a/app/PaymentDrivers/WePay/CreditCard.php +++ b/app/PaymentDrivers/WePay/CreditCard.php @@ -164,7 +164,7 @@ class CreditCard if(in_array($response->state, ['authorized', 'captured'])){ //success nlog("success"); - $payment_status = ($response->status == 'authorized') ? Payment::STATUS_COMPLETED : Payment:STATUS_PENDING; + $payment_status = $response->status == 'authorized' ? Payment::STATUS_COMPLETED : Payment::STATUS_PENDING; $this->processSuccessfulPayment($response, $payment_status); } @@ -173,7 +173,7 @@ class CreditCard //some type of failure nlog("failure"); - $payment_status = ($response->status == 'cancelled') ? Payment::STATUS_CANCELLED : Payment:STATUS_FAILED; + $payment_status = $response->status == 'cancelled' ? Payment::STATUS_CANCELLED : Payment::STATUS_FAILED; $this->processUnSuccessfulPayment($response, $payment_status); } diff --git a/resources/views/portal/ninja2020/gateways/wepay/credit_card/pay.blade.php b/resources/views/portal/ninja2020/gateways/wepay/credit_card/pay.blade.php index 572fd27af20e..6be603104f2a 100644 --- a/resources/views/portal/ninja2020/gateways/wepay/credit_card/pay.blade.php +++ b/resources/views/portal/ninja2020/gateways/wepay/credit_card/pay.blade.php @@ -121,6 +121,13 @@ if (payNowButton) { payNowButton .addEventListener('click', (e) => { + + payNowButton.disabled = true; + + payNowButton.querySelector('svg').classList.remove('hidden'); + payNowButton.querySelector('span').classList.add('hidden'); + + let token = document.getElementById('token').value; /* Attach store card value to form */ @@ -164,6 +171,7 @@ function handleCardPayment(){ var myCard = $('#my-card'); + let payNowButton = document.getElementById('pay-now'); if(document.getElementById('cardholder_name') == "") { document.getElementById('cardholder_name').focus(); @@ -196,11 +204,7 @@ return; } - cardButton = document.getElementById('card_button'); - cardButton.disabled = true; - cardButton.querySelector('svg').classList.remove('hidden'); - cardButton.querySelector('span').classList.add('hidden'); var userName = [valueById('cardholder_name')].join(' '); response = WePay.credit_card.create({ @@ -219,10 +223,10 @@ if (data.error) { //console.log(data); // handle error response error_description - cardButton = document.getElementById('card_button'); - cardButton.disabled = false; - cardButton.querySelector('svg').classList.add('hidden'); - cardButton.querySelector('span').classList.remove('hidden'); + + payNowButton.disabled = false; + payNowButton.querySelector('svg').classList.add('hidden'); + payNowButton.querySelector('span').classList.remove('hidden'); errors.textContent = ''; errors.textContent = data.error_description; From 2075c4e8c167c669af41515b5fcc15b5935186e6 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sun, 20 Jun 2021 13:36:58 +1000 Subject: [PATCH 39/43] Testing WePay payment with Credit Card --- app/PaymentDrivers/WePay/CreditCard.php | 13 ++++++++----- .../gateways/wepay/credit_card/pay.blade.php | 4 ++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/app/PaymentDrivers/WePay/CreditCard.php b/app/PaymentDrivers/WePay/CreditCard.php index acbd2540835f..9ac1dc0fa789 100644 --- a/app/PaymentDrivers/WePay/CreditCard.php +++ b/app/PaymentDrivers/WePay/CreditCard.php @@ -120,6 +120,9 @@ class CreditCard $credit_card_id = (int)$response->credit_card_id; +nlog($response->state); +nlog(boolval($request->input('store_card'))); + if(in_array($response->state, ['new', 'authorized']) && boolval($request->input('store_card'))){ $this->storePaymentMethod($response, GatewayType::CREDIT_CARD); @@ -164,16 +167,16 @@ class CreditCard if(in_array($response->state, ['authorized', 'captured'])){ //success nlog("success"); - $payment_status = $response->status == 'authorized' ? Payment::STATUS_COMPLETED : Payment::STATUS_PENDING; + $payment_status = $response->state == 'authorized' ? Payment::STATUS_COMPLETED : Payment::STATUS_PENDING; - $this->processSuccessfulPayment($response, $payment_status); + return $this->processSuccessfulPayment($response, $payment_status); } if(in_array($response->state, ['released', 'cancelled', 'failed', 'expired'])){ //some type of failure nlog("failure"); - $payment_status = $response->status == 'cancelled' ? Payment::STATUS_CANCELLED : Payment::STATUS_FAILED; + $payment_status = $response->state == 'cancelled' ? Payment::STATUS_CANCELLED : Payment::STATUS_FAILED; $this->processUnSuccessfulPayment($response, $payment_status); } @@ -252,7 +255,7 @@ https://developer.wepay.com/api/api-calls/checkout $data = [ 'payment_type' => PaymentType::CREDIT_CARD_OTHER, - 'amount' => $response->gross, + 'amount' => $response->amount, 'transaction_reference' => $response->checkout_id, 'gateway_type_id' => GatewayType::CREDIT_CARD, ]; @@ -301,7 +304,7 @@ https://developer.wepay.com/api/api-calls/checkout private function storePaymentMethod($response, $payment_method_id) { - +nlog("storing card"); $payment_meta = new \stdClass; $payment_meta->exp_month = (string) $response->expiration_month; $payment_meta->exp_year = (string) $response->expiration_year; diff --git a/resources/views/portal/ninja2020/gateways/wepay/credit_card/pay.blade.php b/resources/views/portal/ninja2020/gateways/wepay/credit_card/pay.blade.php index 6be603104f2a..8994199d07ea 100644 --- a/resources/views/portal/ninja2020/gateways/wepay/credit_card/pay.blade.php +++ b/resources/views/portal/ninja2020/gateways/wepay/credit_card/pay.blade.php @@ -236,8 +236,8 @@ var token = data.credit_card_id; - document.querySelector('input[name="credit_card_id"]').value = null; - document.querySelector('input[name="token"]').value = token; + document.querySelector('input[name="credit_card_id"]').value = token; + document.querySelector('input[name="token"]').value = null; document.getElementById('server-response').submit(); } From 4eda6b119018d81fb4281fdf0f6167ad6f1d2f0c Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sun, 20 Jun 2021 16:24:37 +1000 Subject: [PATCH 40/43] Refactor common code WePay --- app/PaymentDrivers/WePay/ACH.php | 124 +++++++---------------- app/PaymentDrivers/WePay/CreditCard.php | 63 ++---------- app/PaymentDrivers/WePay/WePayCommon.php | 76 ++++++++++++++ 3 files changed, 121 insertions(+), 142 deletions(-) create mode 100644 app/PaymentDrivers/WePay/WePayCommon.php diff --git a/app/PaymentDrivers/WePay/ACH.php b/app/PaymentDrivers/WePay/ACH.php index 23f8cb37c37c..d7489cac9d00 100644 --- a/app/PaymentDrivers/WePay/ACH.php +++ b/app/PaymentDrivers/WePay/ACH.php @@ -15,13 +15,17 @@ namespace App\PaymentDrivers\WePay; use App\Exceptions\PaymentFailed; use App\Models\ClientGatewayToken; use App\Models\GatewayType; +use App\Models\Payment; use App\PaymentDrivers\WePayPaymentDriver; +use App\PaymentDrivers\WePay\WePayCommon; use App\Utils\Traits\MakesHash; use Illuminate\Http\Request; +use Illuminate\Support\Str; class ACH { use MakesHash; + use WePayCommon; public $wepay_payment_driver; @@ -193,102 +197,50 @@ class ACH if($token_meta->state != "authorized") return redirect()->route('client.payment_methods.verification', ['payment_method' => $token->hashed_id, 'method' => GatewayType::BANK_TRANSFER]); + $response = $this->wepay_payment_driver->wepay->request('checkout/create', array( + 'unique_id' => Str::random(40), + 'account_id' => $this->wepay_payment_driver->company_gateway->getConfigField('accountId'), + 'amount' => $this->wepay_payment_driver->payment_hash->data->amount_with_fee, + 'currency' => $this->wepay_payment_driver->client->getCurrencyCode(), + 'short_description' => 'A vacation home rental', + 'type' => 'goods', + 'payment_method' => array( + 'type' => 'payment_bank', + 'payment_bank' => array( + 'id' => $token->token + ) + ) + )); + /* Merge all data and store in the payment hash*/ + $state = [ + 'server_response' => $response, + 'payment_hash' => $request->payment_hash, + ]; + $state = array_merge($state, $request->all()); + $this->wepay_payment_driver->payment_hash->data = array_merge((array) $this->wepay_payment_driver->payment_hash->data, $state); + $this->wepay_payment_driver->payment_hash->save(); + if(in_array($response->state, ['authorized', 'captured'])){ + //success + nlog("success"); + $payment_status = $response->state == 'authorized' ? Payment::STATUS_COMPLETED : Payment::STATUS_PENDING; + return $this->processSuccessfulPayment($response, $payment_status, GatewayType::BANK_TRANSFER); + } -// response = wepay.call('/checkout/create', { -// 'account_id': account_id, -// 'amount': '25.50', -// 'short_description': 'A vacation home rental', -// 'type': 'GOODS', -// 'payment_method': { -// 'type': 'payment_bank', -// 'payment_bank': { -// 'id': 20000061 -// } -// } -// }) + if(in_array($response->state, ['released', 'cancelled', 'failed', 'expired'])){ + //some type of failure + nlog("failure"); + $payment_status = $response->state == 'cancelled' ? Payment::STATUS_CANCELLED : Payment::STATUS_FAILED; + $this->processUnSuccessfulPayment($response, $payment_status); + } - - - - - - - - - - - - // $this->stripe->init(); - - // $source = ClientGatewayToken::query() - // ->where('id', $this->decodePrimaryKey($request->source)) - // ->where('company_id', auth('contact')->user()->client->company->id) - // ->first(); - - // if (!$source) { - // throw new PaymentFailed(ctrans('texts.payment_token_not_found'), 401); - // } - - // $state = [ - // 'payment_method' => $request->payment_method_id, - // 'gateway_type_id' => $request->company_gateway_id, - // 'amount' => $this->stripe->convertToStripeAmount($request->amount, $this->stripe->client->currency()->precision), - // 'currency' => $request->currency, - // 'customer' => $request->customer, - // ]; - - // $state = array_merge($state, $request->all()); - // $state['source'] = $source->token; - - // $this->stripe->payment_hash->data = array_merge((array)$this->stripe->payment_hash->data, $state); - // $this->stripe->payment_hash->save(); - - // try { - // $state['charge'] = \Stripe\Charge::create([ - // 'amount' => $state['amount'], - // 'currency' => $state['currency'], - // 'customer' => $state['customer'], - // 'source' => $state['source'], - // ], $this->stripe->stripe_connect_auth); - - // $state = array_merge($state, $request->all()); - - // $this->stripe->payment_hash->data = array_merge((array)$this->stripe->payment_hash->data, $state); - // $this->stripe->payment_hash->save(); - - // if ($state['charge']->status === 'pending' && is_null($state['charge']->failure_message)) { - // return $this->processPendingPayment($state); - // } - - // return $this->processUnsuccessfulPayment($state); - // } catch (Exception $e) { - // if ($e instanceof CardException) { - // return redirect()->route('client.payment_methods.verification', ['id' => ClientGatewayToken::first()->hashed_id, 'method' => GatewayType::BANK_TRANSFER]); - // } - - // throw new PaymentFailed($e->getMessage(), $e->getCode()); - // } } - - - - - - - - - - - - - private function storePaymentMethod($response, $payment_method_id) { diff --git a/app/PaymentDrivers/WePay/CreditCard.php b/app/PaymentDrivers/WePay/CreditCard.php index 9ac1dc0fa789..7f723ca45219 100644 --- a/app/PaymentDrivers/WePay/CreditCard.php +++ b/app/PaymentDrivers/WePay/CreditCard.php @@ -21,9 +21,13 @@ use App\Models\Payment; use App\Models\PaymentType; use App\Models\SystemLog; use App\PaymentDrivers\WePayPaymentDriver; +use App\PaymentDrivers\WePay\WePayCommon; +use Illuminate\Support\Str; class CreditCard { +use WePayCommon; + public $wepay_payment_driver; public function __construct(WePayPaymentDriver $wepay_payment_driver) @@ -98,6 +102,7 @@ class CreditCard public function paymentView(array $data) { $data['gateway'] = $this->wepay_payment_driver; + $data['description'] = ctrans('texts.invoices') . ': ' . collect($data['invoices'])->pluck('invoice_number'); return render('gateways.wepay.credit_card.pay', $data); } @@ -105,7 +110,6 @@ class CreditCard public function paymentResponse(PaymentResponseRequest $request) { nlog("payment response"); - $this->wepay_payment_driver->init(); //it could be an existing token or a new credit_card_id that needs to be converted into a wepay token if($request->has('credit_card_id') && $request->input('credit_card_id')) @@ -120,9 +124,6 @@ class CreditCard $credit_card_id = (int)$response->credit_card_id; -nlog($response->state); -nlog(boolval($request->input('store_card'))); - if(in_array($response->state, ['new', 'authorized']) && boolval($request->input('store_card'))){ $this->storePaymentMethod($response, GatewayType::CREDIT_CARD); @@ -140,6 +141,7 @@ nlog(boolval($request->input('store_card'))); nlog($request->all()); // charge the credit card $response = $this->wepay_payment_driver->wepay->request('checkout/create', array( + 'unique_id' => Str::random(40), 'account_id' => $this->wepay_payment_driver->company_gateway->getConfigField('accountId'), 'amount' => $this->wepay_payment_driver->payment_hash->data->amount_with_fee, 'currency' => $this->wepay_payment_driver->client->getCurrencyCode(), @@ -169,7 +171,7 @@ nlog(boolval($request->input('store_card'))); nlog("success"); $payment_status = $response->state == 'authorized' ? Payment::STATUS_COMPLETED : Payment::STATUS_PENDING; - return $this->processSuccessfulPayment($response, $payment_status); + return $this->processSuccessfulPayment($response, $payment_status, GatewayType::CREDIT_CARD); } if(in_array($response->state, ['released', 'cancelled', 'failed', 'expired'])){ @@ -250,57 +252,6 @@ https://developer.wepay.com/api/api-calls/checkout } */ - private function processSuccessfulPayment($response, $payment_status) - { - - $data = [ - 'payment_type' => PaymentType::CREDIT_CARD_OTHER, - 'amount' => $response->amount, - 'transaction_reference' => $response->checkout_id, - 'gateway_type_id' => GatewayType::CREDIT_CARD, - ]; - - $payment = $this->wepay_payment_driver->createPayment($data, $payment_status); - - SystemLogger::dispatch( - ['response' => $this->wepay_payment_driver->payment_hash->data->server_response, 'data' => $data], - SystemLog::CATEGORY_GATEWAY_RESPONSE, - SystemLog::EVENT_GATEWAY_SUCCESS, - SystemLog::TYPE_WEPAY, - $this->wepay_payment_driver->client, - $this->wepay_payment_driver->client->company, - ); - - return redirect()->route('client.payments.show', ['payment' => $this->wepay_payment_driver->encodePrimaryKey($payment->id)]); - } - - private function processUnSuccessfulPayment($response, $payment_status) - { - PaymentFailureMailer::dispatch($this->wepay_payment_driver->client, $response->state, $this->wepay_payment_driver->client->company, $response->gross); - - PaymentFailureMailer::dispatch( - $this->wepay_payment_driver->client, - $response, - $this->wepay_payment_driver->client->company, - $response->gross - ); - - $message = [ - 'server_response' => $response, - 'data' => $this->wepay_payment_driver->payment_hash->data, - ]; - - SystemLogger::dispatch( - $message, - SystemLog::CATEGORY_GATEWAY_RESPONSE, - SystemLog::EVENT_GATEWAY_FAILURE, - SystemLog::TYPE_WEPAY, - $this->wepay_payment_driver->client, - $this->wepay_payment_driver->client->company, - ); - - throw new PaymentFailed('Failed to process the payment.', 500); - } private function storePaymentMethod($response, $payment_method_id) { diff --git a/app/PaymentDrivers/WePay/WePayCommon.php b/app/PaymentDrivers/WePay/WePayCommon.php new file mode 100644 index 000000000000..54a72e98fd43 --- /dev/null +++ b/app/PaymentDrivers/WePay/WePayCommon.php @@ -0,0 +1,76 @@ + PaymentType::CREDIT_CARD_OTHER, + 'amount' => $response->amount, + 'transaction_reference' => $response->checkout_id, + 'gateway_type_id' => $gateway_type, + ]; + + $payment = $this->wepay_payment_driver->createPayment($data, $payment_status); + + SystemLogger::dispatch( + ['response' => $this->wepay_payment_driver->payment_hash->data->server_response, 'data' => $data], + SystemLog::CATEGORY_GATEWAY_RESPONSE, + SystemLog::EVENT_GATEWAY_SUCCESS, + SystemLog::TYPE_WEPAY, + $this->wepay_payment_driver->client, + $this->wepay_payment_driver->client->company, + ); + + return redirect()->route('client.payments.show', ['payment' => $this->wepay_payment_driver->encodePrimaryKey($payment->id)]); + } + + private function processUnSuccessfulPayment($response, $payment_status) + { + PaymentFailureMailer::dispatch($this->wepay_payment_driver->client, $response->state, $this->wepay_payment_driver->client->company, $response->amount); + + PaymentFailureMailer::dispatch( + $this->wepay_payment_driver->client, + $response, + $this->wepay_payment_driver->client->company, + $response->gross + ); + + $message = [ + 'server_response' => $response, + 'data' => $this->wepay_payment_driver->payment_hash->data, + ]; + + SystemLogger::dispatch( + $message, + SystemLog::CATEGORY_GATEWAY_RESPONSE, + SystemLog::EVENT_GATEWAY_FAILURE, + SystemLog::TYPE_WEPAY, + $this->wepay_payment_driver->client, + $this->wepay_payment_driver->client->company, + ); + + throw new PaymentFailed('Failed to process the payment.', 500); + } + +} From f2da4d798543c5727ad6fa384190a1aceefc1855 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sun, 20 Jun 2021 16:53:33 +1000 Subject: [PATCH 41/43] Delete credit cards from wepay --- app/PaymentDrivers/WePay/ACH.php | 1 + app/PaymentDrivers/WePay/CreditCard.php | 2 ++ app/PaymentDrivers/WePayPaymentDriver.php | 21 +++++++++++++++++++++ 3 files changed, 24 insertions(+) diff --git a/app/PaymentDrivers/WePay/ACH.php b/app/PaymentDrivers/WePay/ACH.php index d7489cac9d00..a6580b22c836 100644 --- a/app/PaymentDrivers/WePay/ACH.php +++ b/app/PaymentDrivers/WePay/ACH.php @@ -198,6 +198,7 @@ class ACH return redirect()->route('client.payment_methods.verification', ['payment_method' => $token->hashed_id, 'method' => GatewayType::BANK_TRANSFER]); $response = $this->wepay_payment_driver->wepay->request('checkout/create', array( + 'callback_uri' => route('payment_webhook', ['company_key' => $this->wepay_payment_driver->company_gateway->company->company_key, 'company_gateway_id' => $this->wepay_payment_driver->company_gateway->hashed_id]), 'unique_id' => Str::random(40), 'account_id' => $this->wepay_payment_driver->company_gateway->getConfigField('accountId'), 'amount' => $this->wepay_payment_driver->payment_hash->data->amount_with_fee, diff --git a/app/PaymentDrivers/WePay/CreditCard.php b/app/PaymentDrivers/WePay/CreditCard.php index 7f723ca45219..3bdd17876dab 100644 --- a/app/PaymentDrivers/WePay/CreditCard.php +++ b/app/PaymentDrivers/WePay/CreditCard.php @@ -117,6 +117,7 @@ use WePayCommon; nlog("authorize the card first!"); $response = $this->wepay_payment_driver->wepay->request('credit_card/authorize', array( + 'callback_uri' => route('payment_webhook', ['company_key' => $this->wepay_payment_driver->company_gateway->company->company_key, 'company_gateway_id' => $this->wepay_payment_driver->company_gateway->hashed_id]), 'client_id' => config('ninja.wepay.client_id'), 'client_secret' => config('ninja.wepay.client_secret'), 'credit_card_id' => (int)$request->input('credit_card_id'), @@ -141,6 +142,7 @@ use WePayCommon; nlog($request->all()); // charge the credit card $response = $this->wepay_payment_driver->wepay->request('checkout/create', array( + 'callback_uri' => route('payment_webhook', ['company_key' => $this->wepay_payment_driver->company_gateway->company->company_key, 'company_gateway_id' => $this->wepay_payment_driver->company_gateway->hashed_id]), 'unique_id' => Str::random(40), 'account_id' => $this->wepay_payment_driver->company_gateway->getConfigField('accountId'), 'amount' => $this->wepay_payment_driver->payment_hash->data->amount_with_fee, diff --git a/app/PaymentDrivers/WePayPaymentDriver.php b/app/PaymentDrivers/WePayPaymentDriver.php index d5f976bef28f..8b4a2948ebee 100644 --- a/app/PaymentDrivers/WePayPaymentDriver.php +++ b/app/PaymentDrivers/WePayPaymentDriver.php @@ -265,6 +265,27 @@ class WePayPaymentDriver extends BaseDriver } + public function detach(ClientGatewayToken $token) + { + /*Bank accounts cannot be deleted - only CC*/ + if($token->gateway_type_id == 2) + return true; + + $this->init(); + + $response = $this->wepay->request('/credit_card/delete', [ + 'client_id' => config('ninja.wepay.client_id'), + 'client_secret' => config('ninja.wepay.client_secret'), + 'credit_card_id' => intval($token->token), + ]); + + if ($response->state == 'deleted') { + return true; + } else { + throw new \Exception(trans('texts.failed_remove_payment_method')); + } + } + public function getClientRequiredFields(): array { From 4173cb69037512a1aee00ec8f1b3b7764c0e3efc Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sun, 20 Jun 2021 20:24:11 +1000 Subject: [PATCH 42/43] WePay --- app/PaymentDrivers/BaseDriver.php | 11 +++++++++++ app/PaymentDrivers/WePay/ACH.php | 2 +- app/PaymentDrivers/WePay/CreditCard.php | 4 ++-- .../gateways/wepay/signup/wepay-signup.blade.php | 10 +++++----- 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/app/PaymentDrivers/BaseDriver.php b/app/PaymentDrivers/BaseDriver.php index be015b015ca6..e380f0dbf86c 100644 --- a/app/PaymentDrivers/BaseDriver.php +++ b/app/PaymentDrivers/BaseDriver.php @@ -160,6 +160,17 @@ class BaseDriver extends AbstractPaymentDriver { } + /** + * Detaches a payment method from the gateway + * + * @param ClientGatewayToken $token The gateway token + * @return bool boolean response + */ + public function detach(ClientGatewayToken $token) + { + return true; + } + /** * Set the inbound request payment method type for access. * diff --git a/app/PaymentDrivers/WePay/ACH.php b/app/PaymentDrivers/WePay/ACH.php index a6580b22c836..b2d8cbf5823d 100644 --- a/app/PaymentDrivers/WePay/ACH.php +++ b/app/PaymentDrivers/WePay/ACH.php @@ -198,7 +198,7 @@ class ACH return redirect()->route('client.payment_methods.verification', ['payment_method' => $token->hashed_id, 'method' => GatewayType::BANK_TRANSFER]); $response = $this->wepay_payment_driver->wepay->request('checkout/create', array( - 'callback_uri' => route('payment_webhook', ['company_key' => $this->wepay_payment_driver->company_gateway->company->company_key, 'company_gateway_id' => $this->wepay_payment_driver->company_gateway->hashed_id]), + // 'callback_uri' => route('payment_webhook', ['company_key' => $this->wepay_payment_driver->company_gateway->company->company_key, 'company_gateway_id' => $this->wepay_payment_driver->company_gateway->hashed_id]), 'unique_id' => Str::random(40), 'account_id' => $this->wepay_payment_driver->company_gateway->getConfigField('accountId'), 'amount' => $this->wepay_payment_driver->payment_hash->data->amount_with_fee, diff --git a/app/PaymentDrivers/WePay/CreditCard.php b/app/PaymentDrivers/WePay/CreditCard.php index 3bdd17876dab..950b143e850d 100644 --- a/app/PaymentDrivers/WePay/CreditCard.php +++ b/app/PaymentDrivers/WePay/CreditCard.php @@ -117,7 +117,7 @@ use WePayCommon; nlog("authorize the card first!"); $response = $this->wepay_payment_driver->wepay->request('credit_card/authorize', array( - 'callback_uri' => route('payment_webhook', ['company_key' => $this->wepay_payment_driver->company_gateway->company->company_key, 'company_gateway_id' => $this->wepay_payment_driver->company_gateway->hashed_id]), + // 'callback_uri' => route('payment_webhook', ['company_key' => $this->wepay_payment_driver->company_gateway->company->company_key, 'company_gateway_id' => $this->wepay_payment_driver->company_gateway->hashed_id]), 'client_id' => config('ninja.wepay.client_id'), 'client_secret' => config('ninja.wepay.client_secret'), 'credit_card_id' => (int)$request->input('credit_card_id'), @@ -142,7 +142,7 @@ use WePayCommon; nlog($request->all()); // charge the credit card $response = $this->wepay_payment_driver->wepay->request('checkout/create', array( - 'callback_uri' => route('payment_webhook', ['company_key' => $this->wepay_payment_driver->company_gateway->company->company_key, 'company_gateway_id' => $this->wepay_payment_driver->company_gateway->hashed_id]), + // 'callback_uri' => route('payment_webhook', ['company_key' => $this->wepay_payment_driver->company_gateway->company->company_key, 'company_gateway_id' => $this->wepay_payment_driver->company_gateway->hashed_id]), 'unique_id' => Str::random(40), 'account_id' => $this->wepay_payment_driver->company_gateway->getConfigField('accountId'), 'amount' => $this->wepay_payment_driver->payment_hash->data->amount_with_fee, diff --git a/resources/views/portal/ninja2020/gateways/wepay/signup/wepay-signup.blade.php b/resources/views/portal/ninja2020/gateways/wepay/signup/wepay-signup.blade.php index b3b2e8a028a8..2c122944aa2c 100644 --- a/resources/views/portal/ninja2020/gateways/wepay/signup/wepay-signup.blade.php +++ b/resources/views/portal/ninja2020/gateways/wepay/signup/wepay-signup.blade.php @@ -72,7 +72,7 @@
@if($country == 'CA') -
+
@@ -82,15 +82,16 @@
@endif - + @endif
@@ -281,5 +282,4 @@
-
From a58db2100cdcb1b32d28be8022b0fc9f7f14e4bb Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sun, 20 Jun 2021 22:04:12 +1000 Subject: [PATCH 43/43] REfunds for wepay --- app/PaymentDrivers/WePayPaymentDriver.php | 26 ++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/app/PaymentDrivers/WePayPaymentDriver.php b/app/PaymentDrivers/WePayPaymentDriver.php index 8b4a2948ebee..bb695f529b2b 100644 --- a/app/PaymentDrivers/WePayPaymentDriver.php +++ b/app/PaymentDrivers/WePayPaymentDriver.php @@ -160,11 +160,6 @@ class WePayPaymentDriver extends BaseDriver return $this->payment_method->paymentResponse($request); //this is your custom implementation from here } - public function refund(Payment $payment, $amount, $return_client_response = false) - { - return $this->payment_method->yourRefundImplementationHere(); //this is your custom implementation from here - } - public function tokenBilling(ClientGatewayToken $cgt, PaymentHash $payment_hash) { return $this->payment_method->yourTokenBillingImplmentation(); //this is your custom implementation from here @@ -264,6 +259,27 @@ class WePayPaymentDriver extends BaseDriver return true; } + public function refund(Payment $payment, $amount, $return_client_response = false) + { + + $this->init(); + + $response = $this->wepay->request('checkout/refund', array( + 'checkout_id' => $payment->transaction_reference, + 'refund_reason' => 'Refund', + 'amount' => $amount + )); + + + return [ + 'transaction_reference' => $response->checkout_id, + 'transaction_response' => json_encode($response), + 'success' => $response->state == 'refunded' ? true : false, + 'description' => 'refund', + 'code' => 0, + ]; + + } public function detach(ClientGatewayToken $token) {