mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-09 12:42:36 +01:00
Add basic ACH support
This commit is contained in:
parent
e5fb354643
commit
c536bd8569
@ -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';
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 = '<div>'.htmlentities($paymentMethod['bank_name']).'</div>';
|
||||
} else {
|
||||
$code = htmlentities(str_replace(' ', '', strtolower($paymentMethod['type']->name)));
|
||||
$html = '<img height="22" src="'.URL::to('/images/credit_cards/'.$code.'.png').'" alt="'.trans("texts.card_".$code).'">';
|
||||
}
|
||||
|
||||
if ($paymentMethod['type']->id != PAYMENT_TYPE_ACH) {
|
||||
$html .= '<div class="pull-right" style="text-align:right">'.trans('texts.card_expiration', array('expires' => Utils::fromSqlDate($paymentMethod['expiration'], false)->format('m/y'))).'<br>';
|
||||
} else {
|
||||
$html .= '<div style="text-align:right">';
|
||||
}
|
||||
$html .= '•••'.$paymentMethod['last4'].'</div>';
|
||||
|
||||
$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 ? ('<i>' . trans('texts.system') . '</i>') : ($model->user_first_name . ' ' . $model->user_last_name),
|
||||
'user' => $model->is_system ? ('<i>' . trans('texts.system') . '</i>') : ($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 '<img height="22" src="'.URL::to('/images/credit_cards/'.$code.'.png').'" alt="'.htmlentities($card_type).'"> •••'.$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 '<img height="22" src="' . URL::to('/images/credit_cards/' . $code . '.png') . '" alt="' . htmlentities($card_type) . '"> •••' . $model->last4 . ' ' . $expiration;
|
||||
} else {
|
||||
$bankData = PaymentController::getBankData($model->routing_number);
|
||||
if (is_array($bankData)) {
|
||||
return $bankData['name'].' •••' . $model->last4;
|
||||
} else {
|
||||
return '<img height="22" src="' . URL::to('/images/credit_cards/ach.png') . '" alt="' . htmlentities($card_type) . '"> •••' . $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/');
|
||||
}
|
||||
}
|
||||
|
@ -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');
|
||||
|
@ -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;
|
||||
|
@ -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'
|
||||
);
|
||||
|
@ -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 '<img height="22" src="'.URL::to('/images/credit_cards/'.$code.'.png').'" alt="'.htmlentities($card_type).'"> •••'.$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 '<img height="22" src="' . URL::to('/images/credit_cards/' . $code . '.png') . '" alt="' . htmlentities($card_type) . '"> •••' . $model->last4 . ' ' . $expiration;
|
||||
} else {
|
||||
$bankData = PaymentController::getBankData($model->routing_number);
|
||||
if (is_array($bankData)) {
|
||||
return $bankData['name'].' •••' . $model->last4;
|
||||
} else {
|
||||
return '<img height="22" src="' . URL::to('/images/credit_cards/ach.png') . '" alt="' . htmlentities($card_type) . '"> •••' . $model->last4;
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
29
composer.lock
generated
29
composer.lock
generated
@ -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,
|
||||
|
BIN
public/images/credit_cards/ach.png
Normal file
BIN
public/images/credit_cards/ach.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
@ -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.<br/>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;
|
||||
|
@ -69,7 +69,7 @@
|
||||
{{ Former::populateField('remember', 'true') }}
|
||||
|
||||
<div class="modal-header">
|
||||
@if (!isset($hideLogo) || !$hideLogo)
|
||||
@if (!isset($account) || !$account->hasFeature(FEATURE_WHITE_LABEL))
|
||||
<a href="{{ NINJA_WEB_URL }}" target="_blank">
|
||||
<img src="{{ asset('images/icon-login.png') }}" />
|
||||
<h4>Invoice Ninja | {{ trans('texts.account_login') }}</h4>
|
||||
|
@ -54,7 +54,7 @@
|
||||
|
||||
{!! Former::open('client/recover_password')->addClass('form-signin') !!}
|
||||
<div class="modal-header">
|
||||
@if (!isset($hideLogo) || !$hideLogo)
|
||||
@if (!isset($account) || !$account->hasFeature(FEATURE_WHITE_LABEL))
|
||||
<a href="{{ NINJA_WEB_URL }}" target="_blank">
|
||||
<img src="{{ asset('images/icon-login.png') }}" />
|
||||
<h4>Invoice Ninja | {{ trans('texts.password_recovery') }}</h4>
|
||||
|
@ -58,7 +58,7 @@
|
||||
)) !!}
|
||||
|
||||
<div class="modal-header">
|
||||
@if (!isset($hideLogo) || !$hideLogo)
|
||||
@if (!isset($account) || !$account->hasFeature(FEATURE_WHITE_LABEL))
|
||||
<a href="{{ NINJA_WEB_URL }}" target="_blank">
|
||||
<img src="{{ asset('images/icon-login.png') }}" />
|
||||
<h4>Invoice Ninja | {{ trans('texts.set_password') }}</h4>
|
||||
|
@ -14,6 +14,12 @@
|
||||
body {
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
.dropdown-menu li a{
|
||||
overflow:hidden;
|
||||
margin-top:5px;
|
||||
margin-bottom:5px;
|
||||
}
|
||||
</style>
|
||||
@stop
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{App::getLocale()}}">
|
||||
<head>
|
||||
@if (isset($hideLogo) && $hideLogo)
|
||||
@if (isset($account) && $account->hasFeature(FEATURE_WHITE_LABEL))
|
||||
<title>{{ trans('texts.client_portal') }}</title>
|
||||
@else
|
||||
<title>{{ isset($title) ? ($title . ' | Invoice Ninja') : ('Invoice Ninja | ' . trans('texts.app_title')) }}</title>
|
||||
|
611
resources/views/payments/add_paymentmethod.blade.php
Normal file
611
resources/views/payments/add_paymentmethod.blade.php
Normal file
@ -0,0 +1,611 @@
|
||||
@extends('public.header')
|
||||
|
||||
@section('head')
|
||||
@parent
|
||||
@if (!empty($braintreeClientToken))
|
||||
<script type="text/javascript" src="https://js.braintreegateway.com/js/braintree-2.23.0.min.js"></script>
|
||||
<script type="text/javascript" >
|
||||
$(function() {
|
||||
braintree.setup("{{ $braintreeClientToken }}", "custom", {
|
||||
id: "payment-form",
|
||||
hostedFields: {
|
||||
number: {
|
||||
selector: "#card_number",
|
||||
placeholder: "{{ trans('texts.card_number') }}"
|
||||
},
|
||||
cvv: {
|
||||
selector: "#cvv",
|
||||
placeholder: "{{ trans('texts.cvv') }}"
|
||||
},
|
||||
expirationMonth: {
|
||||
selector: "#expiration_month",
|
||||
placeholder: "{{ trans('texts.expiration_month') }}"
|
||||
},
|
||||
expirationYear: {
|
||||
selector: "#expiration_year",
|
||||
placeholder: "{{ trans('texts.expiration_year') }}"
|
||||
},
|
||||
styles: {
|
||||
'input': {
|
||||
'font-family': {!! json_encode(Utils::getFromCache($account->getBodyFontId(), 'fonts')['css_stack']) !!},
|
||||
'font-weight': "{{ Utils::getFromCache($account->getBodyFontId(), 'fonts')['css_weight'] }}",
|
||||
'font-size': '16px'
|
||||
}
|
||||
}
|
||||
},
|
||||
onError: function(e) {
|
||||
// Show the errors on the form
|
||||
if (e.details && e.details.invalidFieldKeys.length) {
|
||||
var invalidField = e.details.invalidFieldKeys[0];
|
||||
|
||||
if (invalidField == 'number') {
|
||||
$('#js-error-message').html('{{ trans('texts.invalid_card_number') }}').fadeIn();
|
||||
}
|
||||
else if (invalidField == 'expirationDate' || invalidField == 'expirationYear' || invalidField == 'expirationMonth') {
|
||||
$('#js-error-message').html('{{ trans('texts.invalid_expiry') }}').fadeIn();
|
||||
}
|
||||
else if (invalidField == 'cvv') {
|
||||
$('#js-error-message').html('{{ trans('texts.invalid_cvv') }}').fadeIn();
|
||||
}
|
||||
}
|
||||
else {
|
||||
$('#js-error-message').html(e.message).fadeIn();
|
||||
}
|
||||
}
|
||||
});
|
||||
$('.payment-form').submit(function(event) {
|
||||
$('#js-error-message').hide();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@elseif ($accountGateway->getPublishableStripeKey())
|
||||
<script type="text/javascript" src="https://js.stripe.com/v2/"></script>
|
||||
<script type="text/javascript">
|
||||
Stripe.setPublishableKey('{{ $accountGateway->getPublishableStripeKey() }}');
|
||||
$(function() {
|
||||
$('.payment-form').submit(function(event) {
|
||||
var $form = $(this);
|
||||
|
||||
var data = {
|
||||
@if($paymentType == PAYMENT_TYPE_STRIPE_ACH)
|
||||
account_holder_name: $('#account_holder_name').val(),
|
||||
account_holder_type: $('[name=account_holder_type]:checked').val(),
|
||||
currency: $("#currency").val(),
|
||||
country: $("#country").val(),
|
||||
routing_number: $('#routing_number').val().replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ''),
|
||||
account_number: $('#account_number').val().replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '')
|
||||
@else
|
||||
name: $('#first_name').val() + ' ' + $('#last_name').val(),
|
||||
address_line1: $('#address1').val(),
|
||||
address_line2: $('#address2').val(),
|
||||
address_city: $('#city').val(),
|
||||
address_state: $('#state').val(),
|
||||
address_zip: $('#postal_code').val(),
|
||||
address_country: $("#country_id option:selected").text(),
|
||||
number: $('#card_number').val(),
|
||||
cvc: $('#cvv').val(),
|
||||
exp_month: $('#expiration_month').val(),
|
||||
exp_year: $('#expiration_year').val()
|
||||
@endif
|
||||
};
|
||||
|
||||
@if($paymentType == PAYMENT_TYPE_STRIPE_ACH)
|
||||
// Validate the account details
|
||||
if (!data.account_holder_type) {
|
||||
$('#js-error-message').html('{{ trans('texts.missing_account_holder_type') }}').fadeIn();
|
||||
return false;
|
||||
}
|
||||
if (!data.account_holder_name) {
|
||||
$('#js-error-message').html('{{ trans('texts.missing_account_holder_name') }}').fadeIn();
|
||||
return false;
|
||||
}
|
||||
if (!data.routing_number || !Stripe.bankAccount.validateRoutingNumber(data.routing_number, data.country)) {
|
||||
$('#js-error-message').html('{{ trans('texts.invalid_routing_number') }}').fadeIn();
|
||||
return false;
|
||||
}
|
||||
if (data.account_number != $('#confirm_account_number').val().replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '')) {
|
||||
$('#js-error-message').html('{{ trans('texts.account_number_mismatch') }}').fadeIn();
|
||||
return false;
|
||||
}
|
||||
if (!data.account_number || !Stripe.bankAccount.validateAccountNumber(data.account_number, data.country)) {
|
||||
$('#js-error-message').html('{{ trans('texts.invalid_account_number') }}').fadeIn();
|
||||
return false;
|
||||
}
|
||||
@else
|
||||
// Validate the card details
|
||||
if (!Stripe.card.validateCardNumber(data.number)) {
|
||||
$('#js-error-message').html('{{ trans('texts.invalid_card_number') }}').fadeIn();
|
||||
return false;
|
||||
}
|
||||
if (!Stripe.card.validateExpiry(data.exp_month, data.exp_year)) {
|
||||
$('#js-error-message').html('{{ trans('texts.invalid_expiry') }}').fadeIn();
|
||||
return false;
|
||||
}
|
||||
if (!Stripe.card.validateCVC(data.cvc)) {
|
||||
$('#js-error-message').html('{{ trans('texts.invalid_cvv') }}').fadeIn();
|
||||
return false;
|
||||
}
|
||||
@endif
|
||||
|
||||
// Disable the submit button to prevent repeated clicks
|
||||
$form.find('button').prop('disabled', true);
|
||||
$('#js-error-message').hide();
|
||||
|
||||
@if($paymentType == PAYMENT_TYPE_STRIPE_ACH)
|
||||
Stripe.bankAccount.createToken(data, stripeResponseHandler);
|
||||
@else
|
||||
Stripe.card.createToken(data, stripeResponseHandler);
|
||||
@endif
|
||||
|
||||
// Prevent the form from submitting with the default action
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
function stripeResponseHandler(status, response) {
|
||||
var $form = $('.payment-form');
|
||||
|
||||
if (response.error) {
|
||||
// Show the errors on the form
|
||||
var error = response.error.message;
|
||||
@if($paymentType == PAYMENT_TYPE_STRIPE_ACH)
|
||||
if(response.error.param == 'bank_account[country]') {
|
||||
error = "{{trans('texts.country_not_supported')}}";
|
||||
}
|
||||
@endif
|
||||
$form.find('button').prop('disabled', false);
|
||||
$('#js-error-message').html(error).fadeIn();
|
||||
} else {
|
||||
// response contains id and card, which contains additional card details
|
||||
var token = response.id;
|
||||
// Insert the token into the form so it gets submitted to the server
|
||||
$form.append($('<input type="hidden" name="stripeToken"/>').val(token));
|
||||
// and submit
|
||||
$form.get(0).submit();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@else
|
||||
<script type="text/javascript">
|
||||
$(function() {
|
||||
$('.payment-form').submit(function(event) {
|
||||
var $form = $(this);
|
||||
|
||||
// Disable the submit button to prevent repeated clicks
|
||||
$form.find('button').prop('disabled', true);
|
||||
|
||||
return true;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endif
|
||||
|
||||
@stop
|
||||
|
||||
@section('content')
|
||||
|
||||
@include('payments.payment_css')
|
||||
|
||||
|
||||
@if($paymentType == PAYMENT_TYPE_STRIPE_ACH)
|
||||
{!! Former::vertical_open($url)
|
||||
->autocomplete('on')
|
||||
->addClass('payment-form')
|
||||
->id('payment-form')
|
||||
->rules(array(
|
||||
'first_name' => 'required',
|
||||
'last_name' => 'required',
|
||||
'account_number' => 'required',
|
||||
'routing_number' => 'required',
|
||||
'account_holder_name' => 'required',
|
||||
'account_holder_type' => 'required'
|
||||
)) !!}
|
||||
@else
|
||||
{!! Former::vertical_open($url)
|
||||
->autocomplete('on')
|
||||
->addClass('payment-form')
|
||||
->id('payment-form')
|
||||
->rules(array(
|
||||
'first_name' => 'required',
|
||||
'last_name' => 'required',
|
||||
'card_number' => 'required',
|
||||
'expiration_month' => 'required',
|
||||
'expiration_year' => 'required',
|
||||
'cvv' => 'required',
|
||||
'address1' => 'required',
|
||||
'city' => 'required',
|
||||
'state' => 'required',
|
||||
'postal_code' => 'required',
|
||||
'country_id' => 'required',
|
||||
'phone' => 'required',
|
||||
'email' => 'required|email'
|
||||
)) !!}
|
||||
@endif
|
||||
|
||||
@if ($client)
|
||||
{{ Former::populate($client) }}
|
||||
{{ Former::populateField('first_name', $contact->first_name) }}
|
||||
{{ Former::populateField('last_name', $contact->last_name) }}
|
||||
@if (!$client->country_id && $client->account->country_id)
|
||||
{{ Former::populateField('country_id', $client->account->country_id) }}
|
||||
{{ Former::populateField('country', $client->account->country->iso_3166_2) }}
|
||||
@endif
|
||||
@if (!$client->currency_id && $client->account->currency_id)
|
||||
{{ Former::populateField('currency_id', $client->account->currency_id) }}
|
||||
{{ Former::populateField('currency', $client->account->currency->code) }}
|
||||
@endif
|
||||
@endif
|
||||
|
||||
@if (Utils::isNinjaDev())
|
||||
{{ Former::populateField('first_name', 'Test') }}
|
||||
{{ Former::populateField('last_name', 'Test') }}
|
||||
{{ Former::populateField('address1', '350 5th Ave') }}
|
||||
{{ Former::populateField('city', 'New York') }}
|
||||
{{ Former::populateField('state', 'NY') }}
|
||||
{{ Former::populateField('postal_code', '10118') }}
|
||||
{{ Former::populateField('country_id', 840) }}
|
||||
@endif
|
||||
|
||||
|
||||
<div class="container">
|
||||
<p> </p>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-7">
|
||||
<header>
|
||||
@if ($client)
|
||||
<h2>{{ $client->getDisplayName() }}</h2>
|
||||
@if(isset($invoiceNumber))
|
||||
<h3>{{ trans('texts.invoice') . ' ' . $invoiceNumber }}<span>| {{ trans('texts.amount_due') }}: <em>{{ $account->formatMoney($amount, $client, true) }}</em></span></h3>
|
||||
@endif
|
||||
@elseif ($paymentTitle)
|
||||
<h2>{{ $paymentTitle }}<br/><small>{{ $paymentSubtitle }}</small></h2>
|
||||
@endif
|
||||
</header>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
@if (Request::secure() || Utils::isNinjaDev())
|
||||
<div class="secure">
|
||||
<h3>{{ trans('texts.secure_payment') }}</h3>
|
||||
<div>{{ trans('texts.256_encryption') }}</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p> <br/> </p>
|
||||
|
||||
<div>
|
||||
<h3>{{ trans('texts.contact_information') }}</h3>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
{!! Former::text('first_name')
|
||||
->placeholder(trans('texts.first_name'))
|
||||
->autocomplete('given-name')
|
||||
->label('') !!}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{!! Former::text('last_name')
|
||||
->placeholder(trans('texts.last_name'))
|
||||
->autocomplete('family-name')
|
||||
->label('') !!}
|
||||
</div>
|
||||
</div>
|
||||
@if (isset($paymentTitle))
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
{!! Former::text('email')
|
||||
->placeholder(trans('texts.email'))
|
||||
->autocomplete('email')
|
||||
->label('') !!}
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<p> <br/> </p>
|
||||
|
||||
@if ($showAddress)
|
||||
<h3>{{ trans('texts.billing_address') }}
|
||||
@if($paymentType != PAYMENT_TYPE_STRIPE_ACH)
|
||||
<span class="help">{{ trans('texts.payment_footer1') }}</span></h3>
|
||||
@endif
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
{!! Former::text('address1')
|
||||
->autocomplete('address-line1')
|
||||
->placeholder(trans('texts.address1'))
|
||||
->label('') !!}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{!! Former::text('address2')
|
||||
->autocomplete('address-line2')
|
||||
->placeholder(trans('texts.address2'))
|
||||
->label('') !!}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
{!! Former::text('city')
|
||||
->autocomplete('address-level2')
|
||||
->placeholder(trans('texts.city'))
|
||||
->label('') !!}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{!! Former::text('state')
|
||||
->autocomplete('address-level1')
|
||||
->placeholder(trans('texts.state'))
|
||||
->label('') !!}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
{!! Former::text('postal_code')
|
||||
->autocomplete('postal-code')
|
||||
->placeholder(trans('texts.postal_code'))
|
||||
->label('') !!}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{!! Former::select('country_id')
|
||||
->placeholder(trans('texts.country_id'))
|
||||
->fromQuery($countries, 'name', 'id')
|
||||
->addGroupClass('country-select')
|
||||
->label('') !!}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p> <br/> </p>
|
||||
@endif
|
||||
|
||||
<h3>{{ trans('texts.billing_method') }}</h3>
|
||||
|
||||
|
||||
|
||||
@if($paymentType == PAYMENT_TYPE_STRIPE_ACH)
|
||||
<p>{{ trans('texts.ach_verification_delay_help') }}</p>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="radio">
|
||||
{!! Former::radios('account_holder_type')->radios(array(
|
||||
trans('texts.individual_account') => array('value' => 'individual'),
|
||||
trans('texts.company_account') => array('value' => 'company'),
|
||||
))->inline()->label(''); !!}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{!! Former::text('account_holder_name')
|
||||
->placeholder(trans('texts.account_holder_name'))
|
||||
->label('') !!}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
{!! Former::select('country')
|
||||
->placeholder(trans('texts.country_id'))
|
||||
->fromQuery($countries, 'name', 'iso_3166_2')
|
||||
->addGroupClass('country-select')
|
||||
->label('') !!}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{!! Former::select('currency')
|
||||
->placeholder(trans('texts.currency_id'))
|
||||
->fromQuery($currencies, 'name', 'code')
|
||||
->addGroupClass('currency-select')
|
||||
->label('') !!}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
{!! Former::text('')
|
||||
->id('routing_number')
|
||||
->placeholder(trans('texts.routing_number'))
|
||||
->label('') !!}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div id="bank_name"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
{!! Former::text('')
|
||||
->id('account_number')
|
||||
->placeholder(trans('texts.account_number'))
|
||||
->label('') !!}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{!! Former::text('')
|
||||
->id('confirm_account_number')
|
||||
->placeholder(trans('texts.confirm_account_number'))
|
||||
->label('') !!}
|
||||
</div>
|
||||
</div>
|
||||
<center>
|
||||
{!! Button::success(strtoupper(trans('texts.add_account')))
|
||||
->submit()
|
||||
->large() !!}
|
||||
</center>
|
||||
@else
|
||||
<div class="row">
|
||||
<div class="col-md-9">
|
||||
@if (!empty($braintreeClientToken))
|
||||
<div id="card_number" class="braintree-hosted form-control"></div>
|
||||
@else
|
||||
{!! Former::text($accountGateway->getPublishableStripeKey() ? '' : 'card_number')
|
||||
->id('card_number')
|
||||
->placeholder(trans('texts.card_number'))
|
||||
->autocomplete('cc-number')
|
||||
->label('') !!}
|
||||
@endif
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
@if (!empty($braintreeClientToken))
|
||||
<div id="cvv" class="braintree-hosted form-control"></div>
|
||||
@else
|
||||
{!! Former::text($accountGateway->getPublishableStripeKey() ? '' : 'cvv')
|
||||
->id('cvv')
|
||||
->placeholder(trans('texts.cvv'))
|
||||
->autocomplete('off')
|
||||
->label('') !!}
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
@if (!empty($braintreeClientToken))
|
||||
<div id="expiration_month" class="braintree-hosted form-control"></div>
|
||||
@else
|
||||
{!! Former::select($accountGateway->getPublishableStripeKey() ? '' : 'expiration_month')
|
||||
->id('expiration_month')
|
||||
->autocomplete('cc-exp-month')
|
||||
->placeholder(trans('texts.expiration_month'))
|
||||
->addOption('01 - January', '1')
|
||||
->addOption('02 - February', '2')
|
||||
->addOption('03 - March', '3')
|
||||
->addOption('04 - April', '4')
|
||||
->addOption('05 - May', '5')
|
||||
->addOption('06 - June', '6')
|
||||
->addOption('07 - July', '7')
|
||||
->addOption('08 - August', '8')
|
||||
->addOption('09 - September', '9')
|
||||
->addOption('10 - October', '10')
|
||||
->addOption('11 - November', '11')
|
||||
->addOption('12 - December', '12')->label('')
|
||||
!!}
|
||||
@endif
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
@if (!empty($braintreeClientToken))
|
||||
<div id="expiration_year" class="braintree-hosted form-control"></div>
|
||||
@else
|
||||
{!! Former::select($accountGateway->getPublishableStripeKey() ? '' : 'expiration_year')
|
||||
->id('expiration_year')
|
||||
->autocomplete('cc-exp-year')
|
||||
->placeholder(trans('texts.expiration_year'))
|
||||
->addOption('2016', '2016')
|
||||
->addOption('2017', '2017')
|
||||
->addOption('2018', '2018')
|
||||
->addOption('2019', '2019')
|
||||
->addOption('2020', '2020')
|
||||
->addOption('2021', '2021')
|
||||
->addOption('2022', '2022')
|
||||
->addOption('2023', '2023')
|
||||
->addOption('2024', '2024')
|
||||
->addOption('2025', '2025')
|
||||
->addOption('2026', '2026')->label('')
|
||||
!!}
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" style="padding-top:18px">
|
||||
<div class="col-md-5">
|
||||
@if (isset($amount) && $client && $account->showTokenCheckbox($storageGateway/* will contain gateway id */))
|
||||
<input id="token_billing" type="checkbox" name="token_billing" {{ $account->selectTokenCheckbox() ? 'CHECKED' : '' }} value="1" style="margin-left:0px; vertical-align:top">
|
||||
<label for="token_billing" class="checkbox" style="display: inline;">{{ trans('texts.token_billing') }}</label>
|
||||
<span class="help-block" style="font-size:15px">
|
||||
@if ($storageGateway == GATEWAY_STRIPE)
|
||||
{!! trans('texts.token_billing_secure', ['link' => link_to('https://stripe.com/', 'Stripe.com', ['target' => '_blank'])]) !!}
|
||||
@elseif ($storageGateway == GATEWAY_BRAINTREE)
|
||||
{!! trans('texts.token_billing_secure', ['link' => link_to('https://www.braintreepayments.com/', 'Braintree', ['target' => '_blank'])]) !!}
|
||||
@endif
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="col-md-7">
|
||||
@if (isset($acceptedCreditCardTypes))
|
||||
<div class="pull-right">
|
||||
@foreach ($acceptedCreditCardTypes as $card)
|
||||
<img src="{{ $card['source'] }}" alt="{{ $card['alt'] }}" style="width: 70px; display: inline; margin-right: 6px;"/>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p> </p>
|
||||
<center>
|
||||
@if(isset($amount))
|
||||
{!! Button::success(strtoupper(trans('texts.pay_now') . ' - ' . $account->formatMoney($amount, $client, true) ))
|
||||
->submit()
|
||||
->large() !!}
|
||||
@else
|
||||
{!! Button::success(strtoupper(trans('texts.add_credit_card') ))
|
||||
->submit()
|
||||
->large() !!}
|
||||
@endif
|
||||
</center>
|
||||
<p> </p>
|
||||
@endif
|
||||
|
||||
<div id="js-error-message" style="display:none" class="alert alert-danger"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<p> </p>
|
||||
<p> </p>
|
||||
|
||||
</div>
|
||||
|
||||
{!! Former::close() !!}
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
$(function() {
|
||||
$('select').change(function() {
|
||||
$(this).css({color:'#444444'});
|
||||
});
|
||||
|
||||
$('#country_id').combobox();
|
||||
$('#country').combobox();
|
||||
$('#currency').combobox();
|
||||
$('#first_name').focus();
|
||||
|
||||
@if($paymentType == PAYMENT_TYPE_STRIPE_ACH)
|
||||
var routingNumberCache = {};
|
||||
$('#routing_number, #country').on('change keypress keyup keydown paste', function(){setTimeout(function () {
|
||||
var routingNumber = $('#routing_number').val().replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
|
||||
|
||||
if (routingNumber.length != 9 || $("#country").val() != 'US' || routingNumberCache[routingNumber] === false) {
|
||||
$('#bank_name').hide();
|
||||
} else if (routingNumberCache[routingNumber]) {
|
||||
$('#bank_name').empty().append(routingNumberCache[routingNumber]).show();
|
||||
} else {
|
||||
routingNumberCache[routingNumber] = false;
|
||||
$('#bank_name').hide();
|
||||
$.ajax({
|
||||
url:"{{ URL::to('/bank') }}/" + routingNumber,
|
||||
success:function(data) {
|
||||
var els = $().add(document.createTextNode(data.name)).add('<br>').add(document.createTextNode(data.city + ", " + data.state));
|
||||
routingNumberCache[routingNumber] = els;
|
||||
|
||||
// Still the same number?
|
||||
if (routingNumber == $('#routing_number').val().replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '')) {
|
||||
$('#bank_name').empty().append(els).show();
|
||||
}
|
||||
},
|
||||
error:function(xhr) {
|
||||
if (xhr.status == 404) {
|
||||
var els = $(document.createTextNode('{{trans('texts.unknown_bank')}}'));
|
||||
;
|
||||
routingNumberCache[routingNumber] = els;
|
||||
|
||||
// Still the same number?
|
||||
if (routingNumber == $('#routing_number').val().replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '')) {
|
||||
$('#bank_name').empty().append(els).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
},10)})
|
||||
@endif
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
@stop
|
@ -67,6 +67,14 @@
|
||||
var $form = $(this);
|
||||
|
||||
var data = {
|
||||
@if($paymentType == PAYMENT_TYPE_STRIPE_ACH)
|
||||
account_holder_name: $('#account_holder_name').val(),
|
||||
account_holder_type: $('[name=account_holder_type]:checked').val(),
|
||||
currency: $("#currency").val(),
|
||||
country: $("#country").val(),
|
||||
routing_number: $('#routing_number').val().replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ''),
|
||||
account_number: $('#account_number').val().replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '')
|
||||
@else
|
||||
name: $('#first_name').val() + ' ' + $('#last_name').val(),
|
||||
address_line1: $('#address1').val(),
|
||||
address_line2: $('#address2').val(),
|
||||
@ -78,27 +86,56 @@
|
||||
cvc: $('#cvv').val(),
|
||||
exp_month: $('#expiration_month').val(),
|
||||
exp_year: $('#expiration_year').val()
|
||||
@endif
|
||||
};
|
||||
|
||||
// Validate the card details
|
||||
if (!Stripe.card.validateCardNumber(data.number)) {
|
||||
$('#js-error-message').html('{{ trans('texts.invalid_card_number') }}').fadeIn();
|
||||
return false;
|
||||
}
|
||||
if (!Stripe.card.validateExpiry(data.exp_month, data.exp_year)) {
|
||||
$('#js-error-message').html('{{ trans('texts.invalid_expiry') }}').fadeIn();
|
||||
return false;
|
||||
}
|
||||
if (!Stripe.card.validateCVC(data.cvc)) {
|
||||
$('#js-error-message').html('{{ trans('texts.invalid_cvv') }}').fadeIn();
|
||||
return false;
|
||||
}
|
||||
@if($paymentType == PAYMENT_TYPE_STRIPE_ACH)
|
||||
// Validate the account details
|
||||
if (!data.account_holder_type) {
|
||||
$('#js-error-message').html('{{ trans('texts.missing_account_holder_type') }}').fadeIn();
|
||||
return false;
|
||||
}
|
||||
if (!data.account_holder_name) {
|
||||
$('#js-error-message').html('{{ trans('texts.missing_account_holder_name') }}').fadeIn();
|
||||
return false;
|
||||
}
|
||||
if (!data.routing_number || !Stripe.bankAccount.validateRoutingNumber(data.routing_number, data.country)) {
|
||||
$('#js-error-message').html('{{ trans('texts.invalid_routing_number') }}').fadeIn();
|
||||
return false;
|
||||
}
|
||||
if (data.account_number != $('#confirm_account_number').val().replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '')) {
|
||||
$('#js-error-message').html('{{ trans('texts.account_number_mismatch') }}').fadeIn();
|
||||
return false;
|
||||
}
|
||||
if (!data.account_number || !Stripe.bankAccount.validateAccountNumber(data.account_number, data.country)) {
|
||||
$('#js-error-message').html('{{ trans('texts.invalid_account_number') }}').fadeIn();
|
||||
return false;
|
||||
}
|
||||
@else
|
||||
// Validate the card details
|
||||
if (!Stripe.card.validateCardNumber(data.number)) {
|
||||
$('#js-error-message').html('{{ trans('texts.invalid_card_number') }}').fadeIn();
|
||||
return false;
|
||||
}
|
||||
if (!Stripe.card.validateExpiry(data.exp_month, data.exp_year)) {
|
||||
$('#js-error-message').html('{{ trans('texts.invalid_expiry') }}').fadeIn();
|
||||
return false;
|
||||
}
|
||||
if (!Stripe.card.validateCVC(data.cvc)) {
|
||||
$('#js-error-message').html('{{ trans('texts.invalid_cvv') }}').fadeIn();
|
||||
return false;
|
||||
}
|
||||
@endif
|
||||
|
||||
// Disable the submit button to prevent repeated clicks
|
||||
$form.find('button').prop('disabled', true);
|
||||
$('#js-error-message').hide();
|
||||
|
||||
|
||||
@if($paymentType == PAYMENT_TYPE_STRIPE_ACH)
|
||||
Stripe.bankAccount.createToken(data, stripeResponseHandler);
|
||||
@else
|
||||
Stripe.card.createToken(data, stripeResponseHandler);
|
||||
@endif
|
||||
|
||||
// Prevent the form from submitting with the default action
|
||||
return false;
|
||||
@ -111,6 +148,11 @@
|
||||
if (response.error) {
|
||||
// Show the errors on the form
|
||||
var error = response.error.message;
|
||||
@if($paymentType == PAYMENT_TYPE_STRIPE_ACH)
|
||||
if(response.error.param == 'bank_account[country]') {
|
||||
error = "{{trans('texts.country_not_supported')}}";
|
||||
}
|
||||
@endif
|
||||
$form.find('button').prop('disabled', false);
|
||||
$('#js-error-message').html(error).fadeIn();
|
||||
} else {
|
||||
@ -144,6 +186,21 @@
|
||||
|
||||
@include('payments.payment_css')
|
||||
|
||||
|
||||
@if($paymentType == PAYMENT_TYPE_STRIPE_ACH)
|
||||
{!! Former::vertical_open($url)
|
||||
->autocomplete('on')
|
||||
->addClass('payment-form')
|
||||
->id('payment-form')
|
||||
->rules(array(
|
||||
'first_name' => 'required',
|
||||
'last_name' => 'required',
|
||||
'account_number' => 'required',
|
||||
'routing_number' => 'required',
|
||||
'account_holder_name' => 'required',
|
||||
'account_holder_type' => 'required'
|
||||
)) !!}
|
||||
@else
|
||||
{!! Former::vertical_open($url)
|
||||
->autocomplete('on')
|
||||
->addClass('payment-form')
|
||||
@ -163,6 +220,7 @@
|
||||
'phone' => 'required',
|
||||
'email' => 'required|email'
|
||||
)) !!}
|
||||
@endif
|
||||
|
||||
@if ($client)
|
||||
{{ Former::populate($client) }}
|
||||
@ -170,6 +228,11 @@
|
||||
{{ Former::populateField('last_name', $contact->last_name) }}
|
||||
@if (!$client->country_id && $client->account->country_id)
|
||||
{{ Former::populateField('country_id', $client->account->country_id) }}
|
||||
{{ Former::populateField('country', $client->account->country->iso_3166_2) }}
|
||||
@endif
|
||||
@if (!$client->currency_id && $client->account->currency_id)
|
||||
{{ Former::populateField('currency_id', $client->account->currency_id) }}
|
||||
{{ Former::populateField('currency', $client->account->currency->code) }}
|
||||
@endif
|
||||
@endif
|
||||
|
||||
@ -243,7 +306,10 @@
|
||||
<p> <br/> </p>
|
||||
|
||||
@if ($showAddress)
|
||||
<h3>{{ trans('texts.billing_address') }} <span class="help">{{ trans('texts.payment_footer1') }}</span></h3>
|
||||
<h3>{{ trans('texts.billing_address') }}
|
||||
@if($paymentType != PAYMENT_TYPE_STRIPE_ACH)
|
||||
<span class="help">{{ trans('texts.payment_footer1') }}</span></h3>
|
||||
@endif
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
{!! Former::text('address1')
|
||||
@ -292,113 +358,178 @@
|
||||
@endif
|
||||
|
||||
<h3>{{ trans('texts.billing_method') }}</h3>
|
||||
<div class="row">
|
||||
<div class="col-md-9">
|
||||
@if (!empty($braintreeClientToken))
|
||||
<div id="card_number" class="braintree-hosted form-control"></div>
|
||||
@else
|
||||
{!! Former::text($accountGateway->getPublishableStripeKey() ? '' : 'card_number')
|
||||
->id('card_number')
|
||||
->placeholder(trans('texts.card_number'))
|
||||
->autocomplete('cc-number')
|
||||
->label('') !!}
|
||||
@endif
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
@if (!empty($braintreeClientToken))
|
||||
<div id="cvv" class="braintree-hosted form-control"></div>
|
||||
@else
|
||||
{!! Former::text($accountGateway->getPublishableStripeKey() ? '' : 'cvv')
|
||||
->id('cvv')
|
||||
->placeholder(trans('texts.cvv'))
|
||||
->autocomplete('off')
|
||||
->label('') !!}
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
@if (!empty($braintreeClientToken))
|
||||
<div id="expiration_month" class="braintree-hosted form-control"></div>
|
||||
@else
|
||||
{!! Former::select($accountGateway->getPublishableStripeKey() ? '' : 'expiration_month')
|
||||
->id('expiration_month')
|
||||
->autocomplete('cc-exp-month')
|
||||
->placeholder(trans('texts.expiration_month'))
|
||||
->addOption('01 - January', '1')
|
||||
->addOption('02 - February', '2')
|
||||
->addOption('03 - March', '3')
|
||||
->addOption('04 - April', '4')
|
||||
->addOption('05 - May', '5')
|
||||
->addOption('06 - June', '6')
|
||||
->addOption('07 - July', '7')
|
||||
->addOption('08 - August', '8')
|
||||
->addOption('09 - September', '9')
|
||||
->addOption('10 - October', '10')
|
||||
->addOption('11 - November', '11')
|
||||
->addOption('12 - December', '12')->label('')
|
||||
!!}
|
||||
@endif
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
@if (!empty($braintreeClientToken))
|
||||
<div id="expiration_year" class="braintree-hosted form-control"></div>
|
||||
@else
|
||||
{!! Former::select($accountGateway->getPublishableStripeKey() ? '' : 'expiration_year')
|
||||
->id('expiration_year')
|
||||
->autocomplete('cc-exp-year')
|
||||
->placeholder(trans('texts.expiration_year'))
|
||||
->addOption('2016', '2016')
|
||||
->addOption('2017', '2017')
|
||||
->addOption('2018', '2018')
|
||||
->addOption('2019', '2019')
|
||||
->addOption('2020', '2020')
|
||||
->addOption('2021', '2021')
|
||||
->addOption('2022', '2022')
|
||||
->addOption('2023', '2023')
|
||||
->addOption('2024', '2024')
|
||||
->addOption('2025', '2025')
|
||||
->addOption('2026', '2026')->label('')
|
||||
!!}
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row" style="padding-top:18px">
|
||||
<div class="col-md-5">
|
||||
@if ($client && $account->showTokenCheckbox($storageGateway/* will contain gateway id */))
|
||||
<input id="token_billing" type="checkbox" name="token_billing" {{ $account->selectTokenCheckbox() ? 'CHECKED' : '' }} value="1" style="margin-left:0px; vertical-align:top">
|
||||
<label for="token_billing" class="checkbox" style="display: inline;">{{ trans('texts.token_billing') }}</label>
|
||||
<span class="help-block" style="font-size:15px">
|
||||
@if ($storageGateway == GATEWAY_STRIPE)
|
||||
{!! trans('texts.token_billing_secure', ['link' => link_to('https://stripe.com/', 'Stripe.com', ['target' => '_blank'])]) !!}
|
||||
@elseif ($storageGateway == GATEWAY_BRAINTREE)
|
||||
{!! trans('texts.token_billing_secure', ['link' => link_to('https://www.braintreepayments.com/', 'Braintree', ['target' => '_blank'])]) !!}
|
||||
@endif
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="col-md-7">
|
||||
@if (isset($acceptedCreditCardTypes))
|
||||
<div class="pull-right">
|
||||
@foreach ($acceptedCreditCardTypes as $card)
|
||||
<img src="{{ $card['source'] }}" alt="{{ $card['alt'] }}" style="width: 70px; display: inline; margin-right: 6px;"/>
|
||||
@endforeach
|
||||
@if($paymentType == PAYMENT_TYPE_STRIPE_ACH)
|
||||
<p>{{ trans('texts.ach_verification_delay_help') }}</p>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="radio">
|
||||
{!! Former::radios('account_holder_type')->radios(array(
|
||||
trans('texts.individual_account') => array('value' => 'individual'),
|
||||
trans('texts.company_account') => array('value' => 'company'),
|
||||
))->inline()->label(''); !!}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{!! Former::text('account_holder_name')
|
||||
->placeholder(trans('texts.account_holder_name'))
|
||||
->label('') !!}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
{!! Former::select('country')
|
||||
->placeholder(trans('texts.country_id'))
|
||||
->fromQuery($countries, 'name', 'iso_3166_2')
|
||||
->addGroupClass('country-select')
|
||||
->label('') !!}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{!! Former::select('currency')
|
||||
->placeholder(trans('texts.currency_id'))
|
||||
->fromQuery($currencies, 'name', 'code')
|
||||
->addGroupClass('currency-select')
|
||||
->label('') !!}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
{!! Former::text('')
|
||||
->id('routing_number')
|
||||
->placeholder(trans('texts.routing_number'))
|
||||
->label('') !!}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div id="bank_name"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
{!! Former::text('')
|
||||
->id('account_number')
|
||||
->placeholder(trans('texts.account_number'))
|
||||
->label('') !!}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{!! Former::text('')
|
||||
->id('confirm_account_number')
|
||||
->placeholder(trans('texts.confirm_account_number'))
|
||||
->label('') !!}
|
||||
</div>
|
||||
</div>
|
||||
<center>
|
||||
{!! Button::success(strtoupper(trans('texts.add_account')))
|
||||
->submit()
|
||||
->large() !!}
|
||||
</center>
|
||||
@else
|
||||
<div class="row">
|
||||
<div class="col-md-9">
|
||||
@if (!empty($braintreeClientToken))
|
||||
<div id="card_number" class="braintree-hosted form-control"></div>
|
||||
@else
|
||||
{!! Former::text($accountGateway->getPublishableStripeKey() ? '' : 'card_number')
|
||||
->id('card_number')
|
||||
->placeholder(trans('texts.card_number'))
|
||||
->autocomplete('cc-number')
|
||||
->label('') !!}
|
||||
@endif
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
@if (!empty($braintreeClientToken))
|
||||
<div id="cvv" class="braintree-hosted form-control"></div>
|
||||
@else
|
||||
{!! Former::text($accountGateway->getPublishableStripeKey() ? '' : 'cvv')
|
||||
->id('cvv')
|
||||
->placeholder(trans('texts.cvv'))
|
||||
->autocomplete('off')
|
||||
->label('') !!}
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
@if (!empty($braintreeClientToken))
|
||||
<div id="expiration_month" class="braintree-hosted form-control"></div>
|
||||
@else
|
||||
{!! Former::select($accountGateway->getPublishableStripeKey() ? '' : 'expiration_month')
|
||||
->id('expiration_month')
|
||||
->autocomplete('cc-exp-month')
|
||||
->placeholder(trans('texts.expiration_month'))
|
||||
->addOption('01 - January', '1')
|
||||
->addOption('02 - February', '2')
|
||||
->addOption('03 - March', '3')
|
||||
->addOption('04 - April', '4')
|
||||
->addOption('05 - May', '5')
|
||||
->addOption('06 - June', '6')
|
||||
->addOption('07 - July', '7')
|
||||
->addOption('08 - August', '8')
|
||||
->addOption('09 - September', '9')
|
||||
->addOption('10 - October', '10')
|
||||
->addOption('11 - November', '11')
|
||||
->addOption('12 - December', '12')->label('')
|
||||
!!}
|
||||
@endif
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
@if (!empty($braintreeClientToken))
|
||||
<div id="expiration_year" class="braintree-hosted form-control"></div>
|
||||
@else
|
||||
{!! Former::select($accountGateway->getPublishableStripeKey() ? '' : 'expiration_year')
|
||||
->id('expiration_year')
|
||||
->autocomplete('cc-exp-year')
|
||||
->placeholder(trans('texts.expiration_year'))
|
||||
->addOption('2016', '2016')
|
||||
->addOption('2017', '2017')
|
||||
->addOption('2018', '2018')
|
||||
->addOption('2019', '2019')
|
||||
->addOption('2020', '2020')
|
||||
->addOption('2021', '2021')
|
||||
->addOption('2022', '2022')
|
||||
->addOption('2023', '2023')
|
||||
->addOption('2024', '2024')
|
||||
->addOption('2025', '2025')
|
||||
->addOption('2026', '2026')->label('')
|
||||
!!}
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" style="padding-top:18px">
|
||||
<div class="col-md-5">
|
||||
@if ($client && $account->showTokenCheckbox($storageGateway/* will contain gateway id */))
|
||||
<input id="token_billing" type="checkbox" name="token_billing" {{ $account->selectTokenCheckbox() ? 'CHECKED' : '' }} value="1" style="margin-left:0px; vertical-align:top">
|
||||
<label for="token_billing" class="checkbox" style="display: inline;">{{ trans('texts.token_billing') }}</label>
|
||||
<span class="help-block" style="font-size:15px">
|
||||
@if ($storageGateway == GATEWAY_STRIPE)
|
||||
{!! trans('texts.token_billing_secure', ['link' => link_to('https://stripe.com/', 'Stripe.com', ['target' => '_blank'])]) !!}
|
||||
@elseif ($storageGateway == GATEWAY_BRAINTREE)
|
||||
{!! trans('texts.token_billing_secure', ['link' => link_to('https://www.braintreepayments.com/', 'Braintree', ['target' => '_blank'])]) !!}
|
||||
@endif
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="col-md-7">
|
||||
@if (isset($acceptedCreditCardTypes))
|
||||
<div class="pull-right">
|
||||
@foreach ($acceptedCreditCardTypes as $card)
|
||||
<img src="{{ $card['source'] }}" alt="{{ $card['alt'] }}" style="width: 70px; display: inline; margin-right: 6px;"/>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p> </p>
|
||||
<center>
|
||||
{!! Button::success(strtoupper(trans('texts.pay_now') . ' - ' . $account->formatMoney($amount, $client, true) ))
|
||||
->submit()
|
||||
->large() !!}
|
||||
</center>
|
||||
<p> </p>
|
||||
<p> </p>
|
||||
<center>
|
||||
{!! Button::success(strtoupper(trans('texts.pay_now') . ' - ' . $account->formatMoney($amount, $client, true) ))
|
||||
->submit()
|
||||
->large() !!}
|
||||
</center>
|
||||
<p> </p>
|
||||
@endif
|
||||
|
||||
<div id="js-error-message" style="display:none" class="alert alert-danger"></div>
|
||||
</div>
|
||||
@ -422,7 +553,47 @@
|
||||
});
|
||||
|
||||
$('#country_id').combobox();
|
||||
$('#country').combobox();
|
||||
$('#currency').combobox();
|
||||
$('#first_name').focus();
|
||||
|
||||
var routingNumberCache = {};
|
||||
$('#routing_number, #country').on('change keypress keyup keydown paste', function(){setTimeout(function () {
|
||||
var routingNumber = $('#routing_number').val().replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
|
||||
|
||||
if (routingNumber.length != 9 || $("#country").val() != 'US' || routingNumberCache[routingNumber] === false) {
|
||||
$('#bank_name').hide();
|
||||
} else if (routingNumberCache[routingNumber]) {
|
||||
$('#bank_name').empty().append(routingNumberCache[routingNumber]).show();
|
||||
} else {
|
||||
routingNumberCache[routingNumber] = false;
|
||||
$('#bank_name').hide();
|
||||
$.ajax({
|
||||
url:"{{ URL::to('/bank') }}/" + routingNumber,
|
||||
success:function(data) {
|
||||
var els = $().add(document.createTextNode(data.name)).add('<br>').add(document.createTextNode(data.city + ", " + data.state));
|
||||
routingNumberCache[routingNumber] = els;
|
||||
|
||||
// Still the same number?
|
||||
if (routingNumber == $('#routing_number').val().replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '')) {
|
||||
$('#bank_name').empty().append(els).show();
|
||||
}
|
||||
},
|
||||
error:function(xhr) {
|
||||
if (xhr.status == 404) {
|
||||
var els = $(document.createTextNode('{{trans('texts.unknown_bank')}}'));
|
||||
;
|
||||
routingNumberCache[routingNumber] = els;
|
||||
|
||||
// Still the same number?
|
||||
if (routingNumber == $('#routing_number').val().replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '')) {
|
||||
$('#bank_name').empty().append(els).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
},10)})
|
||||
});
|
||||
|
||||
</script>
|
||||
|
149
resources/views/payments/paymentmethods.blade.php
Normal file
149
resources/views/payments/paymentmethods.blade.php
Normal file
@ -0,0 +1,149 @@
|
||||
@extends('public.header')
|
||||
|
||||
@section('content')
|
||||
<style type="text/css">
|
||||
.payment_method_img_container{
|
||||
width:37px;
|
||||
text-align: center;
|
||||
display:inline-block;
|
||||
margin-right:10px;
|
||||
}
|
||||
|
||||
.payment_method{
|
||||
margin:20px 0;
|
||||
}
|
||||
|
||||
.payment_method_number{
|
||||
margin-right:10px;
|
||||
width:65px;
|
||||
display:inline-block;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="container main-container">
|
||||
|
||||
<h3>{{ $title }}</h3>
|
||||
|
||||
@foreach ($paymentMethods as $paymentMethod)
|
||||
<div class="payment_method">
|
||||
<span class="payment_method_img_container">
|
||||
<img height="22" src="{{URL::to('/images/credit_cards/'.str_replace(' ', '', strtolower($paymentMethod['type']->name).'.png'))}}" alt="{{trans("texts.card_" . str_replace(' ', '', strtolower($paymentMethod['type']->name)))}}">
|
||||
</span>
|
||||
<span class="payment_method_number">•••••{{$paymentMethod['last4']}}</span>
|
||||
|
||||
@if($paymentMethod['type']->id == PAYMENT_TYPE_ACH)
|
||||
{{ $paymentMethod['bank_name'] }}
|
||||
@if($paymentMethod['status'] == 'new')
|
||||
<a href="javasript::void" onclick="completeVerification('{{$paymentMethod['id']}}','{{$paymentMethod['currency']->symbol}}')">({{trans('texts.complete_verification')}})</a>
|
||||
@elseif($paymentMethod['status'] == 'verification_failed')
|
||||
({{trans('texts.verification_failed')}})
|
||||
@endif
|
||||
@else
|
||||
{!! trans('texts.card_expiration', array('expires'=>Utils::fromSqlDate($paymentMethod['expiration'], false)->format('m/y'))) !!}
|
||||
@endif
|
||||
@if($paymentMethod['default'])
|
||||
({{trans('texts.used_for_auto_bill')}})
|
||||
@elseif($paymentMethod['type']->id != PAYMENT_TYPE_ACH || $paymentMethod['status'] == 'verified')
|
||||
<a href="#" onclick="setDefault('{{$paymentMethod['id']}}')">({{trans('texts.use_for_auto_bill')}})</a>
|
||||
@endif
|
||||
<a href="javasript::void" class="payment_method_remove" onclick="removePaymentMethod('{{$paymentMethod['id']}}')">×</a>
|
||||
</div>
|
||||
@endforeach
|
||||
|
||||
<center>
|
||||
{!! Button::success(strtoupper(trans('texts.add_credit_card')))
|
||||
->asLinkTo(URL::to('/client/paymentmethods/add/'.($gateway->getPaymentType() == PAYMENT_TYPE_STRIPE ? 'stripe_credit_card' : 'credit_card'))) !!}
|
||||
@if($gateway->getACHEnabled())
|
||||
|
||||
{!! Button::success(strtoupper(trans('texts.add_bank_account')))
|
||||
->asLinkTo(URL::to('/client/paymentmethods/add/stripe_ach')) !!}
|
||||
@endif
|
||||
</center>
|
||||
<p></p>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="completeVerificationModal" tabindex="-1" role="dialog" aria-labelledby="completeVerificationModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" style="min-width:150px">
|
||||
<div class="modal-content">
|
||||
{!! Former::open('/client/paymentmethods/verify') !!}
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title" id="completeVerificationModalLabel">{{ trans('texts.complete_verification') }}</h4>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<div style="display:none">
|
||||
{!! Former::text('source_id') !!}
|
||||
</div>
|
||||
<p>{{ trans('texts.bank_account_verification_help') }}</p>
|
||||
<div class="form-group">
|
||||
<label for="verification1" class="control-label col-sm-5">{{ trans('texts.verification_amount1') }}</label>
|
||||
<div class="col-sm-3">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><span class="payment_method_currenct_symbol"></span>0.</span>
|
||||
<input type="number" min="0" max="99" required class="form-control" id="verification1" name="verification1">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="verification2" class="control-label col-sm-5">{{ trans('texts.verification_amount2') }}</label>
|
||||
<div class="col-sm-3">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><span class="payment_method_currenct_symbol"></span>0.</span>
|
||||
<input type="number" min="0" max="99" required class="form-control" id="verification2" name="verification2">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer" style="margin-top: 0px">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{ trans('texts.cancel') }}</button>
|
||||
<button type="submit" class="btn btn-primary">{{ trans('texts.complete_verification') }}</button>
|
||||
</div>
|
||||
{!! Former::close() !!}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="removePaymentMethodModal" tabindex="-1" role="dialog" aria-labelledby="removePaymentMethodModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" style="min-width:150px">
|
||||
<div class="modal-content">
|
||||
{!! Former::open()->id('removeForm') !!}
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title" id="removePaymentMethodModalLabel">{{ trans('texts.remove_payment_method') }}</h4>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<p>{{ trans('texts.confirm_remove_payment_method') }}</p>
|
||||
</div>
|
||||
<div class="modal-footer" style="margin-top: 0px">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{ trans('texts.cancel') }}</button>
|
||||
<button type="submit" class="btn btn-primary">{{ trans('texts.remove') }}</button>
|
||||
</div>
|
||||
{!! Former::close() !!}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{!! Former::open(URL::to('/client/paymentmethods/default'))->id('defaultSourceForm') !!}
|
||||
<input type="hidden" name="source" id="default_id">
|
||||
{!! Former::close() !!}
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
function completeVerification(sourceId, currencySymbol) {
|
||||
$('#source_id').val(sourceId);
|
||||
$('.payment_method_currenct_symbol').text(currencySymbol);
|
||||
$('#completeVerificationModal').modal('show');
|
||||
}
|
||||
|
||||
function removePaymentMethod(sourceId) {
|
||||
$('#removeForm').attr('action', '{{ URL::to('/client/paymentmethods/%s/remove') }}'.replace('%s', sourceId))
|
||||
$('#removePaymentMethodModal').modal('show');
|
||||
}
|
||||
|
||||
function setDefault(sourceId) {
|
||||
$('#default_id').val(sourceId);
|
||||
$('#defaultSourceForm').submit()
|
||||
}
|
||||
</script>
|
||||
@stop
|
@ -7,9 +7,7 @@
|
||||
<link href="//fonts.googleapis.com/css?family=Roboto:400,700,900,100" rel="stylesheet" type="text/css">
|
||||
@endif
|
||||
<link href="{{ asset('css/built.public.css') }}?no_cache={{ NINJA_VERSION }}" rel="stylesheet" type="text/css"/>
|
||||
@if (!empty($clientViewCSS))
|
||||
<style type="text/css">{!! $clientViewCSS !!}</style>
|
||||
@endif
|
||||
<style type="text/css">{!! isset($account)?$account->clientViewCSS():'' !!}</style>
|
||||
@stop
|
||||
|
||||
@section('body')
|
||||
@ -68,15 +66,15 @@
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
@if (!isset($hideLogo) || !$hideLogo)
|
||||
@if (!isset($account) || !$account->hasFeature(FEATURE_WHITE_LABEL))
|
||||
{{-- Per our license, please do not remove or modify this link. --}}
|
||||
<a class="navbar-brand" href="{{ URL::to(NINJA_WEB_URL) }}" target="_blank"><img src="{{ asset('images/invoiceninja-logo.png') }}" style="height:20px"></a>
|
||||
@endif
|
||||
</div>
|
||||
<div id="navbar" class="collapse navbar-collapse">
|
||||
@if (!isset($hideHeader) || !$hideHeader)
|
||||
@if (!isset($account) || $account->isNinjaAccount() || $account->enable_client_portal)
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
@if (!isset($hideDashboard) || !$hideDashboard)
|
||||
@if (!isset($account) || $account->enable_client_portal_dashboard)
|
||||
<li {{ Request::is('*client/dashboard') ? 'class="active"' : '' }}>
|
||||
{!! link_to('/client/dashboard', trans('texts.dashboard') ) !!}
|
||||
</li>
|
||||
@ -87,11 +85,16 @@
|
||||
<li {{ Request::is('*client/invoices') ? 'class="active"' : '' }}>
|
||||
{!! link_to('/client/invoices', trans('texts.invoices') ) !!}
|
||||
</li>
|
||||
@if (!empty($showDocuments))
|
||||
@if ($account->hasFeature(FEATURE_DOCUMENTS))
|
||||
<li {{ Request::is('*client/documents') ? 'class="active"' : '' }}>
|
||||
{!! link_to('/client/documents', trans('texts.documents') ) !!}
|
||||
</li>
|
||||
@endif
|
||||
@if ($account->getTokenGatewayId())
|
||||
<li {{ Request::is('*client/paymentmethods') ? 'class="active"' : '' }}>
|
||||
{!! link_to('/client/paymentmethods', trans('texts.payment_methods') ) !!}
|
||||
</li>
|
||||
@endif
|
||||
<li {{ Request::is('*client/payments') ? 'class="active"' : '' }}>
|
||||
{!! link_to('/client/payments', trans('texts.payments') ) !!}
|
||||
</li>
|
||||
@ -123,7 +126,7 @@
|
||||
<footer id="footer" role="contentinfo">
|
||||
<div class="top">
|
||||
<div class="wrap">
|
||||
@if (!isset($hideLogo) || !$hideLogo)
|
||||
@if (!isset($account) || !$account->hasFeature(FEATURE_WHITE_LABEL))
|
||||
<div id="footer-menu" class="menu-wrap">
|
||||
<ul id="menu-footer-menu" class="menu">
|
||||
<li id="menu-item-31" class="menu-item-31">
|
||||
@ -146,7 +149,7 @@
|
||||
|
||||
<div class="bottom">
|
||||
<div class="wrap">
|
||||
@if (!isset($hideLogo) || !$hideLogo)
|
||||
@if (!isset($account) || !$account->hasFeature(FEATURE_WHITE_LABEL))
|
||||
<div class="copy">Copyright ©{{ date('Y') }} <a href="{{ NINJA_WEB_URL }}" target="_blank">Invoice Ninja</a>. All rights reserved.</div>
|
||||
@endif
|
||||
</div><!-- .wrap -->
|
||||
|
Loading…
Reference in New Issue
Block a user