diff --git a/app/Console/Commands/SendRecurringInvoices.php b/app/Console/Commands/SendRecurringInvoices.php
index abf493d1ca54..574d324d3842 100644
--- a/app/Console/Commands/SendRecurringInvoices.php
+++ b/app/Console/Commands/SendRecurringInvoices.php
@@ -8,6 +8,7 @@ use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use App\Ninja\Mailers\ContactMailer as Mailer;
use App\Ninja\Repositories\InvoiceRepository;
+use App\Services\PaymentService;
use App\Models\Invoice;
use App\Models\InvoiceItem;
use App\Models\Invitation;
@@ -18,13 +19,15 @@ class SendRecurringInvoices extends Command
protected $description = 'Send recurring invoices';
protected $mailer;
protected $invoiceRepo;
+ protected $paymentService;
- public function __construct(Mailer $mailer, InvoiceRepository $invoiceRepo)
+ public function __construct(Mailer $mailer, InvoiceRepository $invoiceRepo, PaymentService $paymentService)
{
parent::__construct();
$this->mailer = $mailer;
$this->invoiceRepo = $invoiceRepo;
+ $this->paymentService = $paymentService;
}
public function fire()
@@ -48,11 +51,49 @@ class SendRecurringInvoices extends Command
$invoice = $this->invoiceRepo->createRecurringInvoice($recurInvoice);
if ($invoice && !$invoice->isPaid()) {
+ $invoice->account->auto_bill_on_due_date;
+
+ $autoBillLater = false;
+ if ($invoice->account->auto_bill_on_due_date || $this->paymentService->getClientRequiresDelayedAutoBill($invoice->client)) {
+ $autoBillLater = true;
+ }
+
+ if($autoBillLater) {
+ if($paymentMethod = $this->paymentService->getClientDefaultPaymentMethod($invoice->client)) {
+ $invoice->autoBillPaymentMethod = $paymentMethod;
+ }
+ }
+
+
$this->info('Sending Invoice');
$this->mailer->sendInvoice($invoice);
}
}
+ $delayedAutoBillInvoices = Invoice::with('account.timezone', 'recurring_invoice', 'invoice_items', 'client', 'user')
+ ->whereRaw('is_deleted IS FALSE AND deleted_at IS NULL AND is_recurring IS FALSE
+ AND balance > 0 AND due_date = ? AND recurring_invoice_id IS NOT NULL',
+ array($today->format('Y-m-d')))
+ ->orderBy('invoices.id', 'asc')
+ ->get();
+ $this->info(count($delayedAutoBillInvoices).' due recurring invoice instance(s) found');
+
+ foreach ($delayedAutoBillInvoices as $invoice) {
+ $autoBill = $invoice->getAutoBillEnabled();
+ $billNow = false;
+
+ if ($autoBill && !$invoice->isPaid()) {
+ $billNow = $invoice->account->auto_bill_on_due_date || $this->paymentService->getClientRequiresDelayedAutoBill($invoice->client);
+ }
+
+ $this->info('Processing Invoice '.$invoice->id.' - Should bill '.($billNow ? 'YES' : 'NO'));
+
+ if ($billNow) {
+ // autoBillInvoice will check for changes to ACH invoices, so we're not checking here
+ $this->paymentService->autoBillInvoice($invoice);
+ }
+ }
+
$this->info('Done');
}
diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php
index 6882a44be1e6..4fa6e1b90126 100644
--- a/app/Http/Controllers/AccountController.php
+++ b/app/Http/Controllers/AccountController.php
@@ -430,10 +430,17 @@ class AccountController extends BaseController
$switchToWepay = !$account->getGatewayConfig(GATEWAY_BRAINTREE) && !$account->getGatewayConfig(GATEWAY_STRIPE);
}
+ $tokenBillingOptions = [];
+ for ($i=1; $i<=4; $i++) {
+ $tokenBillingOptions[$i] = trans("texts.token_billing_{$i}");
+ }
+
return View::make('accounts.payments', [
'showSwitchToWepay' => $switchToWepay,
'showAdd' => $count < count(Gateway::$paymentTypes),
- 'title' => trans('texts.online_payments')
+ 'title' => trans('texts.online_payments'),
+ 'tokenBillingOptions' => $tokenBillingOptions,
+ 'account' => $account,
]);
}
}
@@ -661,6 +668,8 @@ class AccountController extends BaseController
return AccountController::saveDetails();
} elseif ($section === ACCOUNT_LOCALIZATION) {
return AccountController::saveLocalization();
+ } elseif ($section == ACCOUNT_PAYMENTS) {
+ return self::saveOnlinePayments();
} elseif ($section === ACCOUNT_NOTIFICATIONS) {
return AccountController::saveNotifications();
} elseif ($section === ACCOUNT_EXPORT) {
@@ -1133,6 +1142,20 @@ class AccountController extends BaseController
return Redirect::to('settings/'.ACCOUNT_LOCALIZATION);
}
+ private function saveOnlinePayments()
+ {
+ $account = Auth::user()->account;
+ $account->token_billing_type_id = Input::get('token_billing_type_id');
+ $account->auto_bill_on_due_date = boolval(Input::get('auto_bill_on_due_date'));
+ $account->save();
+
+ event(new UserSettingsChanged());
+
+ Session::flash('message', trans('texts.updated_settings'));
+
+ return Redirect::to('settings/'.ACCOUNT_PAYMENTS);
+ }
+
public function removeLogo()
{
$account = Auth::user()->account;
diff --git a/app/Http/Controllers/AccountGatewayController.php b/app/Http/Controllers/AccountGatewayController.php
index 765fbc78a9d6..629932c567e8 100644
--- a/app/Http/Controllers/AccountGatewayController.php
+++ b/app/Http/Controllers/AccountGatewayController.php
@@ -141,11 +141,6 @@ class AccountGatewayController extends BaseController
}
}
- $tokenBillingOptions = [];
- for ($i=1; $i<=4; $i++) {
- $tokenBillingOptions[$i] = trans("texts.token_billing_{$i}");
- }
-
return [
'paymentTypes' => $paymentTypes,
'account' => $account,
@@ -154,7 +149,6 @@ class AccountGatewayController extends BaseController
'config' => false,
'gateways' => $gateways,
'creditCardTypes' => $creditCards,
- 'tokenBillingOptions' => $tokenBillingOptions,
'countGateways' => count($currentGateways)
];
}
@@ -301,7 +295,7 @@ class AccountGatewayController extends BaseController
$config->plaidPublicKey = $oldConfig->plaidPublicKey;
}
- if ($gatewayId == GATEWAY_STRIPE) {
+ if ($gatewayId == GATEWAY_STRIPE || $gatewayId == GATEWAY_WEPAY) {
$config->enableAch = boolval(Input::get('enable_ach'));
}
@@ -327,11 +321,6 @@ class AccountGatewayController extends BaseController
$account->account_gateways()->save($accountGateway);
}
- if (Input::get('token_billing_type_id')) {
- $account->token_billing_type_id = Input::get('token_billing_type_id');
- $account->save();
- }
-
if(isset($wepayResponse)) {
return $wepayResponse;
} else {
diff --git a/app/Http/Controllers/ClientAuth/AuthController.php b/app/Http/Controllers/ClientAuth/AuthController.php
index e3951b5464b8..24fc85222872 100644
--- a/app/Http/Controllers/ClientAuth/AuthController.php
+++ b/app/Http/Controllers/ClientAuth/AuthController.php
@@ -10,7 +10,7 @@ use App\Events\UserLoggedIn;
use App\Http\Controllers\Controller;
use App\Ninja\Repositories\AccountRepository;
use App\Services\AuthService;
-use App\Models\Invitation;
+use App\Models\Contact;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
class AuthController extends Controller {
@@ -22,16 +22,13 @@ class AuthController extends Controller {
public function showLoginForm()
{
- $data = array(
- );
+ $data = array();
- $invitation_key = session('invitation_key');
- if($invitation_key){
- $invitation = Invitation::where('invitation_key', '=', $invitation_key)->first();
- if ($invitation && !$invitation->is_deleted) {
- $invoice = $invitation->invoice;
- $client = $invoice->client;
- $account = $client->account;
+ $contactKey = session('contact_key');
+ if($contactKey){
+ $contact = Contact::where('contact_key', '=', $contactKey)->first();
+ if ($contact && !$contact->is_deleted) {
+ $account = $contact->account;
$data['account'] = $account;
$data['clientFontUrl'] = $account->getFontsUrl();
@@ -51,12 +48,12 @@ class AuthController extends Controller {
{
$credentials = $request->only('password');
$credentials['id'] = null;
-
- $invitation_key = session('invitation_key');
- if($invitation_key){
- $invitation = Invitation::where('invitation_key', '=', $invitation_key)->first();
- if ($invitation && !$invitation->is_deleted) {
- $credentials['id'] = $invitation->contact_id;
+
+ $contactKey = session('contact_key');
+ if($contactKey){
+ $contact = Contact::where('contact_key', '=', $contactKey)->first();
+ if ($contact && !$contact->is_deleted) {
+ $credentials['id'] = $contact->id;
}
}
@@ -75,4 +72,9 @@ class AuthController extends Controller {
'password' => 'required',
]);
}
+
+ public function getSessionExpired()
+ {
+ return view('clientauth.sessionexpired');
+ }
}
diff --git a/app/Http/Controllers/ClientAuth/PasswordController.php b/app/Http/Controllers/ClientAuth/PasswordController.php
index 822764315a0e..af3d97029edf 100644
--- a/app/Http/Controllers/ClientAuth/PasswordController.php
+++ b/app/Http/Controllers/ClientAuth/PasswordController.php
@@ -6,6 +6,7 @@ use Illuminate\Foundation\Auth\ResetsPasswords;
use Illuminate\Http\Request;
use Illuminate\Mail\Message;
use Illuminate\Support\Facades\Password;
+use App\Models\Contact;
use App\Models\Invitation;
@@ -42,16 +43,16 @@ class PasswordController extends Controller {
public function showLinkRequestForm()
{
$data = array();
- $invitation_key = session('invitation_key');
- if($invitation_key){
- $invitation = Invitation::where('invitation_key', '=', $invitation_key)->first();
- if ($invitation && !$invitation->is_deleted) {
- $invoice = $invitation->invoice;
- $client = $invoice->client;
- $account = $client->account;
+ $contactKey = session('contact_key');
+ if($contactKey){
+ $contact = Contact::where('contact_key', '=', $contactKey)->first();
+ if ($contact && !$contact->is_deleted) {
+ $account = $contact->account;
$data['account'] = $account;
$data['clientFontUrl'] = $account->getFontsUrl();
}
+ } else {
+ return \Redirect::to('/client/sessionexpired');
}
return view('clientauth.password')->with($data);
@@ -67,16 +68,16 @@ class PasswordController extends Controller {
{
$broker = $this->getBroker();
- $contact_id = null;
- $invitation_key = session('invitation_key');
- if($invitation_key){
- $invitation = Invitation::where('invitation_key', '=', $invitation_key)->first();
- if ($invitation && !$invitation->is_deleted) {
- $contact_id = $invitation->contact_id;
+ $contactId = null;
+ $contactKey = session('contact_key');
+ if($contactKey){
+ $contact = Contact::where('contact_key', '=', $contactKey)->first();
+ if ($contact && !$contact->is_deleted) {
+ $contactId = $contact->id;
}
}
- $response = Password::broker($broker)->sendResetLink(array('id'=>$contact_id), function (Message $message) {
+ $response = Password::broker($broker)->sendResetLink(array('id'=>$contactId), function (Message $message) {
$message->subject($this->getEmailSubject());
});
@@ -96,27 +97,36 @@ class PasswordController extends Controller {
* If no token is present, display the link request form.
*
* @param \Illuminate\Http\Request $request
- * @param string|null $invitation_key
+ * @param string|null $key
* @param string|null $token
* @return \Illuminate\Http\Response
*/
- public function showResetForm(Request $request, $invitation_key = null, $token = null)
+ public function showResetForm(Request $request, $key = null, $token = null)
{
if (is_null($token)) {
return $this->getEmail();
}
- $data = compact('token', 'invitation_key');
- $invitation_key = session('invitation_key');
- if($invitation_key){
- $invitation = Invitation::where('invitation_key', '=', $invitation_key)->first();
- if ($invitation && !$invitation->is_deleted) {
- $invoice = $invitation->invoice;
- $client = $invoice->client;
- $account = $client->account;
+ $data = compact('token');
+ if($key) {
+ $contact = Contact::where('contact_key', '=', $key)->first();
+ if ($contact && !$contact->is_deleted) {
+ $account = $contact->account;
+ $data['contact_key'] = $contact->contact_key;
+ } else {
+ // Maybe it's an invitation key
+ $invitation = Invitation::where('invitation_key', '=', $key)->first();
+ if ($invitation && !$invitation->is_deleted) {
+ $account = $invitation->account;
+ $data['contact_key'] = $invitation->contact->contact_key;
+ }
+ }
+ if (!empty($account)) {
$data['account'] = $account;
$data['clientFontUrl'] = $account->getFontsUrl();
+ } else {
+ return \Redirect::to('/client/sessionexpired');
}
}
@@ -131,13 +141,13 @@ class PasswordController extends Controller {
* If no token is present, display the link request form.
*
* @param \Illuminate\Http\Request $request
- * @param string|null $invitation_key
+ * @param string|null $key
* @param string|null $token
* @return \Illuminate\Http\Response
*/
- public function getReset(Request $request, $invitation_key = null, $token = null)
+ public function getReset(Request $request, $key = null, $token = null)
{
- return $this->showResetForm($request, $invitation_key, $token);
+ return $this->showResetForm($request, $key, $token);
}
/**
@@ -155,12 +165,12 @@ class PasswordController extends Controller {
);
$credentials['id'] = null;
-
- $invitation_key = $request->input('invitation_key');
- if($invitation_key){
- $invitation = Invitation::where('invitation_key', '=', $invitation_key)->first();
- if ($invitation && !$invitation->is_deleted) {
- $credentials['id'] = $invitation->contact_id;
+
+ $contactKey = session('contact_key');
+ if($contactKey){
+ $contact = Contact::where('contact_key', '=', $contactKey)->first();
+ if ($contact && !$contact->is_deleted) {
+ $credentials['id'] = $contact->id;
}
}
diff --git a/app/Http/Controllers/ClientPortalController.php b/app/Http/Controllers/ClientPortalController.php
index ba025e283f1b..8d66e18293d1 100644
--- a/app/Http/Controllers/ClientPortalController.php
+++ b/app/Http/Controllers/ClientPortalController.php
@@ -17,6 +17,7 @@ use App\Models\Gateway;
use App\Models\Invitation;
use App\Models\Document;
use App\Models\PaymentMethod;
+use App\Models\Contact;
use App\Ninja\Repositories\InvoiceRepository;
use App\Ninja\Repositories\PaymentRepository;
use App\Ninja\Repositories\ActivityRepository;
@@ -70,7 +71,7 @@ class ClientPortalController extends BaseController
}
Session::put($invitationKey, true); // track this invitation has been seen
- Session::put('invitation_key', $invitationKey); // track current invitation
+ Session::put('contact_key', $invitation->contact->contact_key);// track current contact
$account->loadLocalizationSettings($client);
@@ -110,6 +111,8 @@ class ClientPortalController extends BaseController
if($braintreeGateway->getPayPalEnabled()) {
$data['braintreeClientToken'] = $this->paymentService->getBraintreeClientToken($account);
}
+ } elseif ($wepayGateway = $account->getGatewayConfig(GATEWAY_WEPAY)){
+ $data['enableWePayACH'] = $wepayGateway->getAchEnabled();
}
$showApprove = $invoice->quote_invoice_id ? false : true;
@@ -161,6 +164,18 @@ class ClientPortalController extends BaseController
return View::make('invoices.view', $data);
}
+
+ public function contactIndex($contactKey) {
+ if (!$contact = Contact::where('contact_key', '=', $contactKey)->first()) {
+ return $this->returnError();
+ }
+
+ $client = $contact->client;
+
+ Session::put('contact_key', $contactKey);// track current contact
+
+ return redirect()->to($client->account->enable_client_portal?'/client/dashboard':'/client/invoices/');
+ }
private function getPaymentTypes($client, $invitation)
{
@@ -176,8 +191,8 @@ class ClientPortalController extends BaseController
$html = '';
if ($paymentMethod->payment_type_id == PAYMENT_TYPE_ACH) {
- if ($paymentMethod->bank_data) {
- $html = '
' . htmlentities($paymentMethod->bank_data->name) . '
';
+ if ($paymentMethod->bank_name) {
+ $html = '' . htmlentities($paymentMethod->bank_name) . '
';
} else {
$html = '
';
}
@@ -211,43 +226,39 @@ class ClientPortalController extends BaseController
foreach(Gateway::$paymentTypes as $type) {
if ($gateway = $account->getGatewayByType($type)) {
- $types = array($type);
-
- if ($type == PAYMENT_TYPE_STRIPE) {
- $types = array(PAYMENT_TYPE_STRIPE_CREDIT_CARD);
- if ($gateway->getAchEnabled()) {
- $types[] = PAYMENT_TYPE_STRIPE_ACH;
+ if ($type == PAYMENT_TYPE_DIRECT_DEBIT) {
+ if ($gateway->gateway_id == GATEWAY_STRIPE) {
+ $type = PAYMENT_TYPE_STRIPE_ACH;
+ } elseif ($gateway->gateway_id == GATEWAY_WEPAY) {
+ $type = PAYMENT_TYPE_WEPAY_ACH;
}
+ } elseif ($type == PAYMENT_TYPE_PAYPAL && $gateway->gateway_id == GATEWAY_BRAINTREE) {
+ $type = PAYMENT_TYPE_BRAINTREE_PAYPAL;
+ } elseif ($type == PAYMENT_TYPE_CREDIT_CARD && $gateway->gateway_id == GATEWAY_STRIPE) {
+ $type = PAYMENT_TYPE_STRIPE_CREDIT_CARD;
}
- foreach($types as $type) {
- $typeLink = strtolower(str_replace('PAYMENT_TYPE_', '', $type));
- $url = URL::to("/payment/{$invitation->invitation_key}/{$typeLink}");
+ $typeLink = strtolower(str_replace('PAYMENT_TYPE_', '', $type));
+ $url = URL::to("/payment/{$invitation->invitation_key}/{$typeLink}");
- // PayPal doesn't allow being run in an iframe so we need to open in new tab
- if ($type === PAYMENT_TYPE_PAYPAL && $account->iframe_url) {
- $url = 'javascript:window.open("' . $url . '", "_blank")';
- }
-
- if ($type == PAYMENT_TYPE_STRIPE_CREDIT_CARD) {
- $label = trans('texts.' . strtolower(PAYMENT_TYPE_CREDIT_CARD));
- } elseif ($type == PAYMENT_TYPE_STRIPE_ACH) {
- $label = trans('texts.' . strtolower(PAYMENT_TYPE_DIRECT_DEBIT));
- } else {
- $label = trans('texts.' . strtolower($type));
- }
-
- $paymentTypes[] = [
- 'url' => $url, 'label' => $label
- ];
-
- if($gateway->getPayPalEnabled()) {
- $paymentTypes[] = [
- 'label' => trans('texts.paypal'),
- 'url' => $url = URL::to("/payment/{$invitation->invitation_key}/braintree_paypal"),
- ];
- }
+ // PayPal doesn't allow being run in an iframe so we need to open in new tab
+ if ($type === PAYMENT_TYPE_PAYPAL && $account->iframe_url) {
+ $url = 'javascript:window.open("' . $url . '", "_blank")';
}
+
+ if ($type == PAYMENT_TYPE_STRIPE_CREDIT_CARD) {
+ $label = trans('texts.' . strtolower(PAYMENT_TYPE_CREDIT_CARD));
+ } elseif ($type == PAYMENT_TYPE_STRIPE_ACH || $type == PAYMENT_TYPE_WEPAY_ACH) {
+ $label = trans('texts.' . strtolower(PAYMENT_TYPE_DIRECT_DEBIT));
+ } elseif ($type == PAYMENT_TYPE_BRAINTREE_PAYPAL) {
+ $label = trans('texts.' . strtolower(PAYMENT_TYPE_PAYPAL));
+ } else {
+ $label = trans('texts.' . strtolower($type));
+ }
+
+ $paymentTypes[] = [
+ 'url' => $url, 'label' => $label
+ ];
}
}
@@ -277,13 +288,12 @@ class ClientPortalController extends BaseController
public function dashboard()
{
- if (!$invitation = $this->getInvitation()) {
+ if (!$contact = $this->getContact()) {
return $this->returnError();
}
- $account = $invitation->account;
- $invoice = $invitation->invoice;
- $client = $invoice->client;
+ $client = $contact->client;
+ $account = $client->account;
$color = $account->primary_color ? $account->primary_color : '#0b4d78';
if (!$account->enable_client_portal || !$account->enable_client_portal_dashboard) {
@@ -292,6 +302,7 @@ class ClientPortalController extends BaseController
$data = [
'color' => $color,
+ 'contact' => $contact,
'account' => $account,
'client' => $client,
'clientFontUrl' => $account->getFontsUrl(),
@@ -310,12 +321,13 @@ class ClientPortalController extends BaseController
public function activityDatatable()
{
- if (!$invitation = $this->getInvitation()) {
- return false;
+ if (!$contact = $this->getContact()) {
+ return $this->returnError();
}
- $invoice = $invitation->invoice;
- $query = $this->activityRepo->findByClientId($invoice->client_id);
+ $client = $contact->client;
+
+ $query = $this->activityRepo->findByClientId($client->id);
$query->where('activities.adjustment', '!=', 0);
return Datatable::query($query)
@@ -341,11 +353,11 @@ class ClientPortalController extends BaseController
public function recurringInvoiceIndex()
{
- if (!$invitation = $this->getInvitation()) {
+ if (!$contact = $this->getContact()) {
return $this->returnError();
}
- $account = $invitation->account;
+ $account = $contact->account;
if (!$account->enable_client_portal) {
return $this->returnError();
@@ -356,7 +368,7 @@ class ClientPortalController extends BaseController
$data = [
'color' => $color,
'account' => $account,
- 'client' => $invitation->invoice->client,
+ 'client' => $contact->client,
'clientFontUrl' => $account->getFontsUrl(),
'title' => trans('texts.recurring_invoices'),
'entityType' => ENTITY_RECURRING_INVOICE,
@@ -368,11 +380,11 @@ class ClientPortalController extends BaseController
public function invoiceIndex()
{
- if (!$invitation = $this->getInvitation()) {
+ if (!$contact = $this->getContact()) {
return $this->returnError();
}
- $account = $invitation->account;
+ $account = $contact->account;
if (!$account->enable_client_portal) {
return $this->returnError();
@@ -383,7 +395,7 @@ class ClientPortalController extends BaseController
$data = [
'color' => $color,
'account' => $account,
- 'client' => $invitation->invoice->client,
+ 'client' => $contact->client,
'clientFontUrl' => $account->getFontsUrl(),
'title' => trans('texts.invoices'),
'entityType' => ENTITY_INVOICE,
@@ -395,29 +407,30 @@ class ClientPortalController extends BaseController
public function invoiceDatatable()
{
- if (!$invitation = $this->getInvitation()) {
+ if (!$contact = $this->getContact()) {
return '';
}
- return $this->invoiceRepo->getClientDatatable($invitation->contact_id, ENTITY_INVOICE, Input::get('sSearch'));
+ return $this->invoiceRepo->getClientDatatable($contact->id, ENTITY_INVOICE, Input::get('sSearch'));
}
public function recurringInvoiceDatatable()
{
- if (!$invitation = $this->getInvitation()) {
+ if (!$contact = $this->getContact()) {
return '';
}
- return $this->invoiceRepo->getClientRecurringDatatable($invitation->contact_id);
+ return $this->invoiceRepo->getClientRecurringDatatable($contact->id);
}
public function paymentIndex()
{
- if (!$invitation = $this->getInvitation()) {
+ if (!$contact = $this->getContact()) {
return $this->returnError();
}
- $account = $invitation->account;
+
+ $account = $contact->account;
if (!$account->enable_client_portal) {
return $this->returnError();
@@ -438,10 +451,10 @@ class ClientPortalController extends BaseController
public function paymentDatatable()
{
- if (!$invitation = $this->getInvitation()) {
- return false;
+ if (!$contact = $this->getContact()) {
+ return $this->returnError();
}
- $payments = $this->paymentRepo->findForContact($invitation->contact->id, Input::get('sSearch'));
+ $payments = $this->paymentRepo->findForContact($contact->id, Input::get('sSearch'));
return Datatable::query($payments)
->addColumn('invoice_number', function ($model) { return $model->invitation_key ? link_to('/view/'.$model->invitation_key, $model->invoice_number)->toHtml() : $model->invoice_number; })
@@ -458,9 +471,16 @@ class ClientPortalController extends BaseController
return $model->email;
}
} elseif ($model->last4) {
- $bankData = PaymentMethod::lookupBankData($model->routing_number);
- if (is_object($bankData)) {
- return $bankData->name.' •••' . $model->last4;
+ if($model->bank_name) {
+ $bankName = $model->bank_name;
+ } else {
+ $bankData = PaymentMethod::lookupBankData($model->routing_number);
+ if($bankData) {
+ $bankName = $bankData->name;
+ }
+ }
+ if (!empty($bankName)) {
+ return $bankName.' •••' . $model->last4;
} elseif($model->last4) {
return '
•••' . $model->last4;
}
@@ -502,11 +522,11 @@ class ClientPortalController extends BaseController
public function quoteIndex()
{
- if (!$invitation = $this->getInvitation()) {
+ if (!$contact = $this->getContact()) {
return $this->returnError();
}
- $account = $invitation->account;
+ $account = $contact->account;
if (!$account->enable_client_portal) {
return $this->returnError();
@@ -528,20 +548,20 @@ class ClientPortalController extends BaseController
public function quoteDatatable()
{
- if (!$invitation = $this->getInvitation()) {
+ if (!$contact = $this->getContact()) {
return false;
}
- return $this->invoiceRepo->getClientDatatable($invitation->contact_id, ENTITY_QUOTE, Input::get('sSearch'));
+ return $this->invoiceRepo->getClientDatatable($contact->id, ENTITY_QUOTE, Input::get('sSearch'));
}
public function documentIndex()
{
- if (!$invitation = $this->getInvitation()) {
+ if (!$contact = $this->getContact()) {
return $this->returnError();
}
- $account = $invitation->account;
+ $account = $contact->account;
if (!$account->enable_client_portal) {
return $this->returnError();
@@ -563,11 +583,11 @@ class ClientPortalController extends BaseController
public function documentDatatable()
{
- if (!$invitation = $this->getInvitation()) {
+ if (!$contact = $this->getContact()) {
return false;
}
- return $this->documentRepo->getClientDatatable($invitation->contact_id, ENTITY_DOCUMENT, Input::get('sSearch'));
+ return $this->documentRepo->getClientDatatable($contact->id, ENTITY_DOCUMENT, Input::get('sSearch'));
}
private function returnError($error = false)
@@ -578,36 +598,28 @@ class ClientPortalController extends BaseController
]);
}
- private function getInvitation()
- {
- $invitationKey = session('invitation_key');
+ private function getContact() {
+ $contactKey = session('contact_key');
- if (!$invitationKey) {
+ if (!$contactKey) {
return false;
}
- $invitation = Invitation::where('invitation_key', '=', $invitationKey)->first();
+ $contact = Contact::where('contact_key', '=', $contactKey)->first();
- if (!$invitation || $invitation->is_deleted) {
+ if (!$contact || $contact->is_deleted) {
return false;
}
- $invoice = $invitation->invoice;
-
- if (!$invoice || $invoice->is_deleted) {
- return false;
- }
-
- return $invitation;
+ return $contact;
}
public function getDocumentVFSJS($publicId, $name){
- if (!$invitation = $this->getInvitation()) {
+ if (!$contact = $this->getContact()) {
return $this->returnError();
}
- $clientId = $invitation->invoice->client_id;
- $document = Document::scope($publicId, $invitation->account_id)->first();
+ $document = Document::scope($publicId, $contact->account_id)->first();
if(!$document->isPDFEmbeddable()){
@@ -615,9 +627,9 @@ class ClientPortalController extends BaseController
}
$authorized = false;
- if($document->expense && $document->expense->client_id == $invitation->invoice->client_id){
+ if($document->expense && $document->expense->client_id == $contact->client_id){
$authorized = true;
- } else if($document->invoice && $document->invoice->client_id == $invitation->invoice->client_id){
+ } else if($document->invoice && $document->invoice->client_id ==$contact->client_id){
$authorized = true;
}
@@ -692,7 +704,7 @@ class ClientPortalController extends BaseController
return $this->returnError();
}
- Session::put('invitation_key', $invitationKey); // track current invitation
+ Session::put('contact_key', $invitation->contact->contact_key);// track current contact
$invoice = $invitation->invoice;
@@ -725,7 +737,7 @@ class ClientPortalController extends BaseController
return $this->returnError();
}
- Session::put('invitation_key', $invitationKey); // track current invitation
+ Session::put('contact_key', $invitation->contact->contact_key);// track current contact
$clientId = $invitation->invoice->client_id;
$document = Document::scope($publicId, $invitation->account_id)->firstOrFail();
@@ -746,16 +758,17 @@ class ClientPortalController extends BaseController
public function paymentMethods()
{
- if (!$invitation = $this->getInvitation()) {
+ if (!$contact = $this->getContact()) {
return $this->returnError();
}
- $client = $invitation->invoice->client;
+ $client = $contact->client;
$account = $client->account;
$paymentMethods = $this->paymentService->getClientPaymentMethods($client);
$data = array(
'account' => $account,
+ 'contact' => $contact,
'color' => $account->primary_color ? $account->primary_color : '#0b4d78',
'client' => $client,
'clientViewCSS' => $account->clientViewCSS(),
@@ -780,11 +793,11 @@ class ClientPortalController extends BaseController
$amount1 = Input::get('verification1');
$amount2 = Input::get('verification2');
- if (!$invitation = $this->getInvitation()) {
+ if (!$contact = $this->getContact()) {
return $this->returnError();
}
- $client = $invitation->invoice->client;
+ $client = $contact->client;
$result = $this->paymentService->verifyClientPaymentMethod($client, $publicId, $amount1, $amount2);
if (is_string($result)) {
@@ -798,11 +811,11 @@ class ClientPortalController extends BaseController
public function removePaymentMethod($publicId)
{
- if (!$invitation = $this->getInvitation()) {
+ if (!$contact = $this->getContact()) {
return $this->returnError();
}
- $client = $invitation->invoice->client;
+ $client = $contact->client;
$result = $this->paymentService->removeClientPaymentMethod($client, $publicId);
if (is_string($result)) {
@@ -816,21 +829,20 @@ class ClientPortalController extends BaseController
public function addPaymentMethod($paymentType, $token=false)
{
- if (!$invitation = $this->getInvitation()) {
+ if (!$contact = $this->getContact()) {
return $this->returnError();
}
- $invoice = $invitation->invoice;
- $client = $invitation->invoice->client;
+ $client = $contact->client;
$account = $client->account;
$typeLink = $paymentType;
$paymentType = 'PAYMENT_TYPE_' . strtoupper($paymentType);
- $accountGateway = $invoice->client->account->getTokenGateway();
+ $accountGateway = $client->account->getTokenGateway();
$gateway = $accountGateway->gateway;
if ($token && $paymentType == PAYMENT_TYPE_BRAINTREE_PAYPAL) {
- $sourceReference = $this->paymentService->createToken($this->paymentService->createGateway($accountGateway), array('token'=>$token), $accountGateway, $client, $invitation->contact_id);
+ $sourceReference = $this->paymentService->createToken($paymentType, $this->paymentService->createGateway($accountGateway), array('token'=>$token), $accountGateway, $client, $contact->id);
if(empty($sourceReference)) {
$this->paymentMethodError('Token-No-Ref', $this->paymentService->lastError, $accountGateway);
@@ -846,7 +858,7 @@ class ClientPortalController extends BaseController
$data = [
'showBreadcrumbs' => false,
'client' => $client,
- 'contact' => $invitation->contact,
+ 'contact' => $contact,
'gateway' => $gateway,
'accountGateway' => $accountGateway,
'acceptedCreditCardTypes' => $acceptedCreditCardTypes,
@@ -859,8 +871,12 @@ class ClientPortalController extends BaseController
'clientFontUrl' => $account->getFontsUrl(),
'showAddress' => $accountGateway->show_address,
'paymentTitle' => trans('texts.add_payment_method'),
+ 'sourceId' => $token,
];
+ $details = json_decode(Input::get('details'));
+ $data['details'] = $details;
+
if ($paymentType == PAYMENT_TYPE_STRIPE_ACH) {
$data['currencies'] = Cache::get('currencies');
}
@@ -869,7 +885,7 @@ class ClientPortalController extends BaseController
$data['braintreeClientToken'] = $this->paymentService->getBraintreeClientToken($account);
}
- if(!empty($data['braintreeClientToken']) || $accountGateway->getPublishableStripeKey()|| $accountGateway->gateway_id == GATEWAY_WEPAY) {
+ if(!empty($data['braintreeClientToken']) || $accountGateway->getPublishableStripeKey()|| ($accountGateway->gateway_id == GATEWAY_WEPAY && $paymentType != PAYMENT_TYPE_WEPAY_ACH)) {
$data['tokenize'] = true;
}
@@ -878,20 +894,21 @@ class ClientPortalController extends BaseController
public function postAddPaymentMethod($paymentType)
{
- if (!$invitation = $this->getInvitation()) {
+ if (!$contact = $this->getContact()) {
return $this->returnError();
}
+ $client = $contact->client;
+
$typeLink = $paymentType;
$paymentType = 'PAYMENT_TYPE_' . strtoupper($paymentType);
- $client = $invitation->invoice->client;
$account = $client->account;
$accountGateway = $account->getGatewayByType($paymentType);
$sourceToken = Input::get('sourceToken');
if (($validator = PaymentController::processPaymentClientDetails($client, $accountGateway, $paymentType)) !== true) {
- return Redirect::to('client/paymentmethods/add/' . $typeLink)
+ return Redirect::to('client/paymentmethods/add/' . $typeLink.'/'.$sourceToken)
->withErrors($validator)
->withInput(Request::except('cvv'));
}
@@ -903,21 +920,26 @@ class ClientPortalController extends BaseController
$details = array('plaidPublicToken' => Input::get('plaidPublicToken'), 'plaidAccountId' => Input::get('plaidAccountId'));
}
- if ($paymentType == PAYMENT_TYPE_STRIPE_ACH && !Input::get('authorize_ach')) {
+ if (($paymentType == PAYMENT_TYPE_STRIPE_ACH || $paymentType == PAYMENT_TYPE_WEPAY_ACH) && !Input::get('authorize_ach')) {
Session::flash('error', trans('texts.ach_authorization_required'));
- return Redirect::to('client/paymentmethods/add/' . $typeLink)->withInput(Request::except('cvv'));
+ return Redirect::to('client/paymentmethods/add/' . $typeLink.'/'.$sourceToken)->withInput(Request::except('cvv'));
+ }
+
+ if ($paymentType == PAYMENT_TYPE_WEPAY_ACH && !Input::get('tos_agree')) {
+ Session::flash('error', trans('texts.wepay_payment_tos_agree_required'));
+ return Redirect::to('client/paymentmethods/add/' . $typeLink.'/'.$sourceToken)->withInput(Request::except('cvv'));
}
if (!empty($details)) {
$gateway = $this->paymentService->createGateway($accountGateway);
- $sourceReference = $this->paymentService->createToken($gateway, $details, $accountGateway, $client, $invitation->contact_id);
+ $sourceReference = $this->paymentService->createToken($paymentType, $gateway, $details, $accountGateway, $client, $contact->id);
} else {
- return Redirect::to('client/paymentmethods/add/' . $typeLink)->withInput(Request::except('cvv'));
+ return Redirect::to('client/paymentmethods/add/' . $typeLink.'/'.$sourceToken)->withInput(Request::except('cvv'));
}
if(empty($sourceReference)) {
$this->paymentMethodError('Token-No-Ref', $this->paymentService->lastError, $accountGateway);
- return Redirect::to('client/paymentmethods/add/' . $typeLink)->withInput(Request::except('cvv'));
+ return Redirect::to('client/paymentmethods/add/' . $typeLink.'/'.$sourceToken)->withInput(Request::except('cvv'));
} else if ($paymentType == PAYMENT_TYPE_STRIPE_ACH && empty($usingPlaid) ) {
// The user needs to complete verification
Session::flash('message', trans('texts.bank_account_verification_next_steps'));
@@ -929,12 +951,13 @@ class ClientPortalController extends BaseController
}
public function setDefaultPaymentMethod(){
- if (!$invitation = $this->getInvitation()) {
+ if (!$contact = $this->getContact()) {
return $this->returnError();
}
+ $client = $contact->client;
+
$validator = Validator::make(Input::all(), array('source' => 'required'));
- $client = $invitation->invoice->client;
if ($validator->fails()) {
return Redirect::to($client->account->enable_client_portal?'/client/dashboard':'/client/paymentmethods/');
}
@@ -963,12 +986,13 @@ class ClientPortalController extends BaseController
}
public function setAutoBill(){
- if (!$invitation = $this->getInvitation()) {
+ if (!$contact = $this->getContact()) {
return $this->returnError();
}
+ $client = $contact->client;
+
$validator = Validator::make(Input::all(), array('public_id' => 'required'));
- $client = $invitation->invoice->client;
if ($validator->fails()) {
return Redirect::to('client/invoices/recurring');
diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php
index da9b90467350..dcef1b61464e 100644
--- a/app/Http/Controllers/InvoiceController.php
+++ b/app/Http/Controllers/InvoiceController.php
@@ -26,6 +26,7 @@ use App\Ninja\Repositories\InvoiceRepository;
use App\Ninja\Repositories\ClientRepository;
use App\Ninja\Repositories\DocumentRepository;
use App\Services\InvoiceService;
+use App\Services\PaymentService;
use App\Services\RecurringInvoiceService;
use App\Http\Requests\InvoiceRequest;
@@ -39,10 +40,11 @@ class InvoiceController extends BaseController
protected $clientRepo;
protected $documentRepo;
protected $invoiceService;
+ protected $paymentService;
protected $recurringInvoiceService;
protected $entityType = ENTITY_INVOICE;
- public function __construct(Mailer $mailer, InvoiceRepository $invoiceRepo, ClientRepository $clientRepo, InvoiceService $invoiceService, DocumentRepository $documentRepo, RecurringInvoiceService $recurringInvoiceService)
+ public function __construct(Mailer $mailer, InvoiceRepository $invoiceRepo, ClientRepository $clientRepo, InvoiceService $invoiceService, DocumentRepository $documentRepo, RecurringInvoiceService $recurringInvoiceService, PaymentService $paymentService)
{
// parent::__construct();
@@ -51,6 +53,7 @@ class InvoiceController extends BaseController
$this->clientRepo = $clientRepo;
$this->invoiceService = $invoiceService;
$this->recurringInvoiceService = $recurringInvoiceService;
+ $this->paymentService = $paymentService;
}
public function index()
@@ -196,6 +199,10 @@ class InvoiceController extends BaseController
'lastSent' => $lastSent);
$data = array_merge($data, self::getViewModel($invoice));
+ if ($invoice->isSent() && $invoice->getAutoBillEnabled() && !$invoice->isPaid()) {
+ $data['autoBillChangeWarning'] = $this->paymentService->getClientRequiresDelayedAutoBill($invoice->client);
+ }
+
if ($clone) {
$data['formIsChanged'] = true;
}
diff --git a/app/Http/Controllers/PaymentController.php b/app/Http/Controllers/PaymentController.php
index c0dd4a9f8550..fcd6411cddc5 100644
--- a/app/Http/Controllers/PaymentController.php
+++ b/app/Http/Controllers/PaymentController.php
@@ -136,7 +136,6 @@ class PaymentController extends BaseController
public function show_payment($invitationKey, $paymentType = false, $sourceId = false)
{
-
$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;
@@ -154,7 +153,26 @@ class PaymentController extends BaseController
Session::put($invitation->id.'payment_ref', $invoice->id.'_'.uniqid());
- if ($paymentType != PAYMENT_TYPE_BRAINTREE_PAYPAL) {
+ $details = json_decode(Input::get('details'));
+ $data['details'] = $details;
+
+
+ if ($paymentType == PAYMENT_TYPE_BRAINTREE_PAYPAL) {
+ if ($deviceData = Input::get('device_data')) {
+ Session::put($invitation->id . 'device_data', $deviceData);
+ }
+
+ Session::put($invitation->id . 'payment_type', PAYMENT_TYPE_BRAINTREE_PAYPAL);
+ if (!$sourceId || !$details) {
+ return Redirect::to('view/'.$invitationKey);
+ }
+ } elseif ($paymentType == PAYMENT_TYPE_WEPAY_ACH) {
+ Session::put($invitation->id . 'payment_type', PAYMENT_TYPE_WEPAY_ACH);
+
+ if (!$sourceId) {
+ return Redirect::to('view/'.$invitationKey);
+ }
+ } else {
if ($paymentType == PAYMENT_TYPE_TOKEN) {
$useToken = true;
$accountGateway = $invoice->client->account->getTokenGateway();
@@ -204,17 +222,6 @@ class PaymentController extends BaseController
$data['tokenize'] = true;
}
- } else {
- if ($deviceData = Input::get('details')) {
- Session::put($invitation->id . 'device_data', $deviceData);
- }
-
- Session::put($invitation->id . 'payment_type', PAYMENT_TYPE_BRAINTREE_PAYPAL);
- $paypalDetails = json_decode(Input::get('details'));
- if (!$sourceId || !$paypalDetails) {
- return Redirect::to('view/'.$invitationKey);
- }
- $data['paypalDetails'] = $paypalDetails;
}
$data += [
@@ -405,7 +412,7 @@ class PaymentController extends BaseController
}
public static function processPaymentClientDetails($client, $accountGateway, $paymentType, $onSite = true){
- $rules = $paymentType == PAYMENT_TYPE_STRIPE_ACH ? [] : [
+ $rules = ($paymentType == PAYMENT_TYPE_STRIPE_ACH || $paymentType == PAYMENT_TYPE_WEPAY_ACH)? [] : [
'first_name' => 'required',
'last_name' => 'required',
];
@@ -422,7 +429,7 @@ class PaymentController extends BaseController
);
}
- $requireAddress = $accountGateway->show_address && $paymentType != PAYMENT_TYPE_STRIPE_ACH && $paymentType != PAYMENT_TYPE_BRAINTREE_PAYPAL;
+ $requireAddress = $accountGateway->show_address && $paymentType != PAYMENT_TYPE_STRIPE_ACH && $paymentType != PAYMENT_TYPE_BRAINTREE_PAYPAL && $paymentType != PAYMENT_TYPE_WEPAY_ACH;
if ($requireAddress) {
$rules = array_merge($rules, [
@@ -473,6 +480,21 @@ class PaymentController extends BaseController
$customerReference = $client->getGatewayToken($accountGateway, $accountGatewayToken/* return parameter*/);
$paymentMethod = PaymentMethod::scope($sourceId, $account->id, $accountGatewayToken->id)->firstOrFail();
$sourceReference = $paymentMethod->source_reference;
+
+ // What type of payment is this?
+ if ($paymentMethod->payment_type_id == PAYMENT_TYPE_ACH) {
+ if ($accountGateway->gateway_id == GATEWAY_STRIPE) {
+ $paymentType = PAYMENT_TYPE_STRIPE_ACH;
+ } elseif ($accountGateway->gateway_id == GATEWAY_WEPAY) {
+ $paymentType = PAYMENT_TYPE_WEPAY_ACH;
+ }
+ } elseif ($paymentMethod->payment_type_id == PAYMENT_TYPE_ID_PAYPAL && $accountGateway->gateway_id == GATEWAY_BRAINTREE) {
+ $paymentType = PAYMENT_TYPE_BRAINTREE_PAYPAL;
+ } elseif ($accountGateway->gateway_id == GATEWAY_STRIPE) {
+ $paymentType = PAYMENT_TYPE_STRIPE_CREDIT_CARD;
+ } else {
+ $paymentType = PAYMENT_TYPE_CREDIT_CARD;
+ }
}
}
@@ -482,7 +504,6 @@ class PaymentController extends BaseController
->withInput(Request::except('cvv'));
}
-
try {
// For offsite payments send the client's details on file
// If we're using a token then we don't need to send any other data
@@ -494,6 +515,17 @@ class PaymentController extends BaseController
$gateway = $this->paymentService->createGateway($accountGateway);
$details = $this->paymentService->getPaymentDetails($invitation, $accountGateway, $data);
+ $details['paymentType'] = $paymentType;
+
+ // Check for authorization
+ if (($paymentType == PAYMENT_TYPE_STRIPE_ACH || $paymentType == PAYMENT_TYPE_WEPAY_ACH) && !Input::get('authorize_ach')) {
+ Session::flash('error', trans('texts.ach_authorization_required'));
+ return Redirect::to('client/paymentmethods/add/' . $typeLink.'/'.$sourceToken)->withInput(Request::except('cvv'));
+ }
+ if ($paymentType == PAYMENT_TYPE_WEPAY_ACH && !Input::get('tos_agree')) {
+ Session::flash('error', trans('texts.wepay_payment_tos_agree_required'));
+ return Redirect::to('client/paymentmethods/add/' . $typeLink.'/'.$sourceToken)->withInput(Request::except('cvv'));
+ }
// check if we're creating/using a billing token
$tokenBillingSupported = false;
@@ -501,11 +533,6 @@ class PaymentController extends BaseController
if ($accountGateway->gateway_id == GATEWAY_STRIPE) {
$tokenBillingSupported = true;
$customerReferenceParam = 'customerReference';
-
- if ($paymentType == PAYMENT_TYPE_STRIPE_ACH && !Input::get('authorize_ach')) {
- Session::flash('error', trans('texts.ach_authorization_required'));
- return Redirect::to('payment/'.$invitationKey)->withInput(Request::except('cvv'));
- }
} elseif ($accountGateway->gateway_id == GATEWAY_BRAINTREE) {
$tokenBillingSupported = true;
$sourceReferenceParam = 'paymentMethodToken';
@@ -531,8 +558,8 @@ class PaymentController extends BaseController
}
$details[$sourceReferenceParam] = $sourceReference;
unset($details['card']);
- } elseif ($account->token_billing_type_id == TOKEN_BILLING_ALWAYS || Input::get('token_billing') || $paymentType == PAYMENT_TYPE_STRIPE_ACH) {
- $token = $this->paymentService->createToken($gateway, $details, $accountGateway, $client, $invitation->contact_id, $customerReference/* return parameter */, $paymentMethod/* return parameter */);
+ } elseif ($account->token_billing_type_id == TOKEN_BILLING_ALWAYS || Input::get('token_billing') || $paymentType == PAYMENT_TYPE_STRIPE_ACH || $paymentType == PAYMENT_TYPE_WEPAY_ACH) {
+ $token = $this->paymentService->createToken($paymentType, $gateway, $details, $accountGateway, $client, $invitation->contact_id, $customerReference/* return parameter */, $paymentMethod/* return parameter */);
if ($token) {
$details[$sourceReferenceParam] = $token;
if ($customerReferenceParam) {
@@ -570,7 +597,7 @@ class PaymentController extends BaseController
if (!$ref) {
$this->error('No-Ref', $response->getMessage(), $accountGateway);
- if ($onSite && $paymentType != PAYMENT_TYPE_BRAINTREE_PAYPAL) {
+ if ($onSite && $paymentType != PAYMENT_TYPE_BRAINTREE_PAYPAL && $paymentType != PAYMENT_TYPE_WEPAY_ACH) {
return Redirect::to('payment/'.$invitationKey)
->withInput(Request::except('cvv'));
} else {
@@ -598,7 +625,7 @@ class PaymentController extends BaseController
$response->redirect();
} else {
$this->error('Unknown', $response->getMessage(), $accountGateway);
- if ($onSite && $paymentType != PAYMENT_TYPE_BRAINTREE_PAYPAL) {
+ if ($onSite && $paymentType != PAYMENT_TYPE_BRAINTREE_PAYPAL && $paymentType != PAYMENT_TYPE_WEPAY_ACH) {
return Redirect::to('payment/'.$invitationKey)->withInput(Request::except('cvv'));
} else {
return Redirect::to('view/'.$invitationKey);
@@ -606,7 +633,7 @@ class PaymentController extends BaseController
}
} catch (\Exception $e) {
$this->error('Uncaught', false, $accountGateway, $e);
- if ($onSite && $paymentType != PAYMENT_TYPE_BRAINTREE_PAYPAL) {
+ if ($onSite && $paymentType != PAYMENT_TYPE_BRAINTREE_PAYPAL && $paymentType != PAYMENT_TYPE_WEPAY_ACH) {
return Redirect::to('payment/'.$invitationKey)->withInput(Request::except('cvv'));
} else {
return Redirect::to('view/'.$invitationKey);
@@ -667,7 +694,7 @@ class PaymentController extends BaseController
} elseif (method_exists($gateway, 'completePurchase')
&& !$accountGateway->isGateway(GATEWAY_TWO_CHECKOUT)
&& !$accountGateway->isGateway(GATEWAY_CHECKOUT_COM)) {
- $details = $this->paymentService->getPaymentDetails($invitation, $accountGateway);
+ $details = $this->paymentService->getPaymentDetails($invitation, $accountGateway, array());
$response = $this->paymentService->completePurchase($gateway, $accountGateway, $details, $token);
diff --git a/app/Http/Middleware/Authenticate.php b/app/Http/Middleware/Authenticate.php
index 81fde62439e2..1b38969fe929 100644
--- a/app/Http/Middleware/Authenticate.php
+++ b/app/Http/Middleware/Authenticate.php
@@ -19,38 +19,59 @@ class Authenticate {
{
$authenticated = Auth::guard($guard)->check();
- if($guard == 'client' && !empty($request->invitation_key)){
- $old_key = session('invitation_key');
- if($old_key && $old_key != $request->invitation_key){
- if($this->getInvitationContactId($old_key) != $this->getInvitationContactId($request->invitation_key)){
- // This is a different client; reauthenticate
- $authenticated = false;
- Auth::guard($guard)->logout();
- }
- }
- Session::put('invitation_key', $request->invitation_key);
- }
-
if($guard=='client'){
- $invitation_key = session('invitation_key');
- $account_id = $this->getInvitationAccountId($invitation_key);
-
- if(Auth::guard('user')->check() && Auth::user('user')->account_id === $account_id){
+ if(!empty($request->invitation_key)){
+ $contact_key = session('contact_key');
+ if($contact_key) {
+ $contact = $this->getContact($contact_key);
+ $invitation = $this->getInvitation($request->invitation_key);
+
+ if (!$invitation) {
+ return response()->view('error', [
+ 'error' => trans('texts.invoice_not_found'),
+ 'hideHeader' => true,
+ ]);
+ }
+
+ if ($contact->id != $invitation->contact_id) {
+ // This is a different client; reauthenticate
+ $authenticated = false;
+ Auth::guard($guard)->logout();
+ }
+ Session::put('contact_key', $invitation->contact->contact_key);
+ }
+ }
+
+ if (!empty($request->contact_key)) {
+ $contact_key = $request->contact_key;
+ Session::put('contact_key', $contact_key);
+ } else {
+ $contact_key = session('contact_key');
+ }
+
+ if ($contact_key) {
+ $contact = $this->getContact($contact_key);
+ } elseif (!empty($request->invitation_key)) {
+ $invitation = $this->getInvitation($request->invitation_key);
+ $contact = $invitation->contact;
+ Session::put('contact_key', $contact->contact_key);
+ } else {
+ return \Redirect::to('client/sessionexpired');
+ }
+ $account = $contact->account;
+
+ if(Auth::guard('user')->check() && Auth::user('user')->account_id === $account->id){
// This is an admin; let them pretend to be a client
$authenticated = true;
}
// Does this account require portal passwords?
- $account = Account::whereId($account_id)->first();
if($account && (!$account->enable_portal_password || !$account->hasFeature(FEATURE_CLIENT_PORTAL_PASSWORD))){
$authenticated = true;
}
- if(!$authenticated){
- $contact = Contact::whereId($this->getInvitationContactId($invitation_key))->first();
- if($contact && !$contact->password){
- $authenticated = true;
- }
+ if(!$authenticated && $contact && !$contact->password){
+ $authenticated = true;
}
}
@@ -76,16 +97,12 @@ class Authenticate {
}
else return null;
}
-
- protected function getInvitationContactId($key){
- $invitation = $this->getInvitation($key);
-
- return $invitation?$invitation->contact_id:null;
- }
-
- protected function getInvitationAccountId($key){
- $invitation = $this->getInvitation($key);
-
- return $invitation?$invitation->account_id:null;
+
+ protected function getContact($key){
+ $contact = Contact::withTrashed()->where('contact_key', '=', $key)->first();
+ if ($contact && !$contact->is_deleted) {
+ return $contact;
+ }
+ else return null;
}
}
diff --git a/app/Http/routes.php b/app/Http/routes.php
index f8fcbca5ffd7..0db6eaaa6d2d 100644
--- a/app/Http/routes.php
+++ b/app/Http/routes.php
@@ -57,6 +57,7 @@ Route::group(['middleware' => 'auth:client'], function() {
Route::get('client/documents', 'ClientPortalController@documentIndex');
Route::get('client/payments', 'ClientPortalController@paymentIndex');
Route::get('client/dashboard', 'ClientPortalController@dashboard');
+ Route::get('client/dashboard/{contact_key}', 'ClientPortalController@contactIndex');
Route::get('client/documents/js/{documents}/{filename}', 'ClientPortalController@getDocumentVFSJS');
Route::get('client/documents/{invitation_key}/{documents}/{filename?}', 'ClientPortalController@getDocument');
Route::get('client/documents/{invitation_key}/{filename?}', 'ClientPortalController@getInvoiceDocumentsZip');
@@ -101,6 +102,7 @@ Route::get('/user/confirm/{code}', 'UserController@confirm');
Route::get('/client/login', array('as' => 'login', 'uses' => 'ClientAuth\AuthController@getLogin'));
Route::post('/client/login', array('as' => 'login', 'uses' => 'ClientAuth\AuthController@postLogin'));
Route::get('/client/logout', array('as' => 'logout', 'uses' => 'ClientAuth\AuthController@getLogout'));
+Route::get('/client/sessionexpired', array('as' => 'logout', 'uses' => 'ClientAuth\AuthController@getSessionExpired'));
Route::get('/client/recover_password', array('as' => 'forgot', 'uses' => 'ClientAuth\PasswordController@getEmail'));
Route::post('/client/recover_password', array('as' => 'forgot', 'uses' => 'ClientAuth\PasswordController@postEmail'));
Route::get('/client/password/reset/{invitation_key}/{token}', array('as' => 'forgot', 'uses' => 'ClientAuth\PasswordController@getReset'));
@@ -673,6 +675,7 @@ if (!defined('CONTACT_EMAIL')) {
define('PAYMENT_TYPE_STRIPE_CREDIT_CARD', 'PAYMENT_TYPE_STRIPE_CREDIT_CARD');
define('PAYMENT_TYPE_STRIPE_ACH', 'PAYMENT_TYPE_STRIPE_ACH');
define('PAYMENT_TYPE_BRAINTREE_PAYPAL', 'PAYMENT_TYPE_BRAINTREE_PAYPAL');
+ define('PAYMENT_TYPE_WEPAY_ACH', 'PAYMENT_TYPE_WEPAY_ACH');
define('PAYMENT_TYPE_CREDIT_CARD', 'PAYMENT_TYPE_CREDIT_CARD');
define('PAYMENT_TYPE_DIRECT_DEBIT', 'PAYMENT_TYPE_DIRECT_DEBIT');
define('PAYMENT_TYPE_BITCOIN', 'PAYMENT_TYPE_BITCOIN');
diff --git a/app/Models/Account.php b/app/Models/Account.php
index 3248bf95814f..2ab3858d39ff 100644
--- a/app/Models/Account.php
+++ b/app/Models/Account.php
@@ -384,26 +384,31 @@ class Account extends Eloquent
return $format;
}
- public function getGatewayByType($type = PAYMENT_TYPE_ANY)
+ public function getGatewayByType($type = PAYMENT_TYPE_ANY, $exceptFor = null)
{
if ($type == PAYMENT_TYPE_STRIPE_ACH || $type == PAYMENT_TYPE_STRIPE_CREDIT_CARD) {
$type = PAYMENT_TYPE_STRIPE;
}
- if ($type == PAYMENT_TYPE_BRAINTREE_PAYPAL) {
- $gateway = $this->getGatewayConfig(GATEWAY_BRAINTREE);
-
- if (!$gateway || !$gateway->getPayPalEnabled()){
- return false;
- }
- return $gateway;
+ if ($type == PAYMENT_TYPE_WEPAY_ACH) {
+ return $this->getGatewayConfig(GATEWAY_WEPAY);
}
foreach ($this->account_gateways as $gateway) {
+ if ($exceptFor && ($gateway->id == $exceptFor->id)) {
+ continue;
+ }
+
if (!$type || $type == PAYMENT_TYPE_ANY) {
return $gateway;
} elseif ($gateway->isPaymentType($type)) {
return $gateway;
+ } elseif ($type == PAYMENT_TYPE_CREDIT_CARD && $gateway->isPaymentType(PAYMENT_TYPE_STRIPE)) {
+ return $gateway;
+ } elseif ($type == PAYMENT_TYPE_DIRECT_DEBIT && $gateway->getAchEnabled()) {
+ return $gateway;
+ } elseif ($type == PAYMENT_TYPE_PAYPAL && $gateway->getPayPalEnabled()) {
+ return $gateway;
}
}
@@ -1424,32 +1429,13 @@ class Account extends Eloquent
}
public function canAddGateway($type){
+ if ($type == PAYMENT_TYPE_STRIPE) {
+ $type == PAYMENT_TYPE_CREDIT_CARD;
+ }
+
if($this->getGatewayByType($type)) {
return false;
}
- if ($type == PAYMENT_TYPE_CREDIT_CARD && $this->getGatewayByType(PAYMENT_TYPE_STRIPE)) {
- // Stripe is already handling credit card payments
- return false;
- }
-
- if ($type == PAYMENT_TYPE_STRIPE && $this->getGatewayByType(PAYMENT_TYPE_CREDIT_CARD)) {
- // Another gateway is already handling credit card payments
- return false;
- }
-
- if ($type == PAYMENT_TYPE_DIRECT_DEBIT && $stripeGateway = $this->getGatewayByType(PAYMENT_TYPE_STRIPE)) {
- if (!empty($stripeGateway->getAchEnabled())) {
- // Stripe is already handling ACH payments
- return false;
- }
- }
-
- if ($type == PAYMENT_TYPE_PAYPAL && $braintreeGateway = $this->getGatewayConfig(GATEWAY_BRAINTREE)) {
- if (!empty($braintreeGateway->getPayPalEnabled())) {
- // PayPal is already enabled
- return false;
- }
- }
return true;
}
diff --git a/app/Models/Contact.php b/app/Models/Contact.php
index 9c86c4ce5b84..e47211e5133e 100644
--- a/app/Models/Contact.php
+++ b/app/Models/Contact.php
@@ -60,6 +60,15 @@ class Contact extends EntityModel implements AuthenticatableContract, CanResetPa
}
}
+ public function getContactKeyAttribute($contact_key)
+ {
+ if (empty($contact_key) && $this->id) {
+ $this->contact_key = $contact_key = str_random(RANDOM_KEY_LENGTH);
+ static::where('id', $this->id)->update(array('contact_key' => $contact_key));
+ }
+ return $contact_key;
+ }
+
public function getFullName()
{
if ($this->first_name || $this->last_name) {
@@ -68,4 +77,9 @@ class Contact extends EntityModel implements AuthenticatableContract, CanResetPa
return '';
}
}
+
+ public function getLinkAttribute()
+ {
+ return \URL::to('client/dashboard/' . $this->contact_key);
+ }
}
diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php
index 17b6292739c1..30e9a7f792a4 100644
--- a/app/Models/Invoice.php
+++ b/app/Models/Invoice.php
@@ -947,6 +947,20 @@ class Invoice extends EntityModel implements BalanceAffecting
}
return false;
}
+
+ public function getAutoBillEnabled() {
+ if (!$this->is_recurring) {
+ $recurInvoice = $this->recurring_invoice;
+ } else {
+ $recurInvoice = $this;
+ }
+
+ if (!$recurInvoice) {
+ return false;
+ }
+
+ return $recurInvoice->auto_bill == AUTO_BILL_ALWAYS || ($recurInvoice->auto_bill != AUTO_BILL_OFF && $recurInvoice->client_enable_auto_bill);
+ }
}
Invoice::creating(function ($invoice) {
diff --git a/app/Models/Payment.php b/app/Models/Payment.php
index 59f1e1e9823e..288bc566a8f1 100644
--- a/app/Models/Payment.php
+++ b/app/Models/Payment.php
@@ -180,6 +180,16 @@ class Payment extends EntityModel
return PaymentMethod::lookupBankData($this->routing_number);
}
+ public function getBankNameAttribute($bank_name)
+ {
+ if ($bank_name) {
+ return $bank_name;
+ }
+ $bankData = $this->bank_data;
+
+ return $bankData?$bankData->name:null;
+ }
+
public function getLast4Attribute($value)
{
return $value ? str_pad($value, 4, '0', STR_PAD_LEFT) : null;
diff --git a/app/Models/PaymentMethod.php b/app/Models/PaymentMethod.php
index a2f17b722ae6..8c3ea8b2dfd8 100644
--- a/app/Models/PaymentMethod.php
+++ b/app/Models/PaymentMethod.php
@@ -71,6 +71,16 @@ class PaymentMethod extends EntityModel
return static::lookupBankData($this->routing_number);
}
+ public function getBankNameAttribute($bank_name)
+ {
+ if ($bank_name) {
+ return $bank_name;
+ }
+ $bankData = $this->bank_data;
+
+ return $bankData?$bankData->name:null;
+ }
+
public function getLast4Attribute($value)
{
return $value ? str_pad($value, 4, '0', STR_PAD_LEFT) : null;
@@ -148,6 +158,10 @@ class PaymentMethod extends EntityModel
return null;
}
}
+
+ public function requiresDelayedAutoBill(){
+ return $this->payment_type_id == PAYMENT_TYPE_ACH;
+ }
}
PaymentMethod::deleting(function($paymentMethod) {
diff --git a/app/Ninja/Datatables/AccountGatewayDatatable.php b/app/Ninja/Datatables/AccountGatewayDatatable.php
index ad16fc39878a..fe6a8df0df7a 100644
--- a/app/Ninja/Datatables/AccountGatewayDatatable.php
+++ b/app/Ninja/Datatables/AccountGatewayDatatable.php
@@ -29,18 +29,13 @@ class AccountGatewayDatatable extends EntityDatatable
$wepayState = isset($config->state)?$config->state:null;
$linkText = $model->name;
$url = $endpoint.'account/'.$wepayAccountId;
- $wepay = \Utils::setupWepay($accountGateway);
$html = link_to($url, $linkText, array('target'=>'_blank'))->toHtml();
try {
if ($wepayState == 'action_required') {
- $updateUri = $wepay->request('/account/get_update_uri', array(
- 'account_id' => $wepayAccountId,
- 'redirect_uri' => URL::to('gateways'),
- ));
-
+ $updateUri = $endpoint.'api/account_update/'.$wepayAccountId.'?redirect_uri='.urlencode(URL::to('gateways'));
$linkText .= ' ('.trans('texts.action_required').')';
- $url = $updateUri->uri;
+ $url = $updateUri;
$html = "{$linkText}";
$model->setupUrl = $url;
} elseif ($wepayState == 'pending') {
diff --git a/app/Ninja/Datatables/PaymentDatatable.php b/app/Ninja/Datatables/PaymentDatatable.php
index 22006bf742e1..92473c7fd257 100644
--- a/app/Ninja/Datatables/PaymentDatatable.php
+++ b/app/Ninja/Datatables/PaymentDatatable.php
@@ -65,9 +65,16 @@ class PaymentDatatable extends EntityDatatable
return $model->email;
}
} elseif ($model->last4) {
- $bankData = PaymentMethod::lookupBankData($model->routing_number);
- if (is_object($bankData)) {
- return $bankData->name.' •••' . $model->last4;
+ if($model->bank_name) {
+ $bankName = $model->bank_name;
+ } else {
+ $bankData = PaymentMethod::lookupBankData($model->routing_number);
+ if($bankData) {
+ $bankName = $bankData->name;
+ }
+ }
+ if (!empty($bankName)) {
+ return $bankName.' •••' . $model->last4;
} elseif($model->last4) {
return '
•••' . $model->last4;
}
diff --git a/app/Ninja/Mailers/ContactMailer.php b/app/Ninja/Mailers/ContactMailer.php
index 12662a606ed6..9fa2009a7b72 100644
--- a/app/Ninja/Mailers/ContactMailer.php
+++ b/app/Ninja/Mailers/ContactMailer.php
@@ -30,6 +30,7 @@ class ContactMailer extends Mailer
'viewButton',
'paymentLink',
'paymentButton',
+ 'autoBill',
];
public function __construct(TemplateService $templateService)
@@ -106,6 +107,20 @@ class ContactMailer extends Mailer
return $response;
}
+ private function createAutoBillNotifyString($paymentMethod) {
+ if ($paymentMethod->payment_type_id == PAYMENT_TYPE_DIRECT_DEBIT) {
+ $paymentMethodString = trans('texts.auto_bill_payment_method_bank', ['bank'=>$paymentMethod->getBankName(), 'last4'=>$paymentMethod->last4]);
+ } elseif ($paymentMethod->payment_type_id == PAYMENT_TYPE_ID_PAYPAL) {
+ $paymentMethodString = trans('texts.auto_bill_payment_method_paypal', ['email'=>$paymentMethod->email]);
+ } else {
+ $code = str_replace(' ', '', strtolower($paymentMethod->payment_type->name));
+ $cardType = trans("texts.card_" . $code);
+ $paymentMethodString = trans('texts.auto_bill_payment_method_credit_card', ['type'=>$cardType,'last4'=>$paymentMethod->last4]);
+ }
+
+ return trans('texts.auto_bill_notification', ['payment_method'=>$paymentMethodString]);
+ }
+
private function sendInvitation($invitation, $invoice, $body, $subject, $pdfString, $documentStrings)
{
$client = $invoice->client;
@@ -137,6 +152,11 @@ class ContactMailer extends Mailer
'amount' => $invoice->getRequestedAmount()
];
+ if ($invoice->autoBillPaymentMethod) {
+ // Let the client know they'll be billed later
+ $variables['autobill'] = $this->createAutoBillNotifyString($invoice->autoBillPaymentMethod);
+ }
+
if (empty($invitation->contact->password) && $account->hasFeature(FEATURE_CLIENT_PORTAL_PASSWORD) && $account->enable_portal_password && $account->send_portal_password) {
// The contact needs a password
$variables['password'] = $password = $this->generatePassword();
diff --git a/app/Ninja/Repositories/ContactRepository.php b/app/Ninja/Repositories/ContactRepository.php
index 49b73e91a664..50d15af2b21e 100644
--- a/app/Ninja/Repositories/ContactRepository.php
+++ b/app/Ninja/Repositories/ContactRepository.php
@@ -14,6 +14,7 @@ class ContactRepository extends BaseRepository
$contact->send_invoice = true;
$contact->client_id = $data['client_id'];
$contact->is_primary = Contact::scope()->where('client_id', '=', $contact->client_id)->count() == 0;
+ $contact->contact_key = str_random(RANDOM_KEY_LENGTH);
} else {
$contact = Contact::scope($publicId)->firstOrFail();
}
diff --git a/app/Ninja/Repositories/InvoiceRepository.php b/app/Ninja/Repositories/InvoiceRepository.php
index d79c15f24c08..91ac711390bc 100644
--- a/app/Ninja/Repositories/InvoiceRepository.php
+++ b/app/Ninja/Repositories/InvoiceRepository.php
@@ -798,7 +798,8 @@ class InvoiceRepository extends BaseRepository
$recurInvoice->last_sent_date = date('Y-m-d');
$recurInvoice->save();
- if ($recurInvoice->auto_bill == AUTO_BILL_ALWAYS || ($recurInvoice->auto_bill != AUTO_BILL_OFF && $recurInvoice->client_enable_auto_bill)) {
+ if ($recurInvoice->getAutoBillEnabled() && !$recurInvoice->account->auto_bill_on_due_date) {
+ // autoBillInvoice will check for ACH, so we're not checking here
if ($this->paymentService->autoBillInvoice($invoice)) {
// update the invoice reference to match its actual state
// this is to ensure a 'payment received' email is sent
diff --git a/app/Ninja/Repositories/PaymentRepository.php b/app/Ninja/Repositories/PaymentRepository.php
index ef6c88096405..498f0d1d6912 100644
--- a/app/Ninja/Repositories/PaymentRepository.php
+++ b/app/Ninja/Repositories/PaymentRepository.php
@@ -58,6 +58,7 @@ class PaymentRepository extends BaseRepository
'payments.last4',
'payments.email',
'payments.routing_number',
+ 'payments.bank_name',
'invoices.is_deleted as invoice_is_deleted',
'gateways.name as gateway_name',
'gateways.id as gateway_id',
@@ -129,6 +130,7 @@ class PaymentRepository extends BaseRepository
'payments.last4',
'payments.email',
'payments.routing_number',
+ 'payments.bank_name',
'payments.payment_status_id',
'payment_statuses.name as payment_status_name'
);
diff --git a/app/Services/PaymentService.php b/app/Services/PaymentService.php
index b227b466413c..efa27d945b7a 100644
--- a/app/Services/PaymentService.php
+++ b/app/Services/PaymentService.php
@@ -16,6 +16,7 @@ use App\Models\Account;
use App\Models\Country;
use App\Models\Client;
use App\Models\Invoice;
+use App\Models\Activity;
use App\Models\AccountGateway;
use App\Http\Controllers\PaymentController;
use App\Models\AccountGatewayToken;
@@ -88,6 +89,10 @@ class PaymentService extends BaseService
'transactionType' => 'Purchase',
];
+ if ($input !== null) {
+ $data['ip'] = \Request::ip();
+ }
+
if ($accountGateway->isGateway(GATEWAY_PAYPAL_EXPRESS) || $accountGateway->isGateway(GATEWAY_PAYPAL_PRO)) {
$data['ButtonSource'] = 'InvoiceNinja_SP';
};
@@ -302,7 +307,7 @@ class PaymentService extends BaseService
return true;
}
- public function createToken($gateway, $details, $accountGateway, $client, $contactId, &$customerReference = null, &$paymentMethod = null)
+ public function createToken($paymentType, $gateway, $details, $accountGateway, $client, $contactId, &$customerReference = null, &$paymentMethod = null)
{
$customerReference = $client->getGatewayToken($accountGateway, $accountGatewayToken/* return paramenter */);
@@ -394,27 +399,36 @@ class PaymentService extends BaseService
}
} elseif ($accountGateway->gateway_id == GATEWAY_WEPAY) {
$wepay = Utils::setupWePay($accountGateway);
-
try {
- $wepay->request('credit_card/authorize', array(
- 'client_id' => WEPAY_CLIENT_ID,
- 'client_secret' => WEPAY_CLIENT_SECRET,
- 'credit_card_id' => intval($details['token']),
- ));
+ if ($paymentType == PAYMENT_TYPE_WEPAY_ACH) {
+ // Persist bank details
+ $tokenResponse = $wepay->request('/payment_bank/persist', array(
+ 'client_id' => WEPAY_CLIENT_ID,
+ 'client_secret' => WEPAY_CLIENT_SECRET,
+ 'payment_bank_id' => intval($details['token']),
+ ));
+ } else {
+ // Authorize credit card
+ $wepay->request('credit_card/authorize', array(
+ 'client_id' => WEPAY_CLIENT_ID,
+ 'client_secret' => WEPAY_CLIENT_SECRET,
+ 'credit_card_id' => intval($details['token']),
+ ));
- // Update the callback uri and get the card details
- $wepay->request('credit_card/modify', array(
- 'client_id' => WEPAY_CLIENT_ID,
- 'client_secret' => WEPAY_CLIENT_SECRET,
- 'credit_card_id' => intval($details['token']),
- 'auto_update' => WEPAY_AUTO_UPDATE,
- 'callback_uri' => $accountGateway->getWebhookUrl(),
- ));
- $tokenResponse = $wepay->request('credit_card', array(
- 'client_id' => WEPAY_CLIENT_ID,
- 'client_secret' => WEPAY_CLIENT_SECRET,
- 'credit_card_id' => intval($details['token']),
- ));
+ // Update the callback uri and get the card details
+ $wepay->request('credit_card/modify', array(
+ 'client_id' => WEPAY_CLIENT_ID,
+ 'client_secret' => WEPAY_CLIENT_SECRET,
+ 'credit_card_id' => intval($details['token']),
+ 'auto_update' => WEPAY_AUTO_UPDATE,
+ 'callback_uri' => $accountGateway->getWebhookUrl(),
+ ));
+ $tokenResponse = $wepay->request('credit_card', array(
+ 'client_id' => WEPAY_CLIENT_ID,
+ 'client_secret' => WEPAY_CLIENT_SECRET,
+ 'credit_card_id' => intval($details['token']),
+ ));
+ }
$customerReference = CUSTOMER_REFERENCE_LOCAL;
$sourceReference = $details['token'];
@@ -442,6 +456,8 @@ class PaymentService extends BaseService
$accountGatewayToken->save();
$paymentMethod = $this->convertPaymentMethodFromGatewayResponse($tokenResponse, $accountGateway, $accountGatewayToken, $contactId);
+ $paymentMethod->ip = \Request::ip();
+ $paymentMethod->save();
} else {
$this->lastError = $tokenResponse->getMessage();
@@ -510,12 +526,29 @@ class PaymentService extends BaseService
$paymentMethod = $accountGatewayToken ? PaymentMethod::createNew($accountGatewayToken) : new PaymentMethod();
}
- $paymentMethod->payment_type_id = $this->parseCardType($source->credit_card_name);
- $paymentMethod->last4 = $source->last_four;
- $paymentMethod->expiration = $source->expiration_year . '-' . $source->expiration_month . '-01';
- $paymentMethod->setRelation('payment_type', Cache::get('paymentTypes')->find($paymentMethod->payment_type_id));
+ if ($source->payment_bank_id) {
+ $paymentMethod->payment_type_id = PAYMENT_TYPE_ACH;
+ $paymentMethod->last4 = $source->account_last_four;
+ $paymentMethod->bank_name = $source->bank_name;
+ $paymentMethod->source_reference = $source->payment_bank_id;
- $paymentMethod->source_reference = $source->credit_card_id;
+ switch($source->state) {
+ case 'new':
+ case 'pending':
+ $paymentMethod->status = 'new';
+ break;
+ case 'authorized':
+ $paymentMethod->status = 'verified';
+ break;
+ }
+ } else {
+ $paymentMethod->last4 = $source->last_four;
+ $paymentMethod->payment_type_id = $this->parseCardType($source->credit_card_name);
+ $paymentMethod->expiration = $source->expiration_year . '-' . $source->expiration_month . '-01';
+ $paymentMethod->setRelation('payment_type', Cache::get('paymentTypes')->find($paymentMethod->payment_type_id));
+
+ $paymentMethod->source_reference = $source->credit_card_id;
+ }
return $paymentMethod;
}
@@ -564,10 +597,12 @@ class PaymentService extends BaseService
} elseif ($accountGateway->gateway_id == GATEWAY_WEPAY) {
if ($gatewayResponse instanceof \Omnipay\WePay\Message\CustomCheckoutResponse) {
$wepay = \Utils::setupWePay($accountGateway);
- $gatewayResponse = $wepay->request('credit_card', array(
+ $paymentMethodType = $gatewayResponse->getData()['payment_method']['type'];
+
+ $gatewayResponse = $wepay->request($paymentMethodType, array(
'client_id' => WEPAY_CLIENT_ID,
'client_secret' => WEPAY_CLIENT_SECRET,
- 'credit_card_id' => $gatewayResponse->getData()['payment_method']['credit_card']['id'],
+ $paymentMethodType.'_id' => $gatewayResponse->getData()['payment_method'][$paymentMethodType]['id'],
));
}
@@ -644,6 +679,10 @@ class PaymentService extends BaseService
$payment->payment_type_id = $this->detectCardType($card->getNumber());
}
+ if (!empty($paymentDetails['ip'])) {
+ $payment->ip = $paymentDetails['ip'];
+ }
+
$savePaymentMethod = !empty($paymentMethod);
// This will convert various gateway's formats to a known format
@@ -680,6 +719,10 @@ class PaymentService extends BaseService
$payment->email = $paymentMethod->email;
}
+ if ($paymentMethod->bank_name) {
+ $payment->bank_name = $paymentMethod->bank_name;
+ }
+
if ($payerId) {
$payment->payer_id = $payerId;
}
@@ -829,11 +872,46 @@ class PaymentService extends BaseService
return false;
}
+ if ($defaultPaymentMethod->requiresDelayedAutoBill()) {
+ $invoiceDate = \DateTime::createFromFormat('Y-m-d', $invoice->invoice_date);
+ $minDueDate = clone $invoiceDate;
+ $minDueDate->modify('+10 days');
+
+ if (date_create() < $minDueDate) {
+ // Can't auto bill now
+ return false;
+ }
+
+ if ($invoice->partial > 0) {
+ // The amount would be different than the amount in the email
+ return false;
+ }
+
+ $firstUpdate = Activity::where('invoice_id', '=', $invoice->id)
+ ->where('activity_type_id', '=', ACTIVITY_TYPE_UPDATE_INVOICE)
+ ->first();
+
+ if ($firstUpdate) {
+ $backup = json_decode($firstUpdate->json_backup);
+
+ if ($backup->balance != $invoice->balance || $backup->due_date != $invoice->due_date) {
+ // It's changed since we sent the email can't bill now
+ return false;
+ }
+ }
+
+ if ($invoice->payments->count()) {
+ // ACH requirements are strict; don't auto bill this
+ return false;
+ }
+ }
+
// setup the gateway/payment info
$details = $this->getPaymentDetails($invitation, $accountGateway);
$details['customerReference'] = $token;
$details['token'] = $defaultPaymentMethod->source_reference;
+ $details['paymentType'] = $defaultPaymentMethod->payment_type_id;
if ($accountGateway->gateway_id == GATEWAY_WEPAY) {
$details['transaction_id'] = 'autobill_'.$invoice->id;
}
@@ -849,6 +927,24 @@ class PaymentService extends BaseService
}
}
+ public function getClientDefaultPaymentMethod($client) {
+ $this->getClientPaymentMethods($client);
+
+ $client->getGatewayToken($accountGateway/* return parameter */, $accountGatewayToken/* return parameter */);
+
+ if (!$accountGatewayToken) {
+ return false;
+ }
+
+ return $accountGatewayToken->default_payment_method;
+ }
+
+ public function getClientRequiresDelayedAutoBill($client) {
+ $defaultPaymentMethod = $this->getClientDefaultPaymentMethod($client);
+
+ return $defaultPaymentMethod?$defaultPaymentMethod->requiresDelayedAutoBill():null;
+ }
+
public function getDatatable($clientPublicId, $search)
{
$datatable = new PaymentDatatable( ! $clientPublicId, $clientPublicId);
@@ -1057,6 +1153,13 @@ class PaymentService extends BaseService
$details['applicationFee'] = $this->calculateApplicationFee($accountGateway, $details['amount']);
$details['feePayer'] = WEPAY_FEE_PAYER;
$details['callbackUri'] = $accountGateway->getWebhookUrl();
+ if(isset($details['paymentType'])) {
+ if($details['paymentType'] == PAYMENT_TYPE_ACH || $details['paymentType'] == PAYMENT_TYPE_WEPAY_ACH) {
+ $details['paymentMethodType'] = 'payment_bank';
+ }
+
+ unset($details['paymentType']);
+ }
}
$response = $gateway->purchase($details)->send();
diff --git a/app/Services/TemplateService.php b/app/Services/TemplateService.php
index 5a41c705352d..cc3e1becc031 100644
--- a/app/Services/TemplateService.php
+++ b/app/Services/TemplateService.php
@@ -51,6 +51,7 @@ class TemplateService
'$customInvoice1' => $account->custom_invoice_text_label1,
'$customInvoice2' => $account->custom_invoice_text_label2,
'$documents' => $documentsHTML,
+ '$autoBill' => empty($data['autobill'])?'':$data['autobill'],
];
// Add variables for available payment types
diff --git a/composer.lock b/composer.lock
index d5513297fd3f..a2888a4263ac 100644
--- a/composer.lock
+++ b/composer.lock
@@ -977,12 +977,12 @@
"source": {
"type": "git",
"url": "https://github.com/sometechie/omnipay-wepay.git",
- "reference": "2964730018e9fccf0bb0e449065940cad3ca6719"
+ "reference": "fb0e6c9824d15ba74cd6f75421b318e87a9e1822"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sometechie/omnipay-wepay/zipball/2964730018e9fccf0bb0e449065940cad3ca6719",
- "reference": "2964730018e9fccf0bb0e449065940cad3ca6719",
+ "url": "https://api.github.com/repos/sometechie/omnipay-wepay/zipball/fb0e6c9824d15ba74cd6f75421b318e87a9e1822",
+ "reference": "fb0e6c9824d15ba74cd6f75421b318e87a9e1822",
"shasum": ""
},
"require": {
@@ -1014,7 +1014,7 @@
"support": {
"source": "https://github.com/sometechie/omnipay-wepay/tree/additional-calls"
},
- "time": "2016-05-18 18:12:17"
+ "time": "2016-05-23 15:01:20"
},
{
"name": "container-interop/container-interop",
diff --git a/database/migrations/2016_05_24_164847_wepay_ach.php b/database/migrations/2016_05_24_164847_wepay_ach.php
new file mode 100644
index 000000000000..cb1b79433321
--- /dev/null
+++ b/database/migrations/2016_05_24_164847_wepay_ach.php
@@ -0,0 +1,62 @@
+string('contact_key')->nullable()->default(null)->index()->unique();
+ });
+
+ Schema::table('payment_methods', function($table)
+ {
+ $table->string('bank_name')->nullable();
+ $table->string('ip')->nullable();
+ });
+
+ Schema::table('payments', function($table)
+ {
+ $table->string('bank_name')->nullable();
+ $table->string('ip')->nullable();
+ });
+
+ Schema::table('accounts', function($table)
+ {
+ $table->boolean('auto_bill_on_due_date')->default(false);
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::table('contacts', function(Blueprint $table) {
+ $table->dropColumn('contact_key');
+ });
+
+ Schema::table('payments', function($table) {
+ $table->dropColumn('bank_name');
+ $table->dropColumn('ip');
+ });
+
+ Schema::table('payment_methods', function($table) {
+ $table->dropColumn('bank_name');
+ $table->dropColumn('ip');
+ });
+
+ Schema::table('accounts', function($table) {
+ $table->dropColumn('auto_bill_on_due_date');
+ });
+ }
+}
diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php
index 6d4fcdba26ec..093601aa7660 100644
--- a/resources/lang/en/texts.php
+++ b/resources/lang/en/texts.php
@@ -1203,7 +1203,7 @@ $LANG = array(
'ach' => 'ACH',
'enable_ach' => 'Enable ACH',
'stripe_ach_help' => 'ACH support must also be enabled at Stripe.',
- 'stripe_ach_disabled' => 'Another gateway is already configured for direct debit.',
+ 'ach_disabled' => 'Another gateway is already configured for direct debit.',
'plaid' => 'Plaid',
'client_id' => 'Client Id',
@@ -1257,7 +1257,7 @@ $LANG = array(
'plaid_linked_status' => 'Your bank account at :bank',
'add_payment_method' => 'Add Payment Method',
'account_holder_type' => 'Account Holder Type',
- 'ach_authorization' => 'I authorize :company to electronically debit my account and, if necessary, electronically credit my account to correct erroneous debits.',
+ 'ach_authorization' => 'I authorize :company to use my bank account for future payments and, if necessary, electronically credit my account to correct erroneous debits. I understand that I may cancel this authorization at any time by removing the payment method or by contacting :email.',
'ach_authorization_required' => 'You must consent to ACH transactions.',
'off' => 'Off',
'opt_in' => 'Opt-in',
@@ -1324,6 +1324,30 @@ $LANG = array(
'export_help' => 'Use JSON if you plan to import the data into Invoice Ninja.',
'JSON_file' => 'JSON File',
+ 'view_dashboard' => 'View Dashboard',
+ 'client_session_expired' => 'Session Expired',
+ 'client_session_expired_message' => 'Your session has expired. Please click the link in your email again.',
+
+ 'auto_bill_notification' => 'This invoice will automatically be billed to :payment_method on the due date.',
+ 'auto_bill_payment_method_bank' => 'your :bank account ending in :last4',
+ 'auto_bill_payment_method_credit_card' => 'your :type card ending in :last4',
+ 'auto_bill_payment_method_paypal' => 'your PayPal account (:email)',
+ 'auto_bill_notification_placeholder' => 'This invoice will automatically be billed to your Visa card ending in 4242 on the due date.',
+ 'payment_settings' => 'Payment Settings',
+
+ 'on_send_date' => 'On send date',
+ 'on_due_date' => 'On due date',
+ 'auto_bill_ach_date_help' => 'ACH auto bill will always happen on the due date',
+ 'warn_change_auto_bill' => 'Due to NACHA rules, changes to this invoice may prevent ACH auto bill.',
+
+ 'bank_account' => 'Bank Account',
+ 'payment_processed_through_wepay' => 'ACH payments will be processed using WePay.',
+ 'wepay_payment_tos_agree' => 'I agree to the WePay :terms and :privacy_policy.',
+ 'privacy_policy' => 'Privacy Policy',
+ 'wepay_payment_tos_agree_required' => 'You must agree to the WePay Terms of Service and Privacy Policy.',
+ 'payment_settings_supported_gateways' => 'These options are supported by the WePay, Stripe, and Braintree gateways.',
+ 'ach_email_prompt' => 'Please enter your email address:',
+ 'verification_pending' => 'Verification Pending',
);
return $LANG;
diff --git a/resources/views/accounts/account_gateway.blade.php b/resources/views/accounts/account_gateway.blade.php
index cfad9f21a6b6..f195d2849ab8 100644
--- a/resources/views/accounts/account_gateway.blade.php
+++ b/resources/views/accounts/account_gateway.blade.php
@@ -16,7 +16,6 @@
{!! Former::open($url)->method($method)->rule()->addClass('warn-on-exit') !!}
- {!! Former::populateField('token_billing_type_id', $account->token_billing_type_id) !!}
@if ($accountGateway)
{!! Former::populateField('gateway_id', $accountGateway->gateway_id) !!}
@@ -99,12 +98,6 @@
{!! Former::text('publishable_key') !!}
@endif
- @if ($gateway->id == GATEWAY_STRIPE || $gateway->id == GATEWAY_BRAINTREE || $gateway->id == GATEWAY_WEPAY)
- {!! Former::select('token_billing_type_id')
- ->options($tokenBillingOptions)
- ->help(trans('texts.token_billing_help')) !!}
- @endif
-
@if ($gateway->id == GATEWAY_STRIPE)
diff --git a/resources/views/accounts/partials/account_gateway_wepay.blade.php b/resources/views/accounts/partials/account_gateway_wepay.blade.php
index bdbe974aa5e9..a030d4c825b6 100644
--- a/resources/views/accounts/partials/account_gateway_wepay.blade.php
+++ b/resources/views/accounts/partials/account_gateway_wepay.blade.php
@@ -16,7 +16,6 @@
{!! Former::populateField('email', $user->email) !!}
{!! Former::populateField('show_address', 1) !!}
{!! Former::populateField('update_address', 1) !!}
-{!! Former::populateField('token_billing_type_id', $account->token_billing_type_id) !!}
{!! trans('texts.online_payments') !!}
@@ -40,9 +39,6 @@
->text(trans('texts.accept_debit_cards')) !!}
@endif
- {!! Former::select('token_billing_type_id')
- ->options($tokenBillingOptions)
- ->help(trans('texts.token_billing_help')) !!}
{!! Former::checkbox('show_address')
->label(trans('texts.billing_address'))
->text(trans('texts.show_address_help')) !!}
@@ -53,6 +49,18 @@
->label('Accepted Credit Cards')
->checkboxes($creditCardTypes)
->class('creditcard-types') !!}
+ @if ($account->getGatewayByType(PAYMENT_TYPE_DIRECT_DEBIT))
+ {!! Former::checkbox('enable_ach')
+ ->label(trans('texts.ach'))
+ ->text(trans('texts.enable_ach'))
+ ->value(null)
+ ->disabled(true)
+ ->help(trans('texts.ach_disabled')) !!}
+ @else
+ {!! Former::checkbox('enable_ach')
+ ->label(trans('texts.ach'))
+ ->text(trans('texts.enable_ach')) !!}
+ @endif
{!! Former::checkbox('tos_agree')->label(' ')->text(trans('texts.wepay_tos_agree',
['link'=>'
'.trans('texts.wepay_tos_link_text').'']
))->value('true') !!}
diff --git a/resources/views/accounts/payments.blade.php b/resources/views/accounts/payments.blade.php
index 997190ec4f0a..603aec4bf6f5 100644
--- a/resources/views/accounts/payments.blade.php
+++ b/resources/views/accounts/payments.blade.php
@@ -4,6 +4,33 @@
@parent
@include('accounts.nav', ['selected' => ACCOUNT_PAYMENTS])
+ {!! Former::open()->addClass('warn-on-exit') !!}
+ {!! Former::populateField('token_billing_type_id', $account->token_billing_type_id) !!}
+ {!! Former::populateField('auto_bill_on_due_date', $account->auto_bill_on_due_date) !!}
+
+
+
+
+
{!! trans('texts.payment_settings') !!}
+
+
+ {!! Former::select('token_billing_type_id')
+ ->options($tokenBillingOptions)
+ ->help(trans('texts.token_billing_help')) !!}
+ {!! Former::inline_radios('auto_bill_on_due_date')
+ ->label(trans('texts.auto_bill'))
+ ->radios([
+ trans('texts.on_send_date') => ['value'=>0, 'name'=>'auto_bill_on_due_date'],
+ trans('texts.on_due_date') => ['value'=>1, 'name'=>'auto_bill_on_due_date'],
+ ])->help(trans('texts.auto_bill_ach_date_help')) !!}
+
+ {!! Former::actions( Button::success(trans('texts.save'))->submit()->appendIcon(Icon::create('floppy-disk')) ) !!}
+
+
+ {!! Former::close() !!}
+
@if ($showSwitchToWepay)
{!! Button::success(trans('texts.switch_to_wepay'))
->asLinkTo(URL::to('/gateways/switch/wepay'))
diff --git a/resources/views/accounts/templates_and_reminders.blade.php b/resources/views/accounts/templates_and_reminders.blade.php
index 87453e80dae6..aee11968b469 100644
--- a/resources/views/accounts/templates_and_reminders.blade.php
+++ b/resources/views/accounts/templates_and_reminders.blade.php
@@ -266,6 +266,7 @@
'{!! Form::flatButton('view_invoice', '#0b4d78') !!}$password',
"{{ URL::to('/payment/...') }}$password",
'{!! Form::flatButton('pay_now', '#36c157') !!}$password',
+ '{{ trans('texts.auto_bill_notification_placeholder') }}',
];
// Add blanks for custom values
diff --git a/resources/views/clientauth/login.blade.php b/resources/views/clientauth/login.blade.php
index e5baee7c05aa..690f14bd8df7 100644
--- a/resources/views/clientauth/login.blade.php
+++ b/resources/views/clientauth/login.blade.php
@@ -61,7 +61,7 @@
@section('body')
- @include('partials.warn_session', ['redirectTo' => '/client/login'])
+ @include('partials.warn_session', ['redirectTo' => '/client/sessionexpired'])
{!! Former::open('client/login')
->rules(['password' => 'required'])
diff --git a/resources/views/clientauth/password.blade.php b/resources/views/clientauth/password.blade.php
index 9b08937ed1bd..41334fb32995 100644
--- a/resources/views/clientauth/password.blade.php
+++ b/resources/views/clientauth/password.blade.php
@@ -45,6 +45,14 @@
.form-signin .form-control:focus {
z-index: 2;
}
+
+ .modal-header a:link,
+ .modal-header a:visited,
+ .modal-header a:hover,
+ .modal-header a:active {
+ text-decoration: none;
+ color: white;
+ }
@stop
diff --git a/resources/views/clientauth/reset.blade.php b/resources/views/clientauth/reset.blade.php
index f8f0924a0cbc..fe384391127b 100644
--- a/resources/views/clientauth/reset.blade.php
+++ b/resources/views/clientauth/reset.blade.php
@@ -45,6 +45,14 @@
.form-signin .form-control:focus {
z-index: 2;
}
+
+ .modal-header a:link,
+ .modal-header a:visited,
+ .modal-header a:hover,
+ .modal-header a:active {
+ text-decoration: none;
+ color: white;
+ }
@stop
@@ -70,7 +78,7 @@
-
+
{!! Former::password('password')->placeholder(trans('texts.password'))->raw() !!}
diff --git a/resources/views/clientauth/sessionexpired.blade.php b/resources/views/clientauth/sessionexpired.blade.php
new file mode 100644
index 000000000000..1fca7a79b4a7
--- /dev/null
+++ b/resources/views/clientauth/sessionexpired.blade.php
@@ -0,0 +1,79 @@
+@extends('public.header')
+
+@section('head')
+ @parent
+
+
+@endsection
+
+@section('body')
+
+@endsection
\ No newline at end of file
diff --git a/resources/views/clients/show.blade.php b/resources/views/clients/show.blade.php
index 57d5ffbbdd7e..7be5b4649b7b 100644
--- a/resources/views/clients/show.blade.php
+++ b/resources/views/clients/show.blade.php
@@ -145,7 +145,8 @@
@endif
@if ($contact->phone)
{{ $contact->phone }}
- @endif
+ @endif
+
{{ trans('texts.view_dashboard') }}
@endforeach
diff --git a/resources/views/emails/client_password.blade.php b/resources/views/emails/client_password.blade.php
index 9a586b3b5eb6..24f08e95f466 100644
--- a/resources/views/emails/client_password.blade.php
+++ b/resources/views/emails/client_password.blade.php
@@ -8,7 +8,7 @@
@include('partials.email_button', [
- 'link' => URL::to("client/password/reset/".session('invitation_key')."/{$token}"),
+ 'link' => URL::to("client/password/reset/".session('contact_key')."/{$token}"),
'field' => 'reset',
'color' => '#36c157',
])
diff --git a/resources/views/invoices/edit.blade.php b/resources/views/invoices/edit.blade.php
index f32cdcdc70cf..42aa40d27457 100644
--- a/resources/views/invoices/edit.blade.php
+++ b/resources/views/invoices/edit.blade.php
@@ -1226,6 +1226,10 @@
}
function onSaveClick() {
+ @if(!empty($autoBillChangeWarning))
+ if(!confirm("{!! trans('texts.warn_change_auto_bill') !!}"))return;
+ @endif
+
if (model.invoice().is_recurring()) {
// warn invoice will be emailed when saving new recurring invoice
if ({{ $invoice->exists ? 'false' : 'true' }}) {
@@ -1355,6 +1359,10 @@
@if ($invoice->id)
function onPaymentClick() {
+ @if(!empty($autoBillChangeWarning))
+ if(!confirm("{!! trans('texts.warn_change_auto_bill') !!}"))return;
+ @endif
+
window.location = '{{ URL::to('payments/create/' . $invoice->client->public_id . '/' . $invoice->public_id ) }}';
}
diff --git a/resources/views/invoices/view.blade.php b/resources/views/invoices/view.blade.php
index 096b7b74f765..2db836ec78f4 100644
--- a/resources/views/invoices/view.blade.php
+++ b/resources/views/invoices/view.blade.php
@@ -59,6 +59,35 @@
})
});
+ @elseif(!empty($enableWePayACH))
+
+
@endif
@stop
diff --git a/resources/views/payments/add_paymentmethod.blade.php b/resources/views/payments/add_paymentmethod.blade.php
index 6d4e9ff24185..db734f2299a4 100644
--- a/resources/views/payments/add_paymentmethod.blade.php
+++ b/resources/views/payments/add_paymentmethod.blade.php
@@ -6,7 +6,7 @@
@include('payments.tokenization_braintree')
@elseif (isset($accountGateway) && $accountGateway->getPublishableStripeKey())
@include('payments.tokenization_stripe')
- @elseif (isset($accountGateway) && $accountGateway->gateway_id == GATEWAY_WEPAY)
+ @elseif (isset($accountGateway) && $accountGateway->gateway_id == GATEWAY_WEPAY && $paymentType != PAYMENT_TYPE_WEPAY_ACH)
@include('payments.tokenization_wepay')
@else
+@elseif($gateway->gateway_id == GATEWAY_WEPAY && $gateway->getAchEnabled())
+
+
@endif
@if(!empty($paymentMethods))
@foreach ($paymentMethods as $paymentMethod)
@@ -59,11 +85,15 @@
•••••{{$paymentMethod->last4}}
@endif
@if($paymentMethod->payment_type_id == PAYMENT_TYPE_ACH)
- @if($paymentMethod->bank_data)
- {{ $paymentMethod->bank_data->name }}
+ @if($paymentMethod->bank_name)
+ {{ $paymentMethod->bank_name }}
@endif
@if($paymentMethod->status == PAYMENT_METHOD_STATUS_NEW)
- ({{trans('texts.complete_verification')}})
+ @if($gateway->gateway_id == GATEWAY_STRIPE)
+ ({{trans('texts.complete_verification')}})
+ @else
+ ({{ trans('texts.verification_pending') }})
+ @endif
@elseif($paymentMethod->status == PAYMENT_METHOD_STATUS_VERIFICATION_FAILED)
({{trans('texts.verification_failed')}})
@endif
@@ -88,8 +118,14 @@
->asLinkTo(URL::to('/client/paymentmethods/add/'.($gateway->getPaymentType() == PAYMENT_TYPE_STRIPE ? 'stripe_credit_card' : 'credit_card'))) !!}
@if($gateway->getACHEnabled())
+ @if($gateway->gateway_id == GATEWAY_STRIPE)
{!! Button::success(strtoupper(trans('texts.add_bank_account')))
->asLinkTo(URL::to('/client/paymentmethods/add/stripe_ach')) !!}
+ @elseif($gateway->gateway_id == GATEWAY_WEPAY)
+ {!! Button::success(strtoupper(trans('texts.add_bank_account')))
+ ->withAttributes(['id'=>'add-ach'])
+ ->asLinkTo(URL::to('/client/paymentmethods/add/wepay_ach')) !!}
+ @endif
@endif
@if($gateway->getPayPalEnabled())