1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-09-19 16:01:34 +02:00

Working on buy now buttons

This commit is contained in:
Hillel Coren 2016-07-12 23:46:41 +03:00
parent e68e213237
commit 401851e212
10 changed files with 234 additions and 34 deletions

View File

@ -2,7 +2,12 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## [2.6] - 2016-07-08
## [Unreleased]
### Added
- Added 'Buy Now' buttons
## [2.6] - 2016-07-12
### Added
- Configuration for first day of the week #950

View File

@ -25,6 +25,7 @@ use App\Models\Document;
use App\Models\Gateway;
use App\Models\InvoiceDesign;
use App\Models\TaxRate;
use App\Models\Product;
use App\Models\PaymentTerm;
use App\Ninja\Repositories\AccountRepository;
use App\Ninja\Repositories\ReferralRepository;
@ -714,6 +715,14 @@ class AccountController extends BaseController
);
}
$types = [GATEWAY_TYPE_CREDIT_CARD, GATEWAY_TYPE_BANK_TRANSFER, GATEWAY_TYPE_PAYPAL, GATEWAY_TYPE_BITCOIN, GATEWAY_TYPE_DWOLLA];
$options = [];
foreach ($types as $type) {
if ($account->getGatewayByType($type)) {
$options[$type] = trans("texts.{$type}");
}
}
$data = [
'client_view_css' => $css,
'enable_portal_password' => $account->enable_portal_password,
@ -721,6 +730,8 @@ class AccountController extends BaseController
'title' => trans('texts.client_portal'),
'section' => ACCOUNT_CLIENT_PORTAL,
'account' => $account,
'products' => Product::scope()->orderBy('product_key')->get(),
'gateway_types' => $options,
];
return View::make('accounts.client_portal', $data);

View File

