diff --git a/app/Http/Controllers/ClientPortalController.php b/app/Http/Controllers/ClientPortalController.php
index 50058c8bc8..2bb550759b 100644
--- a/app/Http/Controllers/ClientPortalController.php
+++ b/app/Http/Controllers/ClientPortalController.php
@@ -189,8 +189,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 = '';
}
@@ -304,6 +304,7 @@ class ClientPortalController extends BaseController
$data = [
'color' => $color,
+ 'contact' => $contact,
'account' => $account,
'client' => $client,
'clientFontUrl' => $account->getFontsUrl(),
@@ -472,9 +473,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;
}
@@ -762,6 +770,7 @@ class ClientPortalController extends BaseController
$data = array(
'account' => $account,
+ 'contact' => $contact,
'color' => $account->primary_color ? $account->primary_color : '#0b4d78',
'client' => $client,
'clientViewCSS' => $account->clientViewCSS(),
@@ -835,7 +844,7 @@ class ClientPortalController extends BaseController
$gateway = $accountGateway->gateway;
if ($token && $paymentType == PAYMENT_TYPE_BRAINTREE_PAYPAL) {
- $sourceReference = $this->paymentService->createToken($this->paymentService->createGateway($accountGateway), array('token'=>$token), $accountGateway, $client, $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);
@@ -864,8 +873,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');
}
@@ -874,7 +887,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;
}
@@ -897,7 +910,7 @@ class ClientPortalController extends BaseController
$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'));
}
@@ -909,21 +922,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, $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'));
diff --git a/app/Http/Controllers/PaymentController.php b/app/Http/Controllers/PaymentController.php
index 07d20a1571..f62f24f513 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,6 +153,9 @@ class PaymentController extends BaseController
Session::put($invitation->id.'payment_ref', $invoice->id.'_'.uniqid());
+ $details = json_decode(Input::get('details'));
+ $data['details'] = $details;
+
if ($paymentType != PAYMENT_TYPE_BRAINTREE_PAYPAL) {
if ($paymentType == PAYMENT_TYPE_TOKEN) {
$useToken = true;
@@ -205,16 +207,14 @@ class PaymentController extends BaseController
}
} else {
- if ($deviceData = Input::get('details')) {
+ if ($deviceData = Input::get('device_data')) {
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) {
+ if (!$sourceId || !$details) {
return Redirect::to('view/'.$invitationKey);
}
- $data['paypalDetails'] = $paypalDetails;
}
$data += [
@@ -405,7 +405,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 +422,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 +473,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;
+ }
}
}
@@ -494,6 +509,7 @@ class PaymentController extends BaseController
$gateway = $this->paymentService->createGateway($accountGateway);
$details = $this->paymentService->getPaymentDetails($invitation, $accountGateway, $data);
+ $details['paymentType'] = $paymentType;
// check if we're creating/using a billing token
$tokenBillingSupported = false;
diff --git a/app/Models/Account.php b/app/Models/Account.php
index de1328862b..98237ce02e 100644
--- a/app/Models/Account.php
+++ b/app/Models/Account.php
@@ -385,6 +385,10 @@ class Account extends Eloquent
$type = PAYMENT_TYPE_STRIPE;
}
+ if ($type == PAYMENT_TYPE_WEPAY_ACH) {
+ return $this->getGatewayConfig(GATEWAY_WEPAY);
+ }
+
foreach ($this->account_gateways as $gateway) {
if ($exceptFor && ($gateway->id == $exceptFor->id)) {
continue;
diff --git a/app/Ninja/Datatables/PaymentDatatable.php b/app/Ninja/Datatables/PaymentDatatable.php
index 22006bf742..92473c7fd2 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/Repositories/PaymentRepository.php b/app/Ninja/Repositories/PaymentRepository.php
index ef6c880964..498f0d1d69 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 0f1ede8b3f..77f489049f 100644
--- a/app/Services/PaymentService.php
+++ b/app/Services/PaymentService.php
@@ -306,7 +306,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 */);
@@ -398,27 +398,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'];
@@ -516,12 +525,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;
}
@@ -570,10 +596,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'],
));
}
@@ -690,6 +718,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;
}
@@ -876,6 +908,7 @@ class PaymentService extends BaseService
$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;
}
@@ -1117,6 +1150,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/resources/lang/en/texts.php b/resources/lang/en/texts.php
index 5218c16fe5..c0f4f97f4e 100644
--- a/resources/lang/en/texts.php
+++ b/resources/lang/en/texts.php
@@ -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',
@@ -1327,6 +1327,12 @@ $LANG = array(
'auto_bill_on_due_date' => 'Auto bill on due date instead of send 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.',
);
return $LANG;
diff --git a/resources/views/payments/add_paymentmethod.blade.php b/resources/views/payments/add_paymentmethod.blade.php
index e17624af22..db734f2299 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