diff --git a/app/Http/Controllers/AccountGatewayController.php b/app/Http/Controllers/AccountGatewayController.php
index 08b2d1355f80..1ef8d61e98b8 100644
--- a/app/Http/Controllers/AccountGatewayController.php
+++ b/app/Http/Controllers/AccountGatewayController.php
@@ -117,7 +117,7 @@ class AccountGatewayController extends BaseController
}
}
- $paymentTypes[$type] = trans('texts.'.strtolower($type));
+ $paymentTypes[$type] = $type == PAYMENT_TYPE_CREDIT_CARD ? trans('texts.other_providers'): trans('texts.'.strtolower($type));
if ($type == PAYMENT_TYPE_BITCOIN) {
$paymentTypes[$type] .= ' - BitPay';
diff --git a/app/Http/Controllers/ClientAuth/AuthController.php b/app/Http/Controllers/ClientAuth/AuthController.php
index ed5f199ac255..e3951b5464b8 100644
--- a/app/Http/Controllers/ClientAuth/AuthController.php
+++ b/app/Http/Controllers/ClientAuth/AuthController.php
@@ -32,9 +32,8 @@ class AuthController extends Controller {
$invoice = $invitation->invoice;
$client = $invoice->client;
$account = $client->account;
-
- $data['hideLogo'] = $account->hasFeature(FEATURE_WHITE_LABEL);
- $data['clientViewCSS'] = $account->clientViewCSS();
+
+ $data['account'] = $account;
$data['clientFontUrl'] = $account->getFontsUrl();
}
}
diff --git a/app/Http/Controllers/ClientAuth/PasswordController.php b/app/Http/Controllers/ClientAuth/PasswordController.php
index 35576b47ae39..822764315a0e 100644
--- a/app/Http/Controllers/ClientAuth/PasswordController.php
+++ b/app/Http/Controllers/ClientAuth/PasswordController.php
@@ -49,9 +49,7 @@ class PasswordController extends Controller {
$invoice = $invitation->invoice;
$client = $invoice->client;
$account = $client->account;
-
- $data['hideLogo'] = $account->hasFeature(FEATURE_WHITE_LABEL);
- $data['clientViewCSS'] = $account->clientViewCSS();
+ $data['account'] = $account;
$data['clientFontUrl'] = $account->getFontsUrl();
}
}
@@ -116,9 +114,8 @@ class PasswordController extends Controller {
$invoice = $invitation->invoice;
$client = $invoice->client;
$account = $client->account;
-
- $data['hideLogo'] = $account->hasFeature(FEATURE_WHITE_LABEL);
- $data['clientViewCSS'] = $account->clientViewCSS();
+
+ $data['account'] = $account;
$data['clientFontUrl'] = $account->getFontsUrl();
}
}
diff --git a/app/Http/Controllers/PaymentController.php b/app/Http/Controllers/PaymentController.php
index f9c497c64ea8..fadcdd0394b4 100644
--- a/app/Http/Controllers/PaymentController.php
+++ b/app/Http/Controllers/PaymentController.php
@@ -137,7 +137,7 @@ class PaymentController extends BaseController
];
}
- public function show_payment($invitationKey, $paymentType = false)
+ 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();
@@ -155,27 +155,31 @@ class PaymentController extends BaseController
if ($paymentType == PAYMENT_TYPE_TOKEN) {
$useToken = true;
- $paymentType = PAYMENT_TYPE_CREDIT_CARD;
+ $accountGateway = $invoice->client->account->getTokenGateway();
+ $paymentType = $accountGateway->getPaymentType();
+ } else {
+ $accountGateway = $invoice->client->account->getGatewayByType($paymentType);
}
+
Session::put($invitation->id . 'payment_type', $paymentType);
- $accountGateway = $invoice->client->account->getGatewayByType($paymentType);
$gateway = $accountGateway->gateway;
$acceptedCreditCardTypes = $accountGateway->getCreditcardTypes();
-
- // Handle offsite payments
- if ($useToken || $paymentType != PAYMENT_TYPE_CREDIT_CARD
+ $isOffsite = ($paymentType != PAYMENT_TYPE_CREDIT_CARD && $accountGateway->getPaymentType() != PAYMENT_TYPE_STRIPE)
|| $gateway->id == GATEWAY_EWAY
|| $gateway->id == GATEWAY_TWO_CHECKOUT
|| $gateway->id == GATEWAY_PAYFAST
- || $gateway->id == GATEWAY_MOLLIE) {
+ || $gateway->id == GATEWAY_MOLLIE;
+
+ // Handle offsite payments
+ if ($useToken || $isOffsite) {
if (Session::has('error')) {
Session::reflash();
return Redirect::to('view/'.$invitationKey);
} else {
- return self::do_payment($invitationKey, false, $useToken);
+ return self::do_payment($invitationKey, false, $useToken, $sourceId);
}
}
@@ -189,22 +193,24 @@ class PaymentController extends BaseController
'gateway' => $gateway,
'accountGateway' => $accountGateway,
'acceptedCreditCardTypes' => $acceptedCreditCardTypes,
+ 'paymentType' => $paymentType,
'countries' => Cache::get('countries'),
'currencyId' => $client->getCurrencyId(),
'currencyCode' => $client->currency ? $client->currency->code : ($account->currency ? $account->currency->code : 'USD'),
'account' => $client->account,
- 'hideLogo' => $account->hasFeature(FEATURE_WHITE_LABEL),
- 'hideHeader' => $account->isNinjaAccount(),
- 'clientViewCSS' => $account->clientViewCSS(),
- 'clientFontUrl' => $account->getFontsUrl(),
+ 'clientFontUrl' => $client->account->getFontsUrl(),
'showAddress' => $accountGateway->show_address,
];
- if ($gateway->id = GATEWAY_BRAINTREE) {
+ if ($paymentType == PAYMENT_TYPE_STRIPE_ACH) {
+ $data['currencies'] = Cache::get('currencies');
+ }
+
+ if ($gateway->id == GATEWAY_BRAINTREE) {
$data['braintreeClientToken'] = $this->paymentService->getBraintreeClientToken($account);
}
- return View::make('payments.payment', $data);
+ return View::make('payments.add_paymentmethod', $data);
}
public function show_license_payment()
@@ -235,7 +241,7 @@ class PaymentController extends BaseController
$account = $this->accountRepo->getNinjaAccount();
$account->load('account_gateways.gateway');
- $accountGateway = $account->getGatewayByType(PAYMENT_TYPE_CREDIT_CARD);
+ $accountGateway = $account->getGatewayByType(PAYMENT_TYPE_STRIPE_CREDIT_CARD);
$gateway = $accountGateway->gateway;
$acceptedCreditCardTypes = $accountGateway->getCreditcardTypes();
@@ -260,7 +266,7 @@ class PaymentController extends BaseController
'showAddress' => true,
];
- return View::make('payments.payment', $data);
+ return View::make('payments.add_paymentmethod', $data);
}
public function do_license_payment()
@@ -291,7 +297,7 @@ class PaymentController extends BaseController
$account = $this->accountRepo->getNinjaAccount();
$account->load('account_gateways.gateway');
- $accountGateway = $account->getGatewayByType(PAYMENT_TYPE_CREDIT_CARD);
+ $accountGateway = $account->getGatewayByType(PAYMENT_TYPE_STRIPE_CREDIT_CARD);
try {
$affiliate = Affiliate::find(Session::get('affiliate_id'));
@@ -367,13 +373,14 @@ class PaymentController extends BaseController
}
}
- public function do_payment($invitationKey, $onSite = true, $useToken = false)
+ public function do_payment($invitationKey, $onSite = true, $useToken = false, $sourceId = false)
{
$invitation = Invitation::with('invoice.invoice_items', 'invoice.client.currency', 'invoice.client.account.currency', 'invoice.client.account.account_gateways.gateway')->where('invitation_key', '=', $invitationKey)->firstOrFail();
$invoice = $invitation->invoice;
$client = $invoice->client;
$account = $client->account;
- $accountGateway = $account->getGatewayByType(Session::get($invitation->id . 'payment_type'));
+ $paymentType = Session::get($invitation->id . 'payment_type');
+ $accountGateway = $account->getGatewayByType($paymentType);
$rules = [
@@ -445,11 +452,20 @@ class PaymentController extends BaseController
if ($useToken) {
$details['customerReference'] = $client->getGatewayToken();
unset($details['token']);
- } elseif ($account->token_billing_type_id == TOKEN_BILLING_ALWAYS || Input::get('token_billing')) {
- $token = $this->paymentService->createToken($gateway, $details, $accountGateway, $client, $invitation->contact_id, $customerReference);
+ if ($sourceId) {
+ $details['cardReference'] = $sourceId;
+ }
+ } 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 */);
if ($token) {
$details['token'] = $token;
$details['customerReference'] = $customerReference;
+
+ if ($paymentType == PAYMENT_TYPE_STRIPE_ACH && empty($usingPlaid) ) {
+ // The user needs to complete verification
+ Session::flash('message', trans('texts.bank_account_verification_next_steps'));
+ return Redirect::to('/client/paymentmethods');
+ }
} else {
$this->error('Token-No-Ref', $this->paymentService->lastError, $accountGateway);
return Redirect::to('payment/'.$invitationKey)->withInput(Request::except('cvv'));
@@ -463,11 +479,15 @@ class PaymentController extends BaseController
if ($useToken) {
$details['customerId'] = $customerId = $client->getGatewayToken();
- $customer = $gateway->findCustomer($customerId)->send();
- $details['paymentMethodToken'] = $customer->getData()->paymentMethods[0]->token;
+ if (!$sourceId) {
+ $customer = $gateway->findCustomer($customerId)->send();
+ $details['paymentMethodToken'] = $customer->getData()->paymentMethods[0]->token;
+ } else {
+ $details['paymentMethodToken'] = $sourceId;
+ }
unset($details['token']);
} elseif ($account->token_billing_type_id == TOKEN_BILLING_ALWAYS || Input::get('token_billing')) {
- $token = $this->paymentService->createToken($gateway, $details, $accountGateway, $client, $invitation->contact_id, $customerReference);
+ $token = $this->paymentService->createToken($gateway, $details, $accountGateway, $client, $invitation->contact_id, $customerReference/* return parameter */);
if ($token) {
$details['paymentMethodToken'] = $token;
$details['customerId'] = $customerReference;
@@ -683,4 +703,85 @@ class PaymentController extends BaseController
Session::flash('error', $message);
Utils::logError("Payment Error [{$type}]: " . ($exception ? Utils::getErrorString($exception) : $message), 'PHP', true);
}
+
+ public function getBankInfo($routingNumber) {
+ if (strlen($routingNumber) != 9 || !preg_match('/\d{9}/', $routingNumber)) {
+ return response()->json([
+ 'message' => 'Invalid routing number',
+ ], 400);
+ }
+
+ $data = static::getBankData($routingNumber);
+
+ if (is_string($data)) {
+ return response()->json([
+ 'message' => $data,
+ ], 500);
+ } elseif (!empty($data)) {
+ return $data;
+ }
+
+ return response()->json([
+ 'message' => 'Bank not found',
+ ], 404);
+ }
+
+ public static function getBankData($routingNumber) {
+ $cached = Cache::get('bankData:'.$routingNumber);
+
+ if ($cached != null) {
+ return $cached == false ? null : $cached;
+ }
+
+ $dataPath = base_path('vendor/gatepay/FedACHdir/FedACHdir.txt');
+
+ if (!file_exists($dataPath) || !$size = filesize($dataPath)) {
+ return 'Invalid data file';
+ }
+
+ $lineSize = 157;
+ $numLines = $size/$lineSize;
+
+ if ($numLines % 1 != 0) {
+ // The number of lines should be an integer
+ return 'Invalid data file';
+ }
+
+ // Format: http://www.sco.ca.gov/Files-21C/Bank_Master_Interface_Information_Package.pdf
+ $file = fopen($dataPath, 'r');
+
+ // Binary search
+ $low = 0;
+ $high = $numLines - 1;
+ while ($low <= $high) {
+ $mid = floor(($low + $high) / 2);
+
+ fseek($file, $mid * $lineSize);
+ $thisNumber = fread($file, 9);
+
+ if ($thisNumber > $routingNumber) {
+ $high = $mid - 1;
+ } else if ($thisNumber < $routingNumber) {
+ $low = $mid + 1;
+ } else {
+ $data = array('routing_number' => $thisNumber);
+ fseek($file, 26, SEEK_CUR);
+ $data['name'] = trim(fread($file, 36));
+ $data['address'] = trim(fread($file, 36));
+ $data['city'] = trim(fread($file, 20));
+ $data['state'] = fread($file, 2);
+ $data['zip'] = fread($file, 5).'-'.fread($file, 4);
+ $data['phone'] = fread($file, 10);
+ break;
+ }
+ }
+
+ if (!empty($data)) {
+ Cache::put('bankData:'.$routingNumber, $data, 5);
+ return $data;
+ } else {
+ Cache::put('bankData:'.$routingNumber, false, 5);
+ return null;
+ }
+ }
}
diff --git a/app/Http/Controllers/PublicClientController.php b/app/Http/Controllers/PublicClientController.php
index 28391cf7140e..868a116719e8 100644
--- a/app/Http/Controllers/PublicClientController.php
+++ b/app/Http/Controllers/PublicClientController.php
@@ -10,6 +10,8 @@ use Request;
use Response;
use Session;
use Datatable;
+use Validator;
+use Cache;
use App\Models\Gateway;
use App\Models\Invitation;
use App\Models\Document;
@@ -94,7 +96,7 @@ class PublicClientController extends BaseController
$paymentTypes = $this->getPaymentTypes($client, $invitation);
$paymentURL = '';
- if (count($paymentTypes)) {
+ if (count($paymentTypes) == 1) {
$paymentURL = $paymentTypes[0]['url'];
if (!$account->isGatewayConfigured(GATEWAY_PAYPAL_EXPRESS)) {
$paymentURL = URL::to($paymentURL);
@@ -126,11 +128,6 @@ class PublicClientController extends BaseController
'account' => $account,
'showApprove' => $showApprove,
'showBreadcrumbs' => false,
- 'hideLogo' => $account->hasFeature(FEATURE_WHITE_LABEL),
- 'hideHeader' => $account->isNinjaAccount() || !$account->enable_client_portal,
- 'hideDashboard' => !$account->enable_client_portal_dashboard,
- 'showDocuments' => $account->hasFeature(FEATURE_DOCUMENTS),
- 'clientViewCSS' => $account->clientViewCSS(),
'clientFontUrl' => $account->getFontsUrl(),
'invoice' => $invoice->hidePrivateFields(),
'invitation' => $invitation,
@@ -161,23 +158,67 @@ class PublicClientController extends BaseController
$paymentTypes = [];
$account = $client->account;
- if ($client->getGatewayToken()) {
- $paymentTypes[] = [
- 'url' => URL::to("payment/{$invitation->invitation_key}/token"), 'label' => trans('texts.use_card_on_file')
- ];
- }
- foreach(Gateway::$paymentTypes as $type) {
- if ($account->getGatewayByType($type)) {
- $typeLink = strtolower(str_replace('PAYMENT_TYPE_', '', $type));
- $url = URL::to("/payment/{$invitation->invitation_key}/{$typeLink}");
+ $paymentMethods = $this->paymentService->getClientPaymentMethods($client);
- // 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 ($paymentMethods) {
+ foreach ($paymentMethods as $paymentMethod) {
+ if ($paymentMethod['type']->id != PAYMENT_TYPE_ACH || $paymentMethod['status'] == 'verified') {
+
+ if ($paymentMethod['type']->id == PAYMENT_TYPE_ACH) {
+ $html = '
'.trans('texts.card_expiration', array('expires' => Utils::fromSqlDate($paymentMethod['expiration'], false)->format('m/y'))).'
';
+ } else {
+ $html .= '
';
+ }
+ $html .= '•••'.$paymentMethod['last4'].'
';
+
+ $paymentTypes[] = [
+ 'url' => URL::to("/payment/{$invitation->invitation_key}/token/".$paymentMethod['id']),
+ 'label' => $html,
+ ];
+ }
+ }
+ }
+
+
+ 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;
+ }
+ }
+
+ foreach($types as $type) {
+ $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
+ ];
}
- $paymentTypes[] = [
- 'url' => $url, 'label' => trans('texts.'.strtolower($type))
- ];
}
}
@@ -224,9 +265,6 @@ class PublicClientController extends BaseController
'color' => $color,
'account' => $account,
'client' => $client,
- 'hideLogo' => $account->hasFeature(FEATURE_WHITE_LABEL),
- 'showDocuments' => $account->hasFeature(FEATURE_DOCUMENTS),
- 'clientViewCSS' => $account->clientViewCSS(),
'clientFontUrl' => $account->getFontsUrl(),
];
@@ -248,7 +286,7 @@ class PublicClientController extends BaseController
->addColumn('activity_type_id', function ($model) {
$data = [
'client' => Utils::getClientDisplayName($model),
- 'user' => $model->is_system ? ('
' . trans('texts.system') . '') : ($model->user_first_name . ' ' . $model->user_last_name),
+ 'user' => $model->is_system ? ('
' . trans('texts.system') . '') : ($model->user_first_name . ' ' . $model->user_last_name),
'invoice' => trans('texts.invoice') . ' ' . $model->invoice,
'contact' => Utils::getClientDisplayName($model),
'payment' => trans('texts.payment') . ($model->payment ? ' ' . $model->payment : ''),
@@ -280,10 +318,7 @@ class PublicClientController extends BaseController
$data = [
'color' => $color,
- 'hideLogo' => $account->hasFeature(FEATURE_WHITE_LABEL),
- 'hideDashboard' => !$account->enable_client_portal_dashboard,
- 'showDocuments' => $account->hasFeature(FEATURE_DOCUMENTS),
- 'clientViewCSS' => $account->clientViewCSS(),
+ 'account' => $account,
'clientFontUrl' => $account->getFontsUrl(),
'title' => trans('texts.invoices'),
'entityType' => ENTITY_INVOICE,
@@ -317,10 +352,7 @@ class PublicClientController extends BaseController
$color = $account->primary_color ? $account->primary_color : '#0b4d78';
$data = [
'color' => $color,
- 'hideLogo' => $account->hasFeature(FEATURE_WHITE_LABEL),
- 'hideDashboard' => !$account->enable_client_portal_dashboard,
- 'showDocuments' => $account->hasFeature(FEATURE_DOCUMENTS),
- 'clientViewCSS' => $account->clientViewCSS(),
+ 'account' => $account,
'clientFontUrl' => $account->getFontsUrl(),
'entityType' => ENTITY_PAYMENT,
'title' => trans('texts.payments'),
@@ -345,8 +377,17 @@ class PublicClientController extends BaseController
if (!$model->last4) return '';
$code = str_replace(' ', '', strtolower($model->payment_type));
$card_type = trans("texts.card_" . $code);
- $expiration = trans('texts.card_expiration', array('expires'=>Utils::fromSqlDate($model->expiration, false)->format('m/y')));
- return '
.')
•••'.$model->last4.' '.$expiration;
+ if ($model->payment_type_id != PAYMENT_TYPE_ACH) {
+ $expiration = trans('texts.card_expiration', array('expires' => Utils::fromSqlDate($model->expiration, false)->format('m/y')));
+ return '
 . ')
•••' . $model->last4 . ' ' . $expiration;
+ } else {
+ $bankData = PaymentController::getBankData($model->routing_number);
+ if (is_array($bankData)) {
+ return $bankData['name'].' •••' . $model->last4;
+ } else {
+ return '
 . ')
•••' . $model->last4;
+ }
+ }
})
->addColumn('amount', function ($model) { return Utils::formatMoney($model->amount, $model->currency_id, $model->country_id); })
->addColumn('payment_date', function ($model) { return Utils::dateToString($model->payment_date); })
@@ -397,10 +438,7 @@ class PublicClientController extends BaseController
$color = $account->primary_color ? $account->primary_color : '#0b4d78';
$data = [
'color' => $color,
- 'hideLogo' => $account->hasFeature(FEATURE_WHITE_LABEL),
- 'hideDashboard' => !$account->enable_client_portal_dashboard,
- 'showDocuments' => $account->hasFeature(FEATURE_DOCUMENTS),
- 'clientViewCSS' => $account->clientViewCSS(),
+ 'account' => $account,
'clientFontUrl' => $account->getFontsUrl(),
'title' => trans('texts.quotes'),
'entityType' => ENTITY_QUOTE,
@@ -435,10 +473,7 @@ class PublicClientController extends BaseController
$color = $account->primary_color ? $account->primary_color : '#0b4d78';
$data = [
'color' => $color,
- 'hideLogo' => $account->hasFeature(FEATURE_WHITE_LABEL),
- 'hideDashboard' => !$account->enable_client_portal_dashboard,
- 'showDocuments' => $account->hasFeature(FEATURE_DOCUMENTS),
- 'clientViewCSS' => $account->clientViewCSS(),
+ 'account' => $account,
'clientFontUrl' => $account->getFontsUrl(),
'title' => trans('texts.documents'),
'entityType' => ENTITY_DOCUMENT,
@@ -632,4 +667,169 @@ class PublicClientController extends BaseController
return DocumentController::getDownloadResponse($document);
}
+ public function paymentMethods()
+ {
+ if (!$invitation = $this->getInvitation()) {
+ return $this->returnError();
+ }
+
+ $client = $invitation->invoice->client;
+ $account = $client->account;
+ $paymentMethods = $this->paymentService->getClientPaymentMethods($client);
+
+ $data = array(
+ 'account' => $account,
+ 'color' => $account->primary_color ? $account->primary_color : '#0b4d78',
+ 'client' => $client,
+ 'clientViewCSS' => $account->clientViewCSS(),
+ 'clientFontUrl' => $account->getFontsUrl(),
+ 'paymentMethods' => $paymentMethods,
+ 'gateway' => $account->getTokenGateway(),
+ 'title' => trans('texts.payment_methods')
+ );
+
+ return response()->view('payments.paymentmethods', $data);
+ }
+
+ public function verifyPaymentMethod()
+ {
+ $sourceId = Input::get('source_id');
+ $amount1 = Input::get('verification1');
+ $amount2 = Input::get('verification2');
+
+ if (!$invitation = $this->getInvitation()) {
+ return $this->returnError();
+ }
+
+ $client = $invitation->invoice->client;
+ $result = $this->paymentService->verifyClientPaymentMethod($client, $sourceId, $amount1, $amount2);
+
+ if (is_string($result)) {
+ Session::flash('error', $result);
+ } else {
+ Session::flash('message', trans('texts.payment_method_verified'));
+ }
+
+ return redirect()->to('/client/paymentmethods/');
+ }
+
+ public function removePaymentMethod($sourceId)
+ {
+ if (!$invitation = $this->getInvitation()) {
+ return $this->returnError();
+ }
+
+ $client = $invitation->invoice->client;
+ $result = $this->paymentService->removeClientPaymentMethod($client, $sourceId);
+
+ if (is_string($result)) {
+ Session::flash('error', $result);
+ } else {
+ Session::flash('message', trans('texts.payment_method_removed'));
+ }
+
+ return redirect()->to('/client/paymentmethods/');
+ }
+
+ public function addPaymentMethod($paymentType)
+ {
+ if (!$invitation = $this->getInvitation()) {
+ return $this->returnError();
+ }
+
+ $invoice = $invitation->invoice;
+ $client = $invitation->invoice->client;
+ $account = $client->account;
+
+ $typeLink = $paymentType;
+ $paymentType = 'PAYMENT_TYPE_' . strtoupper($paymentType);
+ $accountGateway = $invoice->client->account->getTokenGateway();
+ $gateway = $accountGateway->gateway;
+ $acceptedCreditCardTypes = $accountGateway->getCreditcardTypes();
+
+
+ $data = [
+ 'showBreadcrumbs' => false,
+ 'client' => $client,
+ 'contact' => $invitation->contact,
+ 'gateway' => $gateway,
+ 'accountGateway' => $accountGateway,
+ 'acceptedCreditCardTypes' => $acceptedCreditCardTypes,
+ 'paymentType' => $paymentType,
+ 'countries' => Cache::get('countries'),
+ 'currencyId' => $client->getCurrencyId(),
+ 'currencyCode' => $client->currency ? $client->currency->code : ($account->currency ? $account->currency->code : 'USD'),
+ 'account' => $account,
+ 'url' => URL::to('client/paymentmethods/add/'.$typeLink),
+ 'clientFontUrl' => $account->getFontsUrl(),
+ 'showAddress' => $accountGateway->show_address,
+ ];
+
+ if ($paymentType == PAYMENT_TYPE_STRIPE_ACH) {
+
+ $data['currencies'] = Cache::get('currencies');
+ }
+
+ if ($gateway->id == GATEWAY_BRAINTREE) {
+ $data['braintreeClientToken'] = $this->paymentService->getBraintreeClientToken($account);
+ }
+
+ return View::make('payments.add_paymentmethod', $data);
+ }
+
+ public function postAddPaymentMethod($paymentType)
+ {
+ if (!$invitation = $this->getInvitation()) {
+ return $this->returnError();
+ }
+
+ $paymentType = 'PAYMENT_TYPE_' . strtoupper($paymentType);
+ $client = $invitation->invoice->client;
+ $account = $client->account;
+
+ $accountGateway = $account->getGatewayByType($paymentType);
+ $sourceToken = $accountGateway->gateway_id == GATEWAY_STRIPE ? Input::get('stripeToken'):Input::get('payment_method_nonce');
+
+ if ($sourceToken) {
+ $details = array('token' => $sourceToken);
+ $gateway = $this->paymentService->createGateway($accountGateway);
+ $sourceId = $this->paymentService->createToken($gateway, $details, $accountGateway, $client, $invitation->contact_id);
+ } else {
+ return Redirect::to('payment/'.$invitationKey)->withInput(Request::except('cvv'));
+ }
+
+ if(empty($sourceId)) {
+ $this->error('Token-No-Ref', $this->paymentService->lastError, $accountGateway);
+ return Redirect::to('payment/'.$invitationKey)->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'));
+ return Redirect::to('client/paymentmethods/add/' . $paymentType);
+ } else {
+ Session::flash('message', trans('texts.payment_method_added'));
+ return redirect()->to('/client/paymentmethods/');
+ }
+ }
+
+ public function setDefaultPaymentMethod(){
+ if (!$invitation = $this->getInvitation()) {
+ return $this->returnError();
+ }
+
+ $validator = Validator::make(Input::all(), array('source' => 'required'));
+ if ($validator->fails()) {
+ return Redirect::to('client/paymentmethods');
+ }
+
+ $client = $invitation->invoice->client;
+ $result = $this->paymentService->setClientDefaultPaymentMethod($client, Input::get('source'));
+
+ if (is_string($result)) {
+ Session::flash('error', $result);
+ } else {
+ Session::flash('message', trans('texts.payment_method_set_as_default'));
+ }
+
+ return redirect()->to('/client/paymentmethods/');
+ }
}
diff --git a/app/Http/routes.php b/app/Http/routes.php
index d6cd8f497aec..d21883c566b9 100644
--- a/app/Http/routes.php
+++ b/app/Http/routes.php
@@ -40,9 +40,15 @@ Route::group(['middleware' => 'auth:client'], function() {
Route::get('download/{invitation_key}', 'PublicClientController@download');
Route::get('view', 'HomeController@viewLogo');
Route::get('approve/{invitation_key}', 'QuoteController@approve');
- Route::get('payment/{invitation_key}/{payment_type?}', 'PaymentController@show_payment');
+ Route::get('payment/{invitation_key}/{payment_type?}/{source_id?}', 'PaymentController@show_payment');
Route::post('payment/{invitation_key}', 'PaymentController@do_payment');
Route::match(['GET', 'POST'], 'complete', 'PaymentController@offsite_payment');
+ Route::get('client/paymentmethods', 'PublicClientController@paymentMethods');
+ Route::post('client/paymentmethods/verify', 'PublicClientController@verifyPaymentMethod');
+ Route::get('client/paymentmethods/add/{payment_type}', 'PublicClientController@addPaymentMethod');
+ Route::post('client/paymentmethods/add/{payment_type}', 'PublicClientController@postAddPaymentMethod');
+ Route::post('client/paymentmethods/default', 'PublicClientController@setDefaultPaymentMethod');
+ Route::post('client/paymentmethods/{source_id}/remove', 'PublicClientController@removePaymentMethod');
Route::get('client/quotes', 'PublicClientController@quoteIndex');
Route::get('client/invoices', 'PublicClientController@invoiceIndex');
Route::get('client/documents', 'PublicClientController@documentIndex');
@@ -60,6 +66,7 @@ Route::group(['middleware' => 'auth:client'], function() {
});
+Route::get('bank/{routing_number}', 'PaymentController@getBankInfo');
Route::get('license', 'PaymentController@show_license_payment');
Route::post('license', 'PaymentController@do_license_payment');
Route::get('claim_license', 'PaymentController@claim_license');
@@ -615,6 +622,7 @@ if (!defined('CONTACT_EMAIL')) {
define('TOKEN_BILLING_ALWAYS', 4);
define('PAYMENT_TYPE_CREDIT', 1);
+ define('PAYMENT_TYPE_ACH', 5);
define('PAYMENT_TYPE_VISA', 6);
define('PAYMENT_TYPE_MASTERCARD', 7);
define('PAYMENT_TYPE_AMERICAN_EXPRESS', 8);
@@ -633,6 +641,8 @@ if (!defined('CONTACT_EMAIL')) {
define('PAYMENT_TYPE_PAYPAL', 'PAYMENT_TYPE_PAYPAL');
define('PAYMENT_TYPE_STRIPE', 'PAYMENT_TYPE_STRIPE');
+ define('PAYMENT_TYPE_STRIPE_CREDIT_CARD', 'PAYMENT_TYPE_STRIPE_CREDIT_CARD');
+ define('PAYMENT_TYPE_STRIPE_ACH', 'PAYMENT_TYPE_STRIPE_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 44b49d44e1fe..234d9a5b63ca 100644
--- a/app/Models/Account.php
+++ b/app/Models/Account.php
@@ -379,6 +379,10 @@ class Account extends Eloquent
public function getGatewayByType($type = PAYMENT_TYPE_ANY)
{
+ if ($type == PAYMENT_TYPE_STRIPE_ACH || $type == PAYMENT_TYPE_STRIPE_CREDIT_CARD) {
+ $type = PAYMENT_TYPE_STRIPE;
+ }
+
foreach ($this->account_gateways as $gateway) {
if (!$type || $type == PAYMENT_TYPE_ANY) {
return $gateway;
diff --git a/app/Ninja/Repositories/PaymentRepository.php b/app/Ninja/Repositories/PaymentRepository.php
index c1c8d3e851dd..221cea40ce99 100644
--- a/app/Ninja/Repositories/PaymentRepository.php
+++ b/app/Ninja/Repositories/PaymentRepository.php
@@ -56,6 +56,7 @@ class PaymentRepository extends BaseRepository
'payments.refunded',
'payments.expiration',
'payments.last4',
+ 'payments.routing_number',
'invoices.is_deleted as invoice_is_deleted',
'gateways.name as gateway_name',
'gateways.id as gateway_id',
@@ -107,6 +108,7 @@ class PaymentRepository extends BaseRepository
'clients.public_id as client_public_id',
'payments.amount',
'payments.payment_date',
+ 'payments.payment_type_id',
'invoices.public_id as invoice_public_id',
'invoices.invoice_number',
'contacts.first_name',
@@ -117,6 +119,7 @@ class PaymentRepository extends BaseRepository
'payments.refunded',
'payments.expiration',
'payments.last4',
+ 'payments.routing_number',
'payments.payment_status_id',
'payment_statuses.name as payment_status_name'
);
diff --git a/app/Services/PaymentService.php b/app/Services/PaymentService.php
index 6bfcf2e12559..8ad8a990eb34 100644
--- a/app/Services/PaymentService.php
+++ b/app/Services/PaymentService.php
@@ -5,6 +5,7 @@ use Auth;
use URL;
use DateTime;
use Event;
+use Cache;
use Omnipay;
use Session;
use CreditCard;
@@ -13,6 +14,7 @@ use App\Models\Account;
use App\Models\Country;
use App\Models\Client;
use App\Models\Invoice;
+use App\Http\Controllers\PaymentController;
use App\Models\AccountGatewayToken;
use App\Ninja\Repositories\PaymentRepository;
use App\Ninja\Repositories\AccountRepository;
@@ -155,8 +157,159 @@ class PaymentService extends BaseService
];
}
+ public function getClientPaymentMethods($client) {
+ $token = $client->getGatewayToken($accountGateway);
+ if (!$token) {
+ return null;
+ }
+
+ $gateway = $this->createGateway($accountGateway);
+
+ $paymentMethods = array();
+ if ($accountGateway->gateway_id == GATEWAY_STRIPE) {
+ $response = $gateway->fetchCustomer(array('customerReference' => $token))->send();
+ if (!$response->isSuccessful()) {
+ return null;
+ }
+
+ $data = $response->getData();
+ $default_source = $data['default_source'];
+ $sources = isset($data['sources']) ? $data['sources']['data'] : $data['cards']['data'];
+
+ $paymentTypes = Cache::get('paymentTypes');
+ $currencies = Cache::get('currencies');
+ foreach ($sources as $source) {
+ if ($source['object'] == 'bank_account') {
+ $paymentMethods[] = array(
+ 'id' => $source['id'],
+ 'default' => $source['id'] == $default_source,
+ 'type' => $paymentTypes->find(PAYMENT_TYPE_ACH),
+ 'currency' => $currencies->where('code', strtoupper($source['currency']))->first(),
+ 'last4' => $source['last4'],
+ 'routing_number' => $source['routing_number'],
+ 'bank_name' => $source['bank_name'],
+ 'status' => $source['status'],
+ );
+ } elseif ($source['object'] == 'card') {
+ $paymentMethods[] = array(
+ 'id' => $source['id'],
+ 'default' => $source['id'] == $default_source,
+ 'type' => $paymentTypes->find($this->parseCardType($source['brand'])),
+ 'last4' => $source['last4'],
+ 'expiration' => $source['exp_year'] . '-' . $source['exp_month'] . '-00',
+ );
+ }
+ }
+ } elseif ($accountGateway->gateway_id == GATEWAY_BRAINTREE) {
+
+ }
+
+ return $paymentMethods;
+ }
+
+ public function verifyClientPaymentMethod($client, $sourceId, $amount1, $amount2) {
+ $token = $client->getGatewayToken($accountGateway);
+ if ($accountGateway->gateway_id != GATEWAY_STRIPE) {
+ return 'Unsupported gateway';
+ }
+
+ try{
+ // Omnipay doesn't support verifying payment methods
+ // Also, it doesn't want to urlencode without putting numbers inside the brackets
+ $response = (new \GuzzleHttp\Client(['base_uri'=>'https://api.stripe.com/v1/']))->request(
+ 'POST',
+ 'customers/'.$token.'/sources/'.$sourceId.'/verify',
+ [
+ 'body' => 'amounts[]='.intval($amount1).'&amounts[]='.intval($amount2),
+ 'headers' => ['content-type' => 'application/x-www-form-urlencoded'],
+ 'auth' => [$accountGateway->getConfig()->apiKey,''],
+ ]
+ );
+ return json_decode($response->getBody(), true);
+ } catch (\GuzzleHttp\Exception\BadResponseException $e) {
+ $response = $e->getResponse();
+ $body = json_decode($response->getBody(), true);
+
+ if ($body && $body['error'] && $body['error']['type'] == 'invalid_request_error') {
+ return $body['error']['message'];
+ }
+
+ return $e->getMessage();
+ }
+ }
+
+ public function removeClientPaymentMethod($client, $sourceId) {
+ $token = $client->getGatewayToken($accountGateway/* return parameter */);
+ if (!$token) {
+ return null;
+ }
+
+ $gateway = $this->createGateway($accountGateway);
+
+ if ($accountGateway->gateway_id == GATEWAY_STRIPE) {
+ $response = $gateway->deleteCard(array('customerReference' => $token, 'cardReference'=>$sourceId))->send();
+ if (!$response->isSuccessful()) {
+ return $response->getMessage();
+ }
+ } elseif ($accountGateway->gateway_id == GATEWAY_BRAINTREE) {
+
+ }
+
+ return true;
+ }
+
+ public function setClientDefaultPaymentMethod($client, $sourceId) {
+ $token = $client->getGatewayToken($accountGateway/* return parameter */);
+ if (!$token) {
+ return null;
+ }
+
+ $gateway = $this->createGateway($accountGateway);
+
+ if ($accountGateway->gateway_id == GATEWAY_STRIPE) {
+ try{
+ // Omnipay doesn't support setting a default source
+ $response = (new \GuzzleHttp\Client(['base_uri'=>'https://api.stripe.com/v1/']))->request(
+ 'POST',
+ 'customers/'.$token,
+ [
+ 'body' => 'default_card='.$sourceId,
+ 'headers' => ['content-type' => 'application/x-www-form-urlencoded'],
+ 'auth' => [$accountGateway->getConfig()->apiKey,''],
+ ]
+ );
+ return true;
+ } catch (\GuzzleHttp\Exception\BadResponseException $e) {
+ $response = $e->getResponse();
+ $body = json_decode($response->getBody(), true);
+
+ if ($body && $body['error'] && $body['error']['type'] == 'invalid_request_error') {
+ return $body['error']['message'];
+ }
+
+ return $e->getMessage();
+ }
+ } elseif ($accountGateway->gateway_id == GATEWAY_BRAINTREE) {
+
+ }
+
+ return true;
+ }
+
public function createToken($gateway, $details, $accountGateway, $client, $contactId, &$customerReference = null)
{
+ $customerReference = $client->getGatewayToken();
+
+ if ($customerReference) {
+ $details['customerReference'] = $customerReference;
+
+ $customerResponse = $gateway->fetchCustomer(array('customerReference'=>$customerReference))->send();
+
+ if (!$customerResponse->isSuccessful()){
+ $customerReference = null; // The customer might not exist anymore
+ }
+ }
+
if ($accountGateway->gateway->id == GATEWAY_STRIPE) {
$tokenResponse = $gateway->createCard($details)->send();
$cardReference = $tokenResponse->getCardReference();
@@ -170,10 +323,15 @@ class PaymentService extends BaseService
}
}
} elseif ($accountGateway->gateway->id == GATEWAY_BRAINTREE) {
- $tokenResponse = $gateway->createCustomer(array('customerData'=>array()))->send();
+ if (!$customerReference) {
+ $tokenResponse = $gateway->createCustomer(array('customerData' => array()))->send();
+ if ($tokenResponse->isSuccessful()) {
+ $customerReference = $tokenResponse->getCustomerData()->id;
+ }
+ }
- if ($tokenResponse->isSuccessful()) {
- $details['customerId'] = $customerReference = $tokenResponse->getCustomerData()->id;
+ if ($customerReference) {
+ $details['customerId'] = $customerReference;
$tokenResponse = $gateway->createPaymentMethod($details)->send();
$cardReference = $tokenResponse->getData()->paymentMethod->token;
@@ -264,54 +422,26 @@ class PaymentService extends BaseService
}
if ($accountGateway->gateway_id == GATEWAY_STRIPE) {
- $card = $purchaseResponse->getSource();
- if (!$card) {
- $card = $purchaseResponse->getCard();
- }
+ $data = $purchaseResponse->getData();
+ $source = !empty($data['source'])?$data['source']:$data['card'];
- if ($card) {
- $payment->last4 = $card['last4'];
- $payment->expiration = $card['exp_year'] . '-' . $card['exp_month'] . '-00';
- $stripe_card_types = array(
- 'Visa' => PAYMENT_TYPE_VISA,
- 'American Express' => PAYMENT_TYPE_AMERICAN_EXPRESS,
- 'MasterCard' => PAYMENT_TYPE_MASTERCARD,
- 'Discover' => PAYMENT_TYPE_DISCOVER,
- 'JCB' => PAYMENT_TYPE_JCB,
- 'Diners Club' => PAYMENT_TYPE_DINERS,
- );
+ if ($source) {
+ $payment->last4 = $source['last4'];
- if (!empty($stripe_card_types[$card['brand']])) {
- $payment->payment_type_id = $stripe_card_types[$card['brand']];
- } else {
- $payment->payment_type_id = PAYMENT_TYPE_CREDIT_CARD_OTHER;
+ if ($source['object'] == 'bank_account') {
+ $payment->routing_number = $source['routing_number'];
+ $payment->payment_type_id = PAYMENT_TYPE_ACH;
+ }
+ else{
+ $payment->expiration = $card['exp_year'] . '-' . $card['exp_month'] . '-00';
+ $payment->payment_type_id = $this->parseCardType($card['brand']);
}
}
} elseif ($accountGateway->gateway_id == GATEWAY_BRAINTREE) {
$card = $purchaseResponse->getData()->transaction->creditCardDetails;
$payment->last4 = $card->last4;
$payment->expiration = $card->expirationYear . '-' . $card->expirationMonth . '-00';
-
- $braintree_card_types = array(
- 'Visa' => PAYMENT_TYPE_VISA,
- 'American Express' => PAYMENT_TYPE_AMERICAN_EXPRESS,
- 'MasterCard' => PAYMENT_TYPE_MASTERCARD,
- 'Discover' => PAYMENT_TYPE_DISCOVER,
- 'JCB' => PAYMENT_TYPE_JCB,
- 'Diners Club' => PAYMENT_TYPE_DINERS,
- 'Carte Blanche' => PAYMENT_TYPE_CARTE_BLANCHE,
- 'China UnionPay' => PAYMENT_TYPE_UNIONPAY,
- 'Laser' => PAYMENT_TYPE_LASER,
- 'Maestro' => PAYMENT_TYPE_MAESTRO,
- 'Solo' => PAYMENT_TYPE_SOLO,
- 'Switch' => PAYMENT_TYPE_SWITCH,
- );
-
- if (!empty($braintree_card_types[$card->cardType])) {
- $payment->payment_type_id = $braintree_card_types[$card->cardType];
- } else {
- $payment->payment_type_id = PAYMENT_TYPE_CREDIT_CARD_OTHER;
- }
+ $payment->payment_type_id = $this->parseCardType($card->cardType);
}
if ($payerId) {
@@ -375,6 +505,29 @@ class PaymentService extends BaseService
return $payment;
}
+
+ private function parseCardType($cardName) {
+ $cardTypes = array(
+ 'Visa' => PAYMENT_TYPE_VISA,
+ 'American Express' => PAYMENT_TYPE_AMERICAN_EXPRESS,
+ 'MasterCard' => PAYMENT_TYPE_MASTERCARD,
+ 'Discover' => PAYMENT_TYPE_DISCOVER,
+ 'JCB' => PAYMENT_TYPE_JCB,
+ 'Diners Club' => PAYMENT_TYPE_DINERS,
+ 'Carte Blanche' => PAYMENT_TYPE_CARTE_BLANCHE,
+ 'China UnionPay' => PAYMENT_TYPE_UNIONPAY,
+ 'Laser' => PAYMENT_TYPE_LASER,
+ 'Maestro' => PAYMENT_TYPE_MAESTRO,
+ 'Solo' => PAYMENT_TYPE_SOLO,
+ 'Switch' => PAYMENT_TYPE_SWITCH
+ );
+
+ if (!empty($cardTypes[$cardName])) {
+ return $cardTypes[$cardName];
+ } else {
+ return PAYMENT_TYPE_CREDIT_CARD_OTHER;
+ }
+ }
private function detectCardType($number)
{
@@ -493,12 +646,21 @@ class PaymentService extends BaseService
],
[
'source',
- function ($model) {
+ function ($model) {
if (!$model->last4) return '';
$code = str_replace(' ', '', strtolower($model->payment_type));
$card_type = trans("texts.card_" . $code);
- $expiration = trans('texts.card_expiration', array('expires'=>Utils::fromSqlDate($model->expiration, false)->format('m/y')));
- return '
.')
•••'.$model->last4.' '.$expiration;
+ if ($model->payment_type_id != PAYMENT_TYPE_ACH) {
+ $expiration = trans('texts.card_expiration', array('expires' => Utils::fromSqlDate($model->expiration, false)->format('m/y')));
+ return '
 . ')
•••' . $model->last4 . ' ' . $expiration;
+ } else {
+ $bankData = PaymentController::getBankData($model->routing_number);
+ if (is_array($bankData)) {
+ return $bankData['name'].' •••' . $model->last4;
+ } else {
+ return '
 . ')
•••' . $model->last4;
+ }
+ }
}
],
[
diff --git a/composer.json b/composer.json
index 8eafa6224f42..01c6a26f51cf 100644
--- a/composer.json
+++ b/composer.json
@@ -17,7 +17,7 @@
"omnipay/mollie": "dev-master#22956c1a62a9662afa5f5d119723b413770ac525",
"omnipay/2checkout": "dev-master#e9c079c2dde0d7ba461903b3b7bd5caf6dee1248",
"omnipay/gocardless": "dev-master",
- "omnipay/stripe": "2.3.2",
+ "omnipay/stripe": "dev-master",
"laravel/framework": "5.2.*",
"laravelcollective/html": "5.2.*",
"laravelcollective/bus": "5.2.*",
@@ -73,7 +73,8 @@
"league/flysystem-aws-s3-v3": "~1.0",
"league/flysystem-rackspace": "~1.0",
"barracudanetworks/archivestream-php": "^1.0",
- "omnipay/braintree": "~2.0@dev"
+ "omnipay/braintree": "~2.0@dev",
+ "gatepay/FedACHdir": "dev-master@dev"
},
"require-dev": {
"phpunit/phpunit": "~4.0",
@@ -122,5 +123,23 @@
},
"config": {
"preferred-install": "dist"
- }
+ },
+ "repositories": [
+ {
+ "type": "package",
+ "package": {
+ "name": "gatepay/FedACHdir",
+ "version": "dev-master",
+ "dist": {
+ "url": "https://github.com/gatepay/FedACHdir/archive/master.zip",
+ "type": "zip"
+ },
+ "source": {
+ "url": "git@github.com:gatepay/FedACHdir.git",
+ "type": "git",
+ "reference": "origin/master"
+ }
+ }
+ }
+ ]
}
diff --git a/composer.lock b/composer.lock
index 787493b08070..23b08f2d7e9d 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,8 +4,8 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
- "hash": "e2b43d6bd5e87dfb9b3be07e89a2bd9f",
- "content-hash": "480134957ff37fd0f46b5d90909da660",
+ "hash": "039f9d8f2e342f6c05dadb3523883a47",
+ "content-hash": "fd558fd1e187969baf015eab8e288e5b",
"packages": [
{
"name": "agmscode/omnipay-agms",
@@ -2011,6 +2011,17 @@
],
"time": "2015-01-16 08:41:13"
},
+ {
+ "name": "gatepay/FedACHdir",
+ "version": "dev-master",
+ "source": {
+ "type": "git",
+ "url": "git@github.com:gatepay/FedACHdir.git",
+ "reference": "origin/master"
+ },
+ "type": "library",
+ "time": "2016-04-29 12:01:22"
+ },
{
"name": "guzzle/guzzle",
"version": "v3.8.1",
@@ -5771,16 +5782,16 @@
},
{
"name": "omnipay/stripe",
- "version": "v2.3.2",
+ "version": "dev-master",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/omnipay-stripe.git",
- "reference": "fe05ddd73d9ae38ca026dbc270ca98aaf3f99da4"
+ "reference": "0ea7a647ee01e29c152814e11c2ea6307e5b0db9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/thephpleague/omnipay-stripe/zipball/fe05ddd73d9ae38ca026dbc270ca98aaf3f99da4",
- "reference": "fe05ddd73d9ae38ca026dbc270ca98aaf3f99da4",
+ "url": "https://api.github.com/repos/thephpleague/omnipay-stripe/zipball/0ea7a647ee01e29c152814e11c2ea6307e5b0db9",
+ "reference": "0ea7a647ee01e29c152814e11c2ea6307e5b0db9",
"shasum": ""
},
"require": {
@@ -5824,7 +5835,7 @@
"payment",
"stripe"
],
- "time": "2016-03-19 08:35:06"
+ "time": "2016-04-26 08:34:50"
},
{
"name": "omnipay/targetpay",
@@ -9985,6 +9996,7 @@
"omnipay/mollie": 20,
"omnipay/2checkout": 20,
"omnipay/gocardless": 20,
+ "omnipay/stripe": 20,
"anahkiasen/former": 20,
"chumper/datatable": 20,
"intervention/image": 20,
@@ -10005,7 +10017,8 @@
"labs7in0/omnipay-wechat": 20,
"laracasts/presenter": 20,
"jlapp/swaggervel": 20,
- "omnipay/braintree": 20
+ "omnipay/braintree": 20,
+ "gatepay/fedachdir": 20
},
"prefer-stable": false,
"prefer-lowest": false,
diff --git a/public/images/credit_cards/ach.png b/public/images/credit_cards/ach.png
new file mode 100644
index 000000000000..16cf86bdb3ec
Binary files /dev/null and b/public/images/credit_cards/ach.png differ
diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php
index 467e836a2820..83fdb5481daa 100644
--- a/resources/lang/en/texts.php
+++ b/resources/lang/en/texts.php
@@ -502,7 +502,7 @@ $LANG = array(
'resend_confirmation' => 'Resend confirmation email',
'confirmation_resent' => 'The confirmation email was resent',
'gateway_help_42' => ':link to sign up for BitPay.
Note: use a Legacy API Key, not an API token.',
- 'payment_type_credit_card' => 'Other Providers',
+ 'payment_type_credit_card' => 'Credit Card',
'payment_type_paypal' => 'PayPal',
'payment_type_bitcoin' => 'Bitcoin',
'knowledge_base' => 'Knowledge Base',
@@ -1205,6 +1205,7 @@ $LANG = array(
'card_solo' => 'Solo',
'card_switch' => 'Switch',
'card_visacard' => 'Visa',
+ 'card_ach' => 'ACH',
'payment_type_stripe' => 'Stripe',
'ach' => 'ACH',
@@ -1218,6 +1219,39 @@ $LANG = array(
'public_key' => 'Public Key',
'plaid_optional' => '(optional)',
'plaid_environment_help' => 'When a Stripe test key is given, Plaid\'s development environement (tartan) will be used.',
+ 'other_providers' => 'Other Providers',
+ 'country_not_supported' => 'That country is not supported.',
+ 'invalid_routing_number' => 'The routing number is not valid.',
+ 'invalid_account_number' => 'The account number is not valid.',
+ 'account_number_mismatch' => 'The account numbers do not match.',
+ 'missing_account_holder_type' => 'Please select an individual or company account.',
+ 'missing_account_holder_name' => 'Please enter the account holder\'s name.',
+ 'routing_number' => 'Routing Number',
+ 'confirm_account_number' => 'Confirm Account Number',
+ 'individual_account' => 'Individual Account',
+ 'company_account' => 'Company Account',
+ 'account_holder_name' => 'Account Holder Name',
+ 'add_account' => 'Add Account',
+ 'payment_methods' => 'Payment Methods',
+ 'complete_verification' => 'Complete Verification',
+ 'verification_amount1' => 'Amount 1',
+ 'verification_amount2' => 'Amount 2',
+ 'payment_method_verified' => 'Verification completed successfully',
+ 'verification_failed' => 'Verification Failed',
+ 'remove_payment_method' => 'Remove Payment Method',
+ 'confirm_remove_payment_method' => 'Are you sure you want to remove this payment method?',
+ 'remove' => 'Remove',
+ 'payment_method_removed' => 'Removed payment method.',
+ 'bank_account_verification_help' => 'We have made two deposits into your account with the description "VERIFICATION". These deposits will take 1-2 business days to appear on your statement. Please enter the amounts below.',
+ 'bank_account_verification_next_steps' => 'We have made two deposits into your account with the description "VERIFICATION". These deposits will take 1-2 business days to appear on your statement.
+ Once you have the amounts, come back to this payment methods page and click "Complete Verification" next to the account.',
+ 'unknown_bank' => 'Unknown Bank',
+ 'ach_verification_delay_help' => 'You will be able to use the account after completing verification. Verification usually takes 1-2 business days.',
+ 'add_credit_card' => 'Add Credit Card',
+ 'payment_method_added' => 'Added payment method.',
+ 'use_for_auto_bill' => 'Use For Autobill',
+ 'used_for_auto_bill' => 'Autobill Payment Method',
+ 'payment_method_set_as_default' => 'Set Autobill payment method.'
);
return $LANG;
diff --git a/resources/views/clientauth/login.blade.php b/resources/views/clientauth/login.blade.php
index 6ad28a593831..e5baee7c05aa 100644
--- a/resources/views/clientauth/login.blade.php
+++ b/resources/views/clientauth/login.blade.php
@@ -69,7 +69,7 @@
{{ Former::populateField('remember', 'true') }}