1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-08 12:12:48 +01:00
This commit is contained in:
Hillel Coren 2016-09-15 13:41:09 +03:00
parent 531c26e87c
commit e743af4d24
26 changed files with 750 additions and 94 deletions

View File

@ -1,6 +1,8 @@
<?php namespace App\Http\Controllers;
use App\Models\AccountGateway;
use App\Models\AccountGatewaySettings;
use App\Models\GatewayType;
use App\Services\TemplateService;
use Auth;
use File;
@ -458,10 +460,12 @@ class AccountController extends BaseController
}
return View::make('accounts.payments', [
'showAdd' => $count < count(Gateway::$alternate) + 1,
'title' => trans('texts.online_payments'),
'showAdd' => $count < count(Gateway::$alternate) + 1,
'title' => trans('texts.online_payments'),
'tokenBillingOptions' => $tokenBillingOptions,
'account' => $account,
'currency' => Utils::getFromCache(Session::get(SESSION_CURRENCY, DEFAULT_CURRENCY),
'currencies'),
'account' => $account,
]);
}
}
@ -1226,6 +1230,35 @@ class AccountController extends BaseController
return Redirect::to('settings/'.ACCOUNT_PAYMENTS);
}
/**
* @return \Illuminate\Http\RedirectResponse
*/
public function savePaymentGatewayLimits()
{
$gateway_type_id = intval(Input::get('gateway_type_id'));
$gateway_settings = AccountGatewaySettings::scope()->where('gateway_type_id', '=', $gateway_type_id)->first();
if ( ! $gateway_settings) {
$gateway_settings = AccountGatewaySettings::createNew();
$gateway_settings->gateway_type_id = $gateway_type_id;
}
$gateway_settings->min_limit = Input::get('limit_min_enable') ? intval(Input::get('limit_min')) : null;
$gateway_settings->max_limit = Input::get('limit_max_enable') ? intval(Input::get('limit_max')) : null;
if ($gateway_settings->max_limit !== null && $gateway_settings->min_limit > $gateway_settings->max_limit) {
$gateway_settings->max_limit = $gateway_settings->min_limit;
}
$gateway_settings->save();
event(new UserSettingsChanged());
Session::flash('message', trans('texts.updated_settings'));
return Redirect::to('settings/' . ACCOUNT_PAYMENTS);
}
/**
* @return \Illuminate\Http\RedirectResponse
*/

View File