@ -4,14 +4,20 @@ use Session;
use Input;
use Utils;
use View;
use Auth;
use URL;
use Exception;
use Validator;
use App\Models\Invitation;
use App\Models\Account;
use App\Models\Payment;
use App\Models\Product;
use App\Models\PaymentMethod;
use App\Services\PaymentService;
use App\Ninja\Mailers\UserMailer;
use App\Http\Requests\CreateOnlinePaymentRequest;
use App\Ninja\Repositories\ClientRepository;
use App\Services\InvoiceService;
/**
* Class OnlinePaymentController
@ -203,4 +209,57 @@ class OnlinePaymentController extends BaseController
}
}
public function handleBuyNow(ClientRepository $clientRepo, InvoiceService $invoiceService, $gatewayType = false)
{
$account = Account::whereAccountKey(Input::get('account_key'))->first();
$redirectUrl = Input::get('redirect_url', URL::previous());
if ( ! $account) {
return redirect()->to("{$redirectUrl}/?error=invalid account");
}
Auth::onceUsingId($account->users[0]->id);
$product = Product::scope(Input::get('product_id'))->first();
if ( ! $product) {
return redirect()->to("{$redirectUrl}/?error=invalid product");
}
$rules = [
'first_name' => 'string|max:100',
'last_name' => 'string|max:100',
'email' => 'email|string|max:100',
];
$validator = Validator::make(Input::all(), $rules);
if ($validator->fails()) {
return redirect()->to("{$redirectUrl}/?error=" . $validator->errors()->first());
}
$data = [
'contact' => Input::all()
];
$client = $clientRepo->save($data);
$data = [
'client_id' => $client->public_id,
'invoice_items' => [[
'product_key' => $product->product_key,
'notes' => $product->notes,
'cost' => $product->cost,
'qty' => 1,
'tax_rate1' => $product->default_tax_rate ? $product->default_tax_rate->rate : 0,
'tax_name1' => $product->default_tax_rate ? $product->default_tax_rate->name : '',
]]
];
$invoice = $invoiceService->save($data);
$invitation = $invoice->invitations[0];
$link = $invitation->getLink();
if ($gatewayType) {
return redirect()->to($invitation->getLink('payment') . "/{$gatewayType}");
} else {
return redirect()->to($invitation->getLink());
}
}
}

View File

@ -30,6 +30,7 @@ class VerifyCsrfToken extends BaseVerifier
'hook/email_bounced',
'reseller_stats',
'payment_hook/*',
'buy_now/*',
];
/**
@ -42,7 +43,6 @@ class VerifyCsrfToken extends BaseVerifier
public function handle($request, Closure $next)
{
foreach ($this->openRoutes as $route) {
if ($request->is($route)) {
return $next($request);
}

View File

@ -81,6 +81,7 @@ Route::post('signup/submit', 'AccountController@submitSignup');
Route::get('/auth/{provider}', 'Auth\AuthController@authLogin');
Route::get('/auth_unlink', 'Auth\AuthController@authUnlink');
Route::match(['GET', 'POST'], '/buy_now/{gateway_type?}', 'OnlinePaymentController@handleBuyNow');
Route::post('/hook/email_bounced', 'AppController@emailBounced');
Route::post('/hook/email_opened', 'AppController@emailOpened');

View File

@ -227,7 +227,7 @@ class BasePaymentDriver
$gateway = $this->gateway();
if ($input) {
$this->updateAddress();
$this->updateClient();
}
// load or create token
@ -280,8 +280,13 @@ class BasePaymentDriver
}
}
private function updateAddress()
private function updateClient()
{
if ( ! $this->contact()->email && $this->input['email']) {
$this->contact()->email = $this->input['email'];
$this->contact()->save();
}
if ( ! $this->isGatewayType(GATEWAY_TYPE_CREDIT_CARD)) {
return;
}

View File

@ -12,21 +12,6 @@ use App\Ninja\Datatables\PaymentDatatable;
class PaymentService extends BaseService
{
/**
* @var DatatableService
*/
protected $datatableService;
/**
* @var PaymentRepository
*/
protected $paymentRepo;
/**
* @var AccountRepository
*/
protected $accountRepo;
/**
* PaymentService constructor.
*
@ -157,5 +142,4 @@ class PaymentService extends BaseService
return parent::bulk($ids, $action);
}
}
}

View File

@ -2027,7 +2027,14 @@ $LANG = array(
'restored_expense_category' => 'Successfully restored expense category',
'apply_taxes' => 'Apply taxes',
'min_to_max_users' => ':min to :max users',
'max_users_reached' => 'The maximum number of users has been reached.'
'max_users_reached' => 'The maximum number of users has been reached.',
'buy_now_buttons' => 'Buy Now Buttons',
'landing_page' => 'Landing Page',
'payment_type' => 'Payment Type',
'form' => 'Form',
'link' => 'Link',
'fields' => 'Fields',
'buy_now_buttons_warning' => 'Note: client and invoice records are created even if the transaction isn\'t completed.',
);

View File

@ -3,7 +3,16 @@
@section('head')
@parent
<link href='https://fonts.googleapis.com/css?family=Roboto+Mono' rel='stylesheet' type='text/css'>
@include('money_script')
<link href='https://fonts.googleapis.com/css?family=Roboto+Mono' rel='stylesheet' type='text/css'>
<style>
.checkbox-inline input[type="checkbox"] {
margin-left:-20px !important;
}
</style>
@stop
@section('content')
@ -69,6 +78,65 @@
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{!! trans('texts.buy_now_buttons') !!}</h3>
</div>
<div class="panel-body">
<div class="col-md-10 col-md-offset-1">
{!! Former::select('product')
->onchange('updateBuyNowButtons()')
->addOption('', '')
->inlineHelp('buy_now_buttons_warning')
->addGroupClass('product-select') !!}
{!! Former::inline_checkboxes('client_fields')
->onchange('updateBuyNowButtons()')
->checkboxes([
trans('texts.first_name') => ['value' => 'first_name', 'name' => 'first_name'],
trans('texts.last_name') => ['value' => 'last_name', 'name' => 'last_name'],
trans('texts.email') => ['value' => 'email', 'name' => 'email'],
]) !!}
{!! Former::inline_radios('landing_page')
->onchange('showPaymentTypes();updateBuyNowButtons();')
->radios([
trans('texts.invoice') => ['value' => 'invoice', 'name' => 'landing_page_type'],
trans('texts.payment') => ['value' => 'payment', 'name' => 'landing_page_type'],
])->check('invoice') !!}
<div id="paymentTypesDiv" style="display:none">
{!! Former::select('payment_type')
->onchange('updateBuyNowButtons()')
->options($gateway_types) !!}
</div>
<p>&nbsp;</p>
<div role="tabpanel">
<ul class="nav nav-tabs" role="tablist" style="border: none">
<li role="presentation" class="active">
<a href="#form" aria-controls="form" role="tab" data-toggle="tab">{{ trans('texts.form') }}</a>
</li>
<li role="presentation">
<a href="#link" aria-controls="link" role="tab" data-toggle="tab">{{ trans('texts.link') }}</a>
</li>
</ul>
</div>
<div class="tab-content">
<div role="tabpanel" class="tab-pane active" id="form">
<textarea id="formTextarea" class="form-control" rows="4" readonly></textarea>
</div>
<div role="tabpanel" class="tab-pane" id="link">
<textarea id="linkTextarea" class="form-control" rows="4" readonly></textarea>
</div>
</div>
</div>
</div>
</div>
@if (Utils::hasFeature(FEATURE_CLIENT_PORTAL_CSS))
<div class="panel panel-default">
<div class="panel-heading">
@ -95,12 +163,74 @@
</center>
{!! Former::close() !!}
<script>
var products = {!! $products !!};
console.log(products);
$(function() {
var $productSelect = $('select#product');
for (var i=0; i<products.length; i++) {
var product = products[i];
$productSelect.append(new Option(formatMoney(product.cost) + ' - ' + product.product_key, product.public_id));
}
$productSelect.combobox();
fixCheckboxes();
updateBuyNowButtons();
})
$('#enable_portal_password').change(fixCheckboxes);
function fixCheckboxes(){
function fixCheckboxes() {
var checked = $('#enable_portal_password').is(':checked');
$('#send_portal_password').prop('disabled', !checked);
}
fixCheckboxes();
function showPaymentTypes() {
var val = $('input[name=landing_page_type]:checked').val()
if (val == '{{ ENTITY_PAYMENT }}') {
$('#paymentTypesDiv').fadeIn();
} else {
$('#paymentTypesDiv').hide();
}
}
function updateBuyNowButtons() {
var productId = $('#product').val();
var landingPage = $('input[name=landing_page_type]:checked').val()
var paymentType = landingPage == 'payment' ? '/' + $('#payment_type').val() : '';
var form = '';
var link = '';
if (productId) {
var link = '{{ url('/buy_now') }}' + paymentType +
'?account_key={{ $account->account_key }}' +
'&product_id=' + productId;
var form = '<form action="{{ url('/buy_now') }}' + paymentType + '" method="post" target="_top">' + "\n" +
'<input type="hidden" name="account_key" value="{{ $account->account_key }}"/>' + "\n" +
'<input type="hidden" name="product_id" value="' + productId + '"/>' + "\n";
@foreach (['first_name', 'last_name', 'email'] as $field)
if ($('input#{{ $field }}').is(':checked')) {
form += '<input type="{{ $field == 'email' ? 'email' : 'text' }}" name="{{ $field }}" placeholder="{{ trans("texts.{$field}") }}" required/>' + "\n";
link += '&{{ $field }}=';
}
@endforeach
form += '<input type="submit" value="Buy Now" name="submit"/>' + "\n" + '</form>';
}
$('#formTextarea').text(form);
$('#linkTextarea').text(link);
}
</script>
@stop

View File

@ -77,16 +77,14 @@
</div>
</div>
@if (isset($paymentTitle) || ! empty($contact->email))
<div class="row" style="display:{{ isset($paymentTitle) ? 'block' : 'none' }}">
<div class="col-md-12">
{!! Former::text('email')
->placeholder(trans('texts.email'))
->autocomplete('email')
->label('') !!}
</div>
<div class="row" style="display:{{ isset($paymentTitle) || empty($contact->email) ? 'block' : 'none' }}">
<div class="col-md-12">
{!! Former::text('email')
->placeholder(trans('texts.email'))
->autocomplete('email')
->label('') !!}
</div>
@endif
</div>
<p>&nbsp;<br/>&nbsp;</p>
@ -257,5 +255,5 @@
<p>&nbsp;</p>
{!! Former::close() !!}
@stop