diff --git a/.gitignore b/.gitignore index cd5f809c06..d224f3651e 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,5 @@ /ninja.sublime-project /ninja.sublime-workspace /tests/_log -.idea \ No newline at end of file +.idea +.project diff --git a/app/controllers/AccountController.php b/app/controllers/AccountController.php index 6a7c52d688..09d50ab312 100755 --- a/app/controllers/AccountController.php +++ b/app/controllers/AccountController.php @@ -165,24 +165,56 @@ class AccountController extends \BaseController { $account = Account::with('account_gateways')->findOrFail(Auth::user()->account_id); $accountGateway = null; $config = null; + $configFields = null; if (count($account->account_gateways) > 0) { $accountGateway = $account->account_gateways[0]; $config = $accountGateway->config; - } + $configFields = json_decode($config); + + foreach($configFields as $configField => $value) + { + $configFields->$configField = str_repeat('*', strlen($value)); + } + } + + $recommendedGateways = Gateway::remember(DEFAULT_QUERY_CACHE) + ->where('recommended', '=', '1') + ->orderBy('sort_order') + ->get(); + $recommendedGatewayArray = array(); + + foreach($recommendedGateways as $recommendedGateway) + { + $newRow = count($recommendedGatewayArray) + 1 == round(count($recommendedGateways) / 2); + + $arrayItem = array( + 'value' => $recommendedGateway->id, + 'data-imageUrl' => $recommendedGateway->getLogoUrl(), + 'data-siteUrl' => $recommendedGateway->site_url, + 'data-newRow' => $newRow + ); + $recommendedGatewayArray[$recommendedGateway->name] = $arrayItem; + } + $data = [ 'account' => $account, 'accountGateway' => $accountGateway, - 'config' => json_decode($config), - 'gateways' => Gateway::remember(DEFAULT_QUERY_CACHE)->get(), + 'config' => $configFields, + 'gateways' => Gateway::remember(DEFAULT_QUERY_CACHE) + ->orderBy('name') + ->get(), + 'recommendedGateways' => $recommendedGatewayArray, ]; foreach ($data['gateways'] as $gateway) { - $gateway->fields = Omnipay::create($gateway->provider)->getDefaultParameters(); + $paymentLibrary = $gateway->paymentlibrary; + $gateway->fields = $gateway->getFields(); + if ($accountGateway && $accountGateway->gateway_id == $gateway->id) { $accountGateway->fields = $gateway->fields; @@ -511,13 +543,26 @@ class AccountController extends \BaseController { if ($gatewayId = Input::get('gateway_id')) { $gateway = Gateway::findOrFail($gatewayId); - $fields = Omnipay::create($gateway->provider)->getDefaultParameters(); + + $paymentLibrary = $gateway->paymentlibrary; + + $fields = $gateway->getFields(); foreach ($fields as $field => $details) { if (!in_array($field, ['testMode', 'developerMode', 'headerImageUrl', 'solutionType', 'landingPage', 'brandName'])) { - $rules[$gateway->id.'_'.$field] = 'required'; + if(strtolower($gateway->name) == 'beanstream') + { + if(in_array($field, ['merchant_id', 'passCode'])) + { + $rules[$gateway->id.'_'.$field] = 'required'; + } + } + else + { + $rules[$gateway->id.'_'.$field] = 'required'; + } } } } diff --git a/app/controllers/PaymentController.php b/app/controllers/PaymentController.php index 4ea5ecbf5d..285d4ed872 100755 --- a/app/controllers/PaymentController.php +++ b/app/controllers/PaymentController.php @@ -129,8 +129,10 @@ class PaymentController extends \BaseController private function getPaymentDetails($invoice, $input = null) { $key = $invoice->invoice_number . '_details'; + $gateway = $invoice->client->account->account_gateways[0]->gateway; + $paymentLibrary = $gateway->paymentlibrary; - if ($input) + if ($input && $paymentLibrary->id == PAYMENT_LIBRARY_OMNIPAY) { $data = [ 'firstName' => $input['first_name'], @@ -152,6 +154,49 @@ class PaymentController extends \BaseController ]; Session::put($key, $data); + } + else if ($input && $paymentLibrary->id == PAYMENT_LIBRARY_PHP_PAYMENTS) + { + $input = Input::all(); + $data = [ + 'first_name' => $input['first_name'], + 'last_name' => $input['last_name'], + 'cc_number' => $input['card_number'], + 'cc_exp' => $input['expiration_month'].$input['expiration_year'], + 'cc_code' => $input['cvv'], + 'street' => $input['address1'], + 'street2' => $input['address2'], + 'city' => $input['city'], + 'state' => $input['state'], + 'postal_code' => $input['postal_code'], + 'amt' => $invoice->amount, + 'ship_to_street' => $input['address1'], + 'ship_to_city' => $input['city'], + 'ship_to_state' => $input['state'], + 'ship_to_postal_code' => $input['postal_code'], + 'currency_code' => $invoice->client->currency->code, + ]; + + switch($gateway->id) + { + case GATEWAY_BEANSTREAM: + $data['phone'] = $input['phone']; + $data['email'] = $input['email']; + $data['country'] = $input['country']; + $data['ship_to_country'] = $input['country']; + break; + case GATEWAY_BRAINTREE: + $data['ship_to_state'] = 'Ohio'; //$input['state']; + break; + } + + if(strlen($data['cc_exp']) == 5) + { + $data['cc_exp'] = '0'.$data['cc_exp']; + } + + Session::put($key, $data); + return $data; } else if (Session::get($key)) { @@ -162,22 +207,30 @@ class PaymentController extends \BaseController $data = []; } - $card = new CreditCard($data); - - return [ - 'amount' => $invoice->amount, - 'card' => $card, - 'currency' => $invoice->client->currency->code, - 'returnUrl' => URL::to('complete'), - 'cancelUrl' => URL::to('/') - ]; + if($paymentLibrary->id == PAYMENT_LIBRARY_OMNIPAY) + { + $card = new CreditCard($data); + + return [ + 'amount' => $invoice->amount, + 'card' => $card, + 'currency' => $invoice->client->currency->code, + 'returnUrl' => URL::to('complete'), + 'cancelUrl' => URL::to('/') + ]; + } + else + { + return $data; + } } public function show_payment($invitationKey) { // For PayPal Express we redirect straight to their site - $invitation = Invitation::with('invoice.client.account')->where('invitation_key', '=', $invitationKey)->firstOrFail(); + $invitation = Invitation::with('invoice.client.account', 'invoice.client.account.account_gateways.gateway')->where('invitation_key', '=', $invitationKey)->firstOrFail(); $account = $invitation->invoice->client->account; + if ($account->isGatewayConfigured(GATEWAY_PAYPAL_EXPRESS)) { if (Session::has('error')) @@ -191,16 +244,21 @@ class PaymentController extends \BaseController } } - $invitation = Invitation::with('contact', 'invoice.client')->where('invitation_key', '=', $invitationKey)->firstOrFail(); - $invoice = $invitation->invoice; - $client = $invoice->client; + $invitation = Invitation::with('invoice.invoice_items', 'invoice.client.currency', 'invoice.client.account.account_gateways.gateway')->where('invitation_key', '=', $invitationKey)->firstOrFail(); + $invoice = $invitation->invoice; + $client = $invoice->client; + $gateway = $invoice->client->account->account_gateways[0]->gateway; + $paymentLibrary = $gateway->paymentlibrary; $data = [ 'showBreadcrumbs' => false, 'invitationKey' => $invitationKey, 'invoice' => $invoice, 'client' => $client, - 'contact' => $invitation->contact + 'contact' => $invitation->contact, + 'paymentLibrary' => $paymentLibrary , + 'gateway' => $gateway, + 'countries' => Country::remember(DEFAULT_QUERY_CACHE)->orderBy('name')->get(), ]; return View::make('payments.payment', $data); @@ -235,7 +293,7 @@ class PaymentController extends \BaseController $invitation = Invitation::with('invoice.invoice_items', 'invoice.client.currency', 'invoice.client.account.account_gateways.gateway')->where('invitation_key', '=', $invitationKey)->firstOrFail(); $invoice = $invitation->invoice; $accountGateway = $invoice->client->account->account_gateways[0]; - $gateway = self::createGateway($accountGateway); + $paymentLibrary = $accountGateway->gateway->paymentlibrary; if ($onSite) { @@ -247,46 +305,95 @@ class PaymentController extends \BaseController $client->postal_code = trim(Input::get('postal_code')); $client->save(); } + try { - $details = self::getPaymentDetails($invoice, Input::all()); - $response = $gateway->purchase($details)->send(); - $ref = $response->getTransactionReference(); + if($paymentLibrary->id == PAYMENT_LIBRARY_OMNIPAY) + { + $gateway = self::createGateway($accountGateway); + $details = self::getPaymentDetails($invoice, Input::all()); + $response = $gateway->purchase($details)->send(); + $ref = $response->getTransactionReference(); + + if (!$ref) + { + Session::flash('error', $response->getMessage()); + return Redirect::to('payment/' . $invitationKey) + ->withInput(); + } - if (!$ref) - { - Session::flash('error', $response->getMessage()); - return Redirect::to('payment/' . $invitationKey) - ->withInput(); + if ($response->isSuccessful()) + { + $payment = self::createPayment($invitation, $ref); + + $invoice->invoice_status_id = INVOICE_STATUS_PAID; + $invoice->save(); + + Event::fire('invoice.paid', $payment); + + Session::flash('message', trans('texts.applied_payment')); + return Redirect::to('view/' . $payment->invitation->invitation_key); + } + else if ($response->isRedirect()) + { + $invitation->transaction_reference = $ref; + $invitation->save(); + + $response->redirect(); + } + else + { + Session::flash('error', $response->getMessage()); + return Utils::fatalError('Sorry, there was an error processing your payment. Please try again later.

', $response->getMessage()); + } } + else if ($paymentLibrary->id == PAYMENT_LIBRARY_PHP_PAYMENTS) + { + $gateway = $accountGateway->gateway; + $provider = $gateway->provider; + $p = new PHP_Payments(array('mode' => 'test')); + + $config = Payment_Utility::load('config', 'drivers/'.$provider); + + switch($gateway->id) + { + case GATEWAY_BEANSTREAM: + $config['delay_charge'] = FALSE; + $config['bill_outstanding'] = TRUE; + break; + case GATEWAY_AMAZON: + $config['return_url'] = URL::to('complete'); + $config['abandon_url'] = URL::to('/'); + $config['immediate_return'] = 0; + $config['process_immediate'] = 1; + $config['ipn_url'] = URL::to('ipn'); + $config['collect_shipping_address'] = false; + break; + } + + $details = self::getPaymentDetails($invoice, Input::all()); + + $response = $p->oneoff_payment($provider, $details, $config); - if ($response->isSuccessful()) - { - $payment = self::createPayment($invitation, $ref); - - $invoice->invoice_status_id = INVOICE_STATUS_PAID; - $invoice->save(); - - Event::fire('invoice.paid', $payment); - - Session::flash('message', trans('texts.applied_payment')); - return Redirect::to('view/' . $payment->invitation->invitation_key); - } - else if ($response->isRedirect()) - { - $invitation->transaction_reference = $ref; - $invitation->save(); - - $response->redirect(); - } - else - { - $errorMessage = trans('texts.payment_error') . "\n\n" . $response->getMessage(); - Session::flash('error', $errorMessage); - Utils::logError($errorMessage); - return Redirect::to('view/' . $payment->invitation->invitation_key); - } + if (strtolower($response->status) == 'success') + { + $payment = self::createPayment($invitation, $response->response_message); + + $invoice->invoice_status_id = INVOICE_STATUS_PAID; + $invoice->save(); + + Event::fire('invoice.paid', $payment); + + Session::flash('message', trans('texts.applied_payment')); + return Redirect::to('view/' . $payment->invitation->invitation_key); + } + else + { + Session::flash('error', $response->response_message); + return Utils::fatalError('Sorry, there was an error processing your payment. Please try again later.

', $response->response_message); + } + } } catch (\Exception $e) { diff --git a/app/database/migrations/2014_03_20_200300_create_payment_libraries.php b/app/database/migrations/2014_03_20_200300_create_payment_libraries.php new file mode 100644 index 0000000000..d4c7855a70 --- /dev/null +++ b/app/database/migrations/2014_03_20_200300_create_payment_libraries.php @@ -0,0 +1,61 @@ +increments('id'); + $t->timestamps(); + + $t->string('name'); + $t->boolean('visible')->default(true); + }); + + DB::table('payment_libraries')->insert(['name' => 'Omnipay']); + DB::table('payment_libraries')->insert(['name' => 'PHP-Payments']); + + Schema::table('gateways', function($table) + { + $table->unsignedInteger('payment_library_id')->default(1); + }); + + DB::table('gateways')->update(['payment_library_id' => 1]); + + Schema::table('gateways', function($table) + { + $table->foreign('payment_library_id')->references('id')->on('payment_libraries')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + if (Schema::hasColumn('gateways', 'payment_library_id')) + { + Schema::table('gateways', function($table) + { + $table->dropForeign('gateways_payment_library_id_foreign'); + $table->dropColumn('payment_library_id'); + }); + } + + Schema::dropIfExists('payment_libraries'); + } + +} \ No newline at end of file diff --git a/app/database/migrations/2014_03_25_102200_add_sort_and_recommended_to_gateways.php b/app/database/migrations/2014_03_25_102200_add_sort_and_recommended_to_gateways.php new file mode 100644 index 0000000000..535636f90e --- /dev/null +++ b/app/database/migrations/2014_03_25_102200_add_sort_and_recommended_to_gateways.php @@ -0,0 +1,49 @@ +unsignedInteger('sort_order')->default(10000); + $table->boolean('recommended'); + $table->string('site_url', 200); + }); + } + + public function down() + { + if (Schema::hasColumn('gateways', 'sort_order')) + { + Schema::table('gateways', function($table) + { + $table->dropColumn('sort_order'); + }); + } + + if (Schema::hasColumn('gateways', 'recommended')) + { + Schema::table('gateways', function($table) + { + $table->dropColumn('recommended'); + }); + } + + if (Schema::hasColumn('gateways', 'site_url')) + { + Schema::table('gateways', function($table) + { + $table->dropColumn('site_url'); + }); + } + } +} diff --git a/app/database/seeds/DatabaseSeeder.php b/app/database/seeds/DatabaseSeeder.php index 4d194ed466..955e643203 100755 --- a/app/database/seeds/DatabaseSeeder.php +++ b/app/database/seeds/DatabaseSeeder.php @@ -18,6 +18,9 @@ class DatabaseSeeder extends Seeder { $this->call('CountriesSeeder'); $this->command->info('Seeded the countries!'); + + $this->call('PaymentLibrariesSeeder'); + $this->command->info('Seeded the Payment Libraries!'); } } \ No newline at end of file diff --git a/app/database/seeds/PaymentLibrariesSeeder.php b/app/database/seeds/PaymentLibrariesSeeder.php new file mode 100644 index 0000000000..422ae53465 --- /dev/null +++ b/app/database/seeds/PaymentLibrariesSeeder.php @@ -0,0 +1,35 @@ +'BeanStream', 'provider'=>'BeanStream', 'payment_library_id' => 2), + array('name'=>'Psigate', 'provider'=>'Psigate', 'payment_library_id' => 2) + ]; + + $updateProviders = array( + 0 => 'AuthorizeNet_AIM', + 1 => 'BeanStream', + 2 => 'iTransact', + 3 => 'FirstData_Connect', + 4 => 'PayPal_Pro', + 5 => 'TwoCheckout' + ); + + foreach ($gateways as $gateway) + { + Gateway::create($gateway); + } + + Gateway::whereIn('provider', $updateProviders)->update(array('recommended' => 1)); + + Gateway::where('provider', '=', 'AuthorizeNet_AIM')->update(array('sort_order' => 5, 'site_url' => 'http://www.authorize.net/')); + Gateway::where('provider', '=', 'BeanStream')->update(array('sort_order' => 10, 'site_url' => 'http://www.beanstream.com/')); + Gateway::where('provider', '=', 'FirstData_Connect')->update(array('sort_order' => 20, 'site_url' => 'https://www.firstdata.com/')); + Gateway::where('provider', '=', 'PayPal_Pro')->update(array('sort_order' => 25, 'site_url' => 'https://www.paypal.com/')); + Gateway::where('provider', '=', 'TwoCheckout')->update(array('sort_order' => 30, 'site_url' => 'https://www.2checkout.com/')); + } +} \ No newline at end of file diff --git a/app/models/Gateway.php b/app/models/Gateway.php index 2b072d1f29..c1e747ffa8 100755 --- a/app/models/Gateway.php +++ b/app/models/Gateway.php @@ -4,4 +4,35 @@ class Gateway extends Eloquent { public $timestamps = false; protected $softDelete = false; + + public function paymentlibrary() + { + return $this->belongsTo('PaymentLibrary', 'payment_library_id'); + } + + public function getLogoUrl() + { + return '/images/gateways/logo_'.$this->provider.'.png'; + } + + public function getFields() + { + $paymentLibrary = $this->paymentlibrary; + + if($paymentLibrary->id == PAYMENT_LIBRARY_OMNIPAY) + { + $fields = Omnipay::create($this->provider)->getDefaultParameters(); + } + else + { + $fields = Payment_Utility::load('config', 'drivers/'.$this->provider); + } + + if($fields == null) + { + $fields = array(); + } + + return $fields; + } } \ No newline at end of file diff --git a/app/models/PaymentLibrary.php b/app/models/PaymentLibrary.php new file mode 100644 index 0000000000..b8550c212a --- /dev/null +++ b/app/models/PaymentLibrary.php @@ -0,0 +1,11 @@ +hasMany('Gateway', 'payment_library_id'); + } +} \ No newline at end of file diff --git a/app/routes.php b/app/routes.php index 4ba23ef938..adf911df98 100755 --- a/app/routes.php +++ b/app/routes.php @@ -234,6 +234,16 @@ define('DEFAULT_LOCALE', 'en'); define('GATEWAY_PAYPAL_EXPRESS', 17); define('NINJA_ACCOUNT_KEY', 'zg4ylmzDkdkPOT8yoKQw9LTWaoZJx79h'); +define('PAYMENT_LIBRARY_OMNIPAY', 1); +define('PAYMENT_LIBRARY_PHP_PAYMENTS', 2); + +define('GATEWAY_BEANSTREAM', 29); +define('GATEWAY_AMAZON', 30); +define('GATEWAY_BLUEPAY', 31); +define('GATEWAY_BRAINTREE', 32); +define('GATEWAY_GOOGLE', 33); +define('GATEWAY_PSIGATE', 34); +define('GATEWAY_QUICKBOOKS', 35); if (Auth::check() && !Session::has(SESSION_TIMEZONE)) { diff --git a/app/views/accounts/payments.blade.php b/app/views/accounts/payments.blade.php index c289d8829f..53ab0d38f3 100755 --- a/app/views/accounts/payments.blade.php +++ b/app/views/accounts/payments.blade.php @@ -10,6 +10,15 @@ {{ Former::populateField('notify_paid', intval(Auth::user()->notify_paid)) }} {{ Former::legend('Payment Gateway') }} + + {{Former::label('Lorem Ipsum goes here.')}} + +

+ {{ Former::radios('recommendedGateway_id') + ->label('Recommended Gateways') + ->radios($recommendedGateways) + ->class('recommended-gateway')}} +
@if ($accountGateway) {{ Former::populateField('gateway_id', $accountGateway->gateway_id) }} @@ -22,12 +31,14 @@ @endforeach @endif - {{ Former::select('gateway_id')->label('Provider')->addOption('', '') - ->fromQuery($gateways, 'name', 'id')->onchange('setFieldsShown()'); }} + {{ Former::select('gateway_id')->label('PayPal & Other Gateways')->addOption('', '') + ->dataClass('gateway-dropdown') + ->fromQuery($gateways, 'name', 'id') + ->onchange('setFieldsShown()'); }} @foreach ($gateways as $gateway) -