@ -20,6 +20,7 @@ use App\Http\Requests\CreateOnlinePaymentRequest;
use App\Ninja\Repositories\ClientRepository;
use App\Ninja\Repositories\InvoiceRepository;
use App\Services\InvoiceService;
use App\Models\GatewayType;
/**
* Class OnlinePaymentController
@ -60,7 +61,7 @@ class OnlinePaymentController extends BaseController
* @param bool $sourceId
* @return \Illuminate\Http\RedirectResponse
*/
public function showPayment($invitationKey, $gatewayType = false, $sourceId = false)
public function showPayment($invitationKey, $gatewayTypeAlias = false, $sourceId = false)
{
if ( ! $invitation = $this->invoiceRepo->findInvoiceByInvitation($invitationKey)) {
return response()->view('error', [
@ -75,11 +76,15 @@ class OnlinePaymentController extends BaseController
$invitation = $invitation->load('invoice.client.account.account_gateways.gateway');
if ( ! $gatewayType) {
$gatewayType = Session::get($invitation->id . 'gateway_type');
if ( ! $gatewayTypeAlias) {
$gatewayTypeId = Session::get($invitation->id . 'gateway_type');
} elseif ($gatewayTypeAlias != GATEWAY_TYPE_TOKEN) {
$gatewayTypeId = GatewayType::getIdFromAlias($gatewayTypeAlias);
} else {
$gatewayTypeId = $gatewayTypeAlias;
}
$paymentDriver = $invitation->account->paymentDriver($invitation, $gatewayType);
$paymentDriver = $invitation->account->paymentDriver($invitation, $gatewayTypeId);
try {
return $paymentDriver->startPurchase(Input::all(), $sourceId);
@ -95,8 +100,8 @@ class OnlinePaymentController extends BaseController
public function doPayment(CreateOnlinePaymentRequest $request)
{
$invitation = $request->invitation;
$gatewayType = Session::get($invitation->id . 'gateway_type');
$paymentDriver = $invitation->account->paymentDriver($invitation, $gatewayType);
$gatewayTypeId = Session::get($invitation->id . 'gateway_type');
$paymentDriver = $invitation->account->paymentDriver($invitation, $gatewayTypeId);
try {
$paymentDriver->completeOnsitePurchase($request->all());
@ -114,17 +119,24 @@ class OnlinePaymentController extends BaseController
/**
* @param bool $invitationKey
* @param bool $gatewayType
* @param mixed $gatewayTypeAlias
* @return \Illuminate\Http\RedirectResponse
*/
public function offsitePayment($invitationKey = false, $gatewayType = false)
public function offsitePayment($invitationKey = false, $gatewayTypeAlias = false)
{
$invitationKey = $invitationKey ?: Session::get('invitation_key');
$invitation = Invitation::with('invoice.invoice_items', 'invoice.client.currency', 'invoice.client.account.account_gateways.gateway')
->where('invitation_key', '=', $invitationKey)->firstOrFail();
$gatewayType = $gatewayType ?: Session::get($invitation->id . 'gateway_type');
$paymentDriver = $invitation->account->paymentDriver($invitation, $gatewayType);
if ( ! $gatewayTypeAlias) {
$gatewayTypeId = Session::get($invitation->id . 'gateway_type');
} elseif ($gatewayTypeAlias != GATEWAY_TYPE_TOKEN) {
$gatewayTypeId = GatewayType::getIdFromAlias($gatewayTypeAlias);
} else {
$gatewayTypeId = $gatewayTypeAlias;
}
$paymentDriver = $invitation->account->paymentDriver($invitation, $gatewayTypeId);
if ($error = Input::get('error_description') ?: Input::get('error')) {
return $this->error($paymentDriver, $error);
@ -228,7 +240,7 @@ class OnlinePaymentController extends BaseController
}
}
public function handleBuyNow(ClientRepository $clientRepo, InvoiceService $invoiceService, $gatewayType = false)
public function handleBuyNow(ClientRepository $clientRepo, InvoiceService $invoiceService, $gatewayTypeAlias = false)
{
if (Crawler::isCrawler()) {
return redirect()->to(NINJA_WEB_URL, 301);
@ -282,8 +294,8 @@ class OnlinePaymentController extends BaseController
$invitation = $invoice->invitations[0];
$link = $invitation->getLink();
if ($gatewayType) {
return redirect()->to($invitation->getLink('payment') . "/{$gatewayType}");
if ($gatewayTypeAlias) {
return redirect()->to($invitation->getLink('payment') . "/{$gatewayTypeAlias}");
} else {
return redirect()->to($invitation->getLink());
}

View File

@ -132,6 +132,7 @@ Route::group(['middleware' => 'auth:user'], function() {
Route::get('settings/user_details', 'AccountController@showUserDetails');
Route::post('settings/user_details', 'AccountController@saveUserDetails');
Route::post('settings/payment_gateway_limits', 'AccountController@savePaymentGatewayLimits');
Route::post('users/change_password', 'UserController@changePassword');
Route::resource('clients', 'ClientController');
@ -704,11 +705,11 @@ if (!defined('CONTACT_EMAIL')) {
define('PAYMENT_METHOD_STATUS_VERIFICATION_FAILED', 'verification_failed');
define('PAYMENT_METHOD_STATUS_VERIFIED', 'verified');
define('GATEWAY_TYPE_CREDIT_CARD', 'credit_card');
define('GATEWAY_TYPE_BANK_TRANSFER', 'bank_transfer');
define('GATEWAY_TYPE_PAYPAL', 'paypal');
define('GATEWAY_TYPE_BITCOIN', 'bitcoin');
define('GATEWAY_TYPE_DWOLLA', 'dwolla');
define('GATEWAY_TYPE_CREDIT_CARD', 1);
define('GATEWAY_TYPE_BANK_TRANSFER', 2);
define('GATEWAY_TYPE_PAYPAL', 3);
define('GATEWAY_TYPE_BITCOIN', 4);
define('GATEWAY_TYPE_DWOLLA', 5);
define('GATEWAY_TYPE_TOKEN', 'token');
define('REMINDER1', 'reminder1');
@ -852,6 +853,7 @@ if (!defined('CONTACT_EMAIL')) {
'invoiceStatus' => 'App\Models\InvoiceStatus',
'frequencies' => 'App\Models\Frequency',
'gateways' => 'App\Models\Gateway',
'gatewayTypes' => 'App\Models\GatewayType',
'fonts' => 'App\Models\Font',
'banks' => 'App\Models\Bank',
];

View File

@ -610,14 +610,14 @@ class Account extends Eloquent
/**
* @param bool $invitation
* @param bool $gatewayType
* @param mixed $gatewayTypeId
* @return bool
*/
public function paymentDriver($invitation = false, $gatewayType = false)
public function paymentDriver($invitation = false, $gatewayTypeId = false)
{
/** @var AccountGateway $accountGateway */
if ($accountGateway = $this->getGatewayByType($gatewayType)) {
return $accountGateway->paymentDriver($invitation, $gatewayType);
if ($accountGateway = $this->getGatewayByType($gatewayTypeId)) {
return $accountGateway->paymentDriver($invitation, $gatewayTypeId);
}
return false;

View File

@ -73,14 +73,14 @@ class AccountGateway extends EntityModel
/**
* @param bool $invitation
* @param bool $gatewayType
* @param mixed $gatewayTypeId
* @return mixed
*/
public function paymentDriver($invitation = false, $gatewayType = false)
public function paymentDriver($invitation = false, $gatewayTypeId = false)
{
$class = static::paymentDriverClass($this->gateway->provider);
return new $class($this, $invitation, $gatewayType);
return new $class($this, $invitation, $gatewayTypeId);
}
/**

View File

@ -0,0 +1,32 @@
<?php namespace App\Models;
use Auth;
/**
* Class AccountGatewaySettings
*/
class AccountGatewaySettings extends EntityModel
{
/**
* @var array
*/
protected $dates = ['updated_at'];
/**
* @var bool
*/
protected static $hasPublicId = false;
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function gatewayType()
{
return $this->belongsTo('App\Models\GatewayType');
}
public function setCreatedAtAttribute($value)
{
// to Disable created_at
}
}

View File

@ -2,6 +2,7 @@
use Auth;
use Eloquent;
use Illuminate\Database\QueryException;
use Utils;
use Validator;
@ -14,6 +15,12 @@ class EntityModel extends Eloquent
* @var bool
*/
public $timestamps = true;
/**
* @var bool
*/
protected static $hasPublicId = true;
/**
* @var array
*/
@ -56,13 +63,16 @@ class EntityModel extends Eloquent
$lastEntity = $className::whereAccountId($entity->account_id);
}
$lastEntity = $lastEntity->orderBy('public_id', 'DESC')
->first();
if ($lastEntity) {
$entity->public_id = $lastEntity->public_id + 1;
} else {
$entity->public_id = 1;
if (static::$hasPublicId) {
$lastEntity = $lastEntity->orderBy('public_id', 'DESC')
->first();
if ($lastEntity) {
$entity->public_id = $lastEntity->public_id + 1;
} else {
$entity->public_id = 1;
}
}
return $entity;

View File

@ -0,0 +1,34 @@
<?php namespace App\Models;
use Eloquent;
use Cache;
use Utils;
/**
* Class GatewayType
*/
class GatewayType extends Eloquent
{
/**
* @var bool
*/
public $timestamps = false;
/**
* @return mixed
*/
public function getName()
{
return $this->name;
}
public static function getAliasFromId($id)
{
return Utils::getFromCache($id, 'gatewayTypes')->alias;
}
public static function getIdFromAlias($alias)
{
return Cache::get('gatewayTypes')->where('alias', $alias)->first()->id;
}
}

View File

@ -11,4 +11,12 @@ class PaymentType extends Eloquent
* @var bool
*/
public $timestamps = false;
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function gatewayType()
{
return $this->belongsTo('App\Models\GatewayType');
}
}

View File

@ -1,6 +1,11 @@
<?php namespace App\Ninja\Datatables;
use App\Models\AccountGatewaySettings;
use App\Models\GatewayType;
use URL;
use Cache;
use Utils;
use Session;
use App\Models\AccountGateway;
class AccountGatewayDatatable extends EntityDatatable
@ -45,12 +50,52 @@ class AccountGatewayDatatable extends EntityDatatable
}
}
],
[
'limit',
function ($model) {
$accountGateway = AccountGateway::find($model->id);
$paymentDriver = $accountGateway->paymentDriver();
$gatewayTypes = $paymentDriver->gatewayTypes();
$gatewayTypes = array_diff($gatewayTypes, array(GATEWAY_TYPE_TOKEN));
$html = '';
foreach ($gatewayTypes as $gatewayTypeId) {
$accountGatewaySettings = AccountGatewaySettings::scope()->where('account_gateway_settings.gateway_type_id',
'=', $gatewayTypeId)->first();
$gatewayType = GatewayType::find($gatewayTypeId);
if (count($gatewayTypes) > 1) {
if ($html) {
$html .= '<br>';
}
$html .= $gatewayType->name . ' &mdash; ';
}
if ($accountGatewaySettings && $accountGatewaySettings->min_limit !== null && $accountGatewaySettings->max_limit !== null) {
$html .= Utils::formatMoney($accountGatewaySettings->min_limit) . ' - ' . Utils::formatMoney($accountGatewaySettings->max_limit);
} elseif ($accountGatewaySettings && $accountGatewaySettings->min_limit !== null) {
$html .= trans('texts.min_limit',
array('min' => Utils::formatMoney($accountGatewaySettings->min_limit))
);
} elseif ($accountGatewaySettings && $accountGatewaySettings->max_limit !== null) {
$html .= trans('texts.max_limit',
array('max' => Utils::formatMoney($accountGatewaySettings->max_limit))
);
} else {
$html .= trans('texts.no_limit');
}
}
return $html;
}
],
];
}
public function actions()
{
return [
$actions = [
[
uctrans('texts.resend_confirmation_email'),
function ($model) {
@ -98,6 +143,30 @@ class AccountGatewayDatatable extends EntityDatatable
}
]
];
foreach (Cache::get('gatewayTypes') as $gatewayType) {
$actions[] = [
trans('texts.set_limits', ['gateway_type' => $gatewayType->name]),
function () use ($gatewayType) {
$accountGatewaySettings = AccountGatewaySettings::scope()->where('account_gateway_settings.gateway_type_id',
'=', $gatewayType->id)->first();
$min = $accountGatewaySettings && $accountGatewaySettings->min_limit !== null ? $accountGatewaySettings->min_limit : 'null';
$max = $accountGatewaySettings && $accountGatewaySettings->max_limit !== null ? $accountGatewaySettings->max_limit : 'null';
return "javascript:showLimitsModal('{$gatewayType->name}',{$gatewayType->id},$min,$max)";
},
function ($model) use ($gatewayType) {
// Only show this action if the given gateway supports this gateway type
$accountGateway = AccountGateway::find($model->id);
$paymentDriver = $accountGateway->paymentDriver();
$gatewayTypes = $paymentDriver->gatewayTypes();
return in_array($gatewayType->id, $gatewayTypes);
}
];
}
return $actions;
}
}

View File

@ -2,16 +2,19 @@
use URL;
use Session;
use Utils;
use Request;
use Omnipay;
use Exception;
use CreditCard;
use DateTime;
use App\Models\AccountGatewayToken;
use App\Models\AccountGatewaySettings;
use App\Models\Account;
use App\Models\Payment;
use App\Models\PaymentMethod;
use App\Models\Country;
use App\Models\GatewayType;
class BasePaymentDriver
{
@ -119,6 +122,12 @@ class BasePaymentDriver
$gateway = $this->accountGateway->gateway;
if ( ! $this->meetsGatewayTypeLimits($this->gatewayType)) {
// The customer must have hacked the URL
Session::flash('error', trans('texts.limits_not_met'));
return redirect()->to('view/' . $this->invitation->invitation_key);
}
if ($this->isGatewayType(GATEWAY_TYPE_TOKEN) || $gateway->is_offsite) {
if (Session::has('error')) {
Session::reflash();
@ -158,12 +167,14 @@ class BasePaymentDriver
// check if a custom view exists for this provider
protected function paymentView()
{
$file = sprintf('%s/views/payments/%s/%s.blade.php', resource_path(), $this->providerName(), $this->gatewayType);
$gatewayTypeAlias = GatewayType::getAliasFromId($this->gatewayType);
$file = sprintf('%s/views/payments/%s/%s.blade.php', resource_path(), $this->providerName(), $gatewayTypeAlias);
if (file_exists($file)) {
return sprintf('payments.%s/%s', $this->providerName(), $this->gatewayType);
return sprintf('payments.%s/%s', $this->providerName(), $gatewayTypeAlias);
} else {
return sprintf('payments.%s', $this->gatewayType);
return sprintf('payments.%s', $gatewayTypeAlias);
}
}
@ -323,7 +334,8 @@ class BasePaymentDriver
protected function paymentDetails($paymentMethod = false)
{
$invoice = $this->invoice();
$completeUrl = url('complete/' . $this->invitation->invitation_key . '/' . $this->gatewayType);
$gatewayTypeAlias = GatewayType::getAliasFromId($this->gatewayType);
$completeUrl = url('complete/' . $this->invitation->invitation_key . '/' . $gatewayTypeAlias);
$data = [
'amount' => $invoice->getRequestedAmount(),
@ -750,6 +762,10 @@ class BasePaymentDriver
continue;
}
if ( ! $this->meetsGatewayTypeLimits($paymentMethod->payment_type->gateway_type_id)) {
continue;
}
$url = URL::to("/payment/{$this->invitation->invitation_key}/token/".$paymentMethod->public_id);
if ($paymentMethod->payment_type_id == PAYMENT_TYPE_ACH) {
@ -777,27 +793,59 @@ class BasePaymentDriver
{
$links = [];
foreach ($this->gatewayTypes() as $gatewayType) {
if ($gatewayType === GATEWAY_TYPE_TOKEN) {
foreach ($this->gatewayTypes() as $gatewayTypeId) {
if ($gatewayTypeId === GATEWAY_TYPE_TOKEN) {
continue;
}
if ( ! $this->meetsGatewayTypeLimits($gatewayTypeId)) {
continue;
}
$gatewayTypeAlias = GatewayType::getAliasFromId($gatewayTypeId);
$links[] = [
'url' => $this->paymentUrl($gatewayType),
'label' => trans("texts.{$gatewayType}")
'url' => $this->paymentUrl($gatewayTypeAlias),
'label' => trans("texts.{$gatewayTypeAlias}")
];
}
return $links;
}
protected function paymentUrl($gatewayType)
protected function meetsGatewayTypeLimits($gatewayTypeId)
{
if ( !$gatewayTypeId ) {
return true;
}
$accountGatewaySettings = AccountGatewaySettings::scope()->where('account_gateway_settings.gateway_type_id',
'=', $gatewayTypeId)->first();
if ($accountGatewaySettings) {
$invoice = $this->invoice();
if ($accountGatewaySettings->min_limit !== null && $invoice->balance < $accountGatewaySettings->min_limit) {
return false;
}
if ($accountGatewaySettings->max_limit !== null && $invoice->balance > $accountGatewaySettings->max_limit) {
return false;
}
}
return true;
}
protected function paymentUrl($gatewayTypeAlias)
{
$account = $this->account();
$url = URL::to("/payment/{$this->invitation->invitation_key}/{$gatewayType}");
$url = URL::to("/payment/{$this->invitation->invitation_key}/{$gatewayTypeAlias}");
$gatewayTypeId = GatewayType::getIdFromAlias($gatewayTypeAlias);
// PayPal doesn't allow being run in an iframe so we need to open in new tab
if ($gatewayType === GATEWAY_TYPE_PAYPAL) {
if ($gatewayTypeId === GATEWAY_TYPE_PAYPAL) {
$url .= '#braintree_paypal';
if ($account->iframe_url) {

View File

@ -30,6 +30,7 @@
"fuse.js": "~2.0.2",
"dropzone": "~4.3.0",
"sweetalert": "~1.1.3",
"nouislider": "~8.5.1",
"bootstrap-daterangepicker": "~2.1.24"
},
"resolutions": {

View File

@ -0,0 +1,70 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateGatewayTypes extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::dropIfExists('gateway_types');
Schema::create('gateway_types', function($table)
{
$table->increments('id');
$table->string('alias');
$table->string('name');
});
Schema::dropIfExists('account_gateway_settings');
Schema::create('account_gateway_settings', function($table)
{
$table->increments('id');
$table->unsignedInteger('account_id');
$table->unsignedInteger('user_id');
$table->unsignedInteger('gateway_type_id')->nullable();
$table->timestamp('updated_at')->nullable();
$table->unsignedInteger('min_limit')->nullable();
$table->unsignedInteger('max_limit')->nullable();
$table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->foreign('gateway_type_id')->references('id')->on('gateway_types')->onDelete('cascade');
});
Schema::table('payment_types', function($table)
{
$table->unsignedInteger('gateway_type_id')->nullable();
});
Schema::table('payment_types', function($table)
{
$table->foreign('gateway_type_id')->references('id')->on('gateway_types')->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('payment_types', function($table)
{
$table->dropForeign('payment_types_gateway_type_id_foreign');
$table->dropColumn('gateway_type_id');
});
Schema::dropIfExists('account_gateway_settings');
Schema::dropIfExists('gateway_types');
}
}

View File

@ -17,6 +17,7 @@ class DatabaseSeeder extends Seeder
$this->call('CountriesSeeder');
$this->call('PaymentLibrariesSeeder');
$this->call('FontsSeeder');
$this->call('GatewayTypesSeeder');
$this->call('BanksSeeder');
$this->call('InvoiceStatusSeeder');
$this->call('PaymentStatusSeeder');

View File

@ -0,0 +1,28 @@
<?php
use App\Models\GatewayType;
class GatewayTypesSeeder extends Seeder
{
public function run()
{
Eloquent::unguard();
$gateway_types = [
['alias' => 'credit_card', 'name' => 'Credit Card'],
['alias' => 'bank_transfer', 'name' => 'Bank Transfer'],
['alias' => 'paypal', 'name' => 'PayPal'],
['alias' => 'bitcoin', 'name' => 'Bitcoin'],
['alias' => 'dwolla', 'name' => 'Dwolla'],
];
foreach ($gateway_types as $gateway_type) {
$record = GatewayType::where('name', '=', $gateway_type['name'])->first();
if (!$record) {
GatewayType::create($gateway_type);
}
}
}
}

View File

@ -10,32 +10,39 @@ class PaymentTypesSeeder extends Seeder
$paymentTypes = [
array('name' => 'Apply Credit'),
array('name' => 'Bank Transfer'),
array('name' => 'Bank Transfer', 'gateway_type_id' => GATEWAY_TYPE_BANK_TRANSFER),
array('name' => 'Cash'),
array('name' => 'Debit'),
array('name' => 'ACH'),
array('name' => 'Visa Card'),
array('name' => 'MasterCard'),
array('name' => 'American Express'),
array('name' => 'Discover Card'),
array('name' => 'Diners Card'),
array('name' => 'EuroCard'),
array('name' => 'Nova'),
array('name' => 'Credit Card Other'),
array('name' => 'PayPal'),
array('name' => 'Debit', 'gateway_type_id' => GATEWAY_TYPE_CREDIT_CARD),
array('name' => 'ACH', 'gateway_type_id' => GATEWAY_TYPE_BANK_TRANSFER),
array('name' => 'Visa Card', 'gateway_type_id' => GATEWAY_TYPE_CREDIT_CARD),
array('name' => 'MasterCard', 'gateway_type_id' => GATEWAY_TYPE_CREDIT_CARD),
array('name' => 'American Express', 'gateway_type_id' => GATEWAY_TYPE_CREDIT_CARD),
array('name' => 'Discover Card', 'gateway_type_id' => GATEWAY_TYPE_CREDIT_CARD),
array('name' => 'Diners Card', 'gateway_type_id' => GATEWAY_TYPE_CREDIT_CARD),
array('name' => 'EuroCard', 'gateway_type_id' => GATEWAY_TYPE_CREDIT_CARD),
array('name' => 'Nova', 'gateway_type_id' => GATEWAY_TYPE_CREDIT_CARD),
array('name' => 'Credit Card Other', 'gateway_type_id' => GATEWAY_TYPE_CREDIT_CARD),
array('name' => 'PayPal', 'gateway_type_id' => GATEWAY_TYPE_PAYPAL),
array('name' => 'Google Wallet'),
array('name' => 'Check'),
array('name' => 'Carte Blanche'),
array('name' => 'UnionPay'),
array('name' => 'JCB'),
array('name' => 'Laser'),
array('name' => 'Maestro'),
array('name' => 'Solo'),
array('name' => 'Switch'),
array('name' => 'Carte Blanche', 'gateway_type_id' => GATEWAY_TYPE_CREDIT_CARD),
array('name' => 'UnionPay', 'gateway_type_id' => GATEWAY_TYPE_CREDIT_CARD),
array('name' => 'JCB', 'gateway_type_id' => GATEWAY_TYPE_CREDIT_CARD),
array('name' => 'Laser', 'gateway_type_id' => GATEWAY_TYPE_CREDIT_CARD),
array('name' => 'Maestro', 'gateway_type_id' => GATEWAY_TYPE_CREDIT_CARD),
array('name' => 'Solo', 'gateway_type_id' => GATEWAY_TYPE_CREDIT_CARD),
array('name' => 'Switch', 'gateway_type_id' => GATEWAY_TYPE_CREDIT_CARD),
];
foreach ($paymentTypes as $paymentType) {
if (!DB::table('payment_types')->where('name', '=', $paymentType['name'])->get()) {
$record = PaymentType::where('name', '=', $paymentType['name'])->first();
if ( $record) {
$record->name = $paymentType['name'];
$record->gateway_type_id = ! empty($paymentType['gateway_type_id']) ? $paymentType['gateway_type_id'] : null;
$record->save();
} else {
PaymentType::create($paymentType);
}
}

View File

@ -13,6 +13,7 @@ class UpdateSeeder extends Seeder
$this->call('PaymentLibrariesSeeder');
$this->call('FontsSeeder');
$this->call('GatewayTypesSeeder');
$this->call('BanksSeeder');
$this->call('InvoiceStatusSeeder');
$this->call('PaymentStatusSeeder');

View File

@ -111,6 +111,7 @@ elixir(function(mix) {
//bowerDir + '/stacktrace-js/dist/stacktrace-with-polyfills.min.js',
bowerDir + '/fuse.js/src/fuse.js',
bowerDir + '/sweetalert/dist/sweetalert-dev.js',
bowerDir + '/nouislider/distribute/nouislider.js',
'bootstrap-combobox.js',
'script.js',
'pdf.pdfmake.js',

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1078,3 +1078,144 @@ div.panel-body div.panel-body {
width: 100%;
height: 100%;
}
/* Limits */
.noUi-target,
.noUi-target * {
-webkit-touch-callout: none;
-webkit-user-select: none;
-ms-touch-action: none;
touch-action: none;
-ms-user-select: none;
-moz-user-select: none;
user-select: none;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.noUi-target {
position: relative;
direction: ltr;
margin:0 17px;
}
.noUi-base {
width: 100%;
height: 100%;
position: relative;
z-index: 1; /* Fix 401 */
}
.noUi-origin {
position: absolute;
right: 0;
top: 0;
left: 0;
bottom: 0;
}
.noUi-handle {
position: relative;
z-index: 1;
}
.noUi-stacking .noUi-handle {
/* This class is applied to the lower origin when
its values is > 50%. */
z-index: 10;
}
.noUi-state-tap .noUi-origin {
-webkit-transition: left 0.3s, top 0.3s;
transition: left 0.3s, top 0.3s;
}
.noUi-state-drag * {
cursor: inherit !important;
}
.noUi-base,
.noUi-handle {
-webkit-transform: translate3d(0,0,0);
transform: translate3d(0,0,0);
}
.noUi-horizontal {
height: 15px;
}
.noUi-horizontal .noUi-handle {
width: 34px;
height: 25px;
left: -17px;
top: -6px;
}
.noUi-background {
background: #f9f9f9;
}
.noUi-connect {
background: #286090;
}
.noUi-origin {
border-radius: 2px;
}
.noUi-target {
border-radius: 4px;
border: 1px solid #ddd;
}
.noUi-draggable {
cursor: w-resize;
}
.noUi-vertical .noUi-draggable {
cursor: n-resize;
}
.noUi-handle {
border: 1px solid #777;
border-radius: 3px;
background: #FFF;
cursor: pointer;
}
.noUi-handle:before,
.noUi-handle:after {
content: "";
display: block;
position: absolute;
height: 13px;
width: 1px;
background: #777;
left: 14px;
top: 5px;
}
.noUi-handle:after {
left: 17px;
}
.noUi-vertical .noUi-handle:before,
.noUi-vertical .noUi-handle:after {
width: 14px;
height: 1px;
left: 6px;
top: 14px;
}
.noUi-vertical .noUi-handle:after {
top: 17px;
}
#payment-limits-slider{
margin-bottom:10px;
}
#payment-limit-min-container .input-group,
#payment-limit-max-container .input-group{
max-width:200px;
clear:both;
}
#payment-limit-max-container{
text-align:right;
}
#payment-limit-max-container .input-group,
#payment-limit-max-container label{
float:right;
}
#payment-limit-min[disabled],
#payment-limit-max[disabled]{
background-color: #eee!important;
color: #999!important;
}

View File

@ -2117,6 +2117,18 @@ $LANG = array(
'total_expenses' => 'Total Expenses',
'quote_to' => 'Quote to',
// Limits
'limit' => 'Limit',
'min_limit' => 'Min: :min',
'max_limit' => 'Max: :max',
'no_limit' => 'No Limits',
'set_limits' => 'Set :gateway_type Limits',
'enable_min' => 'Enable min',
'enable_max' => 'Enable max',
'min' => 'Min',
'max' => 'Max',
'limits_not_met' => 'This invoice does not meet the limits for that payment type.',
);
return $LANG;

View File

@ -166,6 +166,10 @@
->help(trans('texts.plaid_environment_help')) !!}
</div>
</div>
@elseif ($accountGateway && $accountGateway->gateway_id == GATEWAY_WEPAY)
{!! Former::checkbox('enable_ach')
->label(trans('texts.ach'))
->text(trans('texts.enable_ach')) !!}
@endif
</div>

View File

@ -50,15 +50,73 @@
{!! Datatable::table()
->addColumn(
trans('texts.name'),
trans('texts.limit'),
trans('texts.action'))
->setUrl(url('api/gateways/'))
->setOptions('sPaginationType', 'bootstrap')
->setOptions('bFilter', false)
->setOptions('bAutoWidth', false)
->setOptions('aoColumns', [[ "sWidth"=> "80%" ], ["sWidth"=> "20%"]])
->setOptions('aoColumns', [[ "sWidth"=> "50%" ], ["sWidth"=> "30%"], ["sWidth"=> "20%"]])
->setOptions('aoColumnDefs', [['bSortable'=>false, 'aTargets'=>[1]]])
->render('datatable') !!}
{!! Former::open( 'settings/payment_gateway_limits') !!}
<div class="modal fade" id="paymentLimitsModal" tabindex="-1" role="dialog"
aria-labelledby="paymentLimitsModalLabel"
aria-hidden="true">
<div class="modal-dialog" style="min-width:150px">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" id="paymentLimitsModalLabel"></h4>
</div>
<div class="modal-body">
<div class="row" style="text-align:center">
<div class="col-xs-12">
<div id="payment-limits-slider"></div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div id="payment-limit-min-container">
<label for="payment-limit-min">{{ trans('texts.min') }}</label><br>
<div class="input-group">
<span class="input-group-addon">{{ $currency->symbol }}</span>
<input type="number" class="form-control" min="0" id="payment-limit-min"
name="limit_min">
</div>
<label><input type="checkbox" id="payment-limit-min-enable"
name="limit_min_enable"> {{ trans('texts.enable_min') }}</label>
</div>
</div>
<div class="col-md-6">
<div id="payment-limit-max-container">
<label for="payment-limit-max">{{ trans('texts.max') }}</label><br>
<div class="input-group">
<span class="input-group-addon">{{ $currency->symbol }}</span>
<input type="number" class="form-control" min="0" id="payment-limit-max"
name="limit_max">
</div>
<label><input type="checkbox" id="payment-limit-max-enable"
name="limit_max_enable"> {{ trans('texts.enable_max') }}</label>
</div>
</div>
</div>
<input type="hidden" name="gateway_type_id" id="payment-limit-gateway-type">
</div>
<div class="modal-footer" style="margin-top: 0px">
<button type="button" class="btn btn-default"
data-dismiss="modal">{{ trans('texts.cancel') }}</button>
<button type="submit" class="btn btn-primary">{{ trans('texts.save') }}</button>
</div>
</div>
</div>
</div>
{!! Former::close() !!}
<script>
window.onDatatableReady = actionListHandler;
function setTrashVisible() {
@ -69,6 +127,89 @@
refreshDatatable();
})
}
function showLimitsModal(gateway_type, gateway_type_id, min_limit, max_limit) {
var modalLabel = {!! json_encode(trans('texts.set_limits')) !!};
$('#paymentLimitsModalLabel').text(modalLabel.replace(':gateway_type', gateway_type));
limitsSlider.noUiSlider.set([min_limit !== null ? min_limit : 0, max_limit !== null ? max_limit : 100000]);
if (min_limit !== null) {
$('#payment-limit-min').removeAttr('disabled');
$('#payment-limit-min-enable').prop('checked', true);
} else {
$('#payment-limit-min').attr('disabled', 'disabled');
$('#payment-limit-min-enable').prop('checked', false);
}
if (max_limit !== null) {
$('#payment-limit-max').removeAttr('disabled');
$('#payment-limit-max-enable').prop('checked', true);
} else {
$('#payment-limit-max').attr('disabled', 'disabled');
$('#payment-limit-max-enable').prop('checked', false);
}
$('#payment-limit-gateway-type').val(gateway_type_id);
$('#paymentLimitsModal').modal('show');
}
var limitsSlider = document.getElementById('payment-limits-slider');
noUiSlider.create(limitsSlider, {
start: [0, 100000],
connect: true,
range: {
'min': [0, 1],
'30%': [500, 1],
'70%': [5000, 1],
'max': [100000, 1]
}
});
limitsSlider.noUiSlider.on('update', function (values, handle) {
var value = values[handle];
if (handle == 1) {
$('#payment-limit-max').val(Math.round(value)).removeAttr('disabled');
$('#payment-limit-max-enable').prop('checked', true);
} else {
$('#payment-limit-min').val(Math.round(value)).removeAttr('disabled');
$('#payment-limit-min-enable').prop('checked', true);
}
});
$('#payment-limit-min').on('change keyup', function () {
setTimeout(function () {
limitsSlider.noUiSlider.set([$('#payment-limit-min').val(), null]);
}, 100);
$('#payment-limit-min-enable').attr('checked', 'checked');
});
$('#payment-limit-max').on('change keyup', function () {
setTimeout(function () {
limitsSlider.noUiSlider.set([null, $('#payment-limit-max').val()]);
}, 100);
$('#payment-limit-max-enable').attr('checked', 'checked');
});
$('#payment-limit-min-enable').change(function () {
if ($(this).is(':checked')) {
$('#payment-limit-min').removeAttr('disabled');
} else {
$('#payment-limit-min').attr('disabled', 'disabled');
}
});
$('#payment-limit-max-enable').change(function () {
if ($(this).is(':checked')) {
$('#payment-limit-max').removeAttr('disabled');
} else {
$('#payment-limit-max').attr('disabled', 'disabled');
}
});
</script>
@stop