1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-08 20:22:42 +01:00

Added support for Stripe.js

This commit is contained in:
Hillel Coren 2015-11-29 22:13:50 +02:00
parent 30631b2bc4
commit 165ecd0bf0
24 changed files with 288 additions and 174 deletions

View File

@ -212,13 +212,8 @@ class AccountController extends BaseController
{
// check that logo is less than the max file size
$account = Auth::user()->account;
if ($account->hasLogo()) {
$filename = $account->getLogoPath();
$bytes = File::size($filename);
if ($bytes > MAX_LOGO_FILE_SIZE * 1000) {
$bytes /= 1000;
Session::flash('warning', trans('texts.logo_too_large', ['size' => round($bytes) . 'KB']));
}
if ($account->isLogoTooLarge()) {
Session::flash('warning', trans('texts.logo_too_large', ['size' => $account->getLogoSize() . 'KB']));
}
$data = [
@ -272,6 +267,12 @@ class AccountController extends BaseController
$account->load('account_gateways');
$count = count($account->account_gateways);
if ($accountGateway = $account->getGatewayConfig(GATEWAY_STRIPE)) {
if ( ! $accountGateway->getPublishableStripeKey()) {
Session::flash('warning', trans('texts.missing_publishable_key'));
}
}
if ($count == 0) {
return Redirect::to('gateways/create');
} else {

View File

@ -236,6 +236,13 @@ class AccountGatewayController extends BaseController
}
}
$publishableKey = Input::get('publishable_key');
if ($publishableKey = str_replace('*', '', $publishableKey)) {
$config->publishableKey = $publishableKey;
} elseif ($oldConfig && property_exists($oldConfig, 'publishableKey')) {
$config->publishableKey = $oldConfig->publishableKey;
}
$cardCount = 0;
if ($creditcards) {
foreach ($creditcards as $card => $value) {

View File

@ -159,6 +159,7 @@ class PaymentController extends BaseController
'client' => $client,
'contact' => $invitation->contact,
'gateway' => $gateway,
'accountGateway' => $accountGateway,
'acceptedCreditCardTypes' => $acceptedCreditCardTypes,
'countries' => Cache::get('countries'),
'currencyId' => $client->getCurrencyId(),
@ -340,12 +341,20 @@ class PaymentController extends BaseController
$rules = [
'first_name' => 'required',
'last_name' => 'required',
'card_number' => 'required',
'expiration_month' => 'required',
'expiration_year' => 'required',
'cvv' => 'required',
];
if ( ! Input::get('stripeToken')) {
$rules = array_merge(
$rules,
[
'card_number' => 'required',
'expiration_month' => 'required',
'expiration_year' => 'required',
'cvv' => 'required',
]
);
}
if ($accountGateway->show_address) {
$rules = array_merge($rules, [
'address1' => 'required',
@ -390,6 +399,11 @@ class PaymentController extends BaseController
// check if we're creating/using a billing token
if ($accountGateway->gateway_id == GATEWAY_STRIPE) {
if ($token = Input::get('stripeToken')) {
$details['token'] = $token;
unset($details['card']);
}
if ($useToken) {
$details['customerReference'] = $client->getGatewayToken();
} elseif ($account->token_billing_type_id == TOKEN_BILLING_ALWAYS || Input::get('token_billing')) {

View File

@ -45,6 +45,14 @@ class HandleUserLoggedIn {
Session::put(SESSION_USER_ACCOUNTS, $users);
$account->loadLocalizationSettings();
// if they're using Stripe make sure they're using Stripe.js
$accountGateway = $account->getGatewayConfig(GATEWAY_STRIPE);
if ($accountGateway && ! $accountGateway->getPublishableStripeKey()) {
Session::flash('warning', trans('texts.missing_publishable_key'));
} elseif ($account->isLogoTooLarge()) {
Session::flash('warning', trans('texts.logo_too_large', ['size' => $account->getLogoSize() . 'KB']));
}
}
}

View File

@ -6,6 +6,7 @@ use Session;
use DateTime;
use Event;
use App;
use File;
use App\Events\UserSettingsChanged;
use Illuminate\Database\Eloquent\SoftDeletes;
use Laracasts\Presenter\PresentableTrait;
@ -581,6 +582,21 @@ class Account extends Eloquent
}
}
public function getLogoSize()
{
if (!$this->hasLogo()) {
return 0;
}
$filename = $this->getLogoPath();
return round(File::size($filename) / 1000);
}
public function isLogoTooLarge()
{
return $this->getLogoSize() > MAX_LOGO_FILE_SIZE;
}
public function getSubscription($eventId)
{
return Subscription::where('account_id', '=', $this->id)->where('event_id', '=', $eventId)->first();

View File

@ -57,5 +57,25 @@ class AccountGateway extends EntityModel
{
return json_decode(Crypt::decrypt($this->config));
}
public function getConfigField($field)
{
$config = $this->getConfig();
if (!$field || !property_exists($config, $field)) {
return false;
}
return $config->$field;
}
public function getPublishableStripeKey()
{
if ( ! $this->isGateway(GATEWAY_STRIPE)) {
return false;
}
return $this->getConfigField('publishableKey');
}
}

View File

@ -44,7 +44,9 @@ class PaymentService extends BaseService
}
$function = "set".ucfirst($key);
$gateway->$function($val);
if (method_exists($gateway, $function)) {
$gateway->$function($val);
}
}
if ($accountGateway->isGateway(GATEWAY_DWOLLA)) {
@ -100,10 +102,10 @@ class PaymentService extends BaseService
$data = [
'firstName' => $input['first_name'],
'lastName' => $input['last_name'],
'number' => $input['card_number'],
'expiryMonth' => $input['expiration_month'],
'expiryYear' => $input['expiration_year'],
'cvv' => $input['cvv'],
'number' => isset($input['card_number']) ? $input['card_number'] : null,
'expiryMonth' => isset($input['expiration_month']) ? $input['expiration_month'] : null,
'expiryYear' => isset($input['expiration_year']) ? $input['expiration_year'] : null,
'cvv' => isset($input['cvv']) ? $input['cvv'] : '',
];
if (isset($input['country_id'])) {

View File

@ -447,7 +447,6 @@
'gateway_help_1' => ':link til at registrere dig hos Authorize.net.',
'gateway_help_2' => ':link til at registrere dig hos Authorize.net.',
'gateway_help_17' => ':link til at hente din PayPal API signatur.',
'gateway_help_23' => 'Note: brug din hemmelige API nøgle, IKKE din publicerede API nøgle.',
'gateway_help_27' => ':link til at registrere dig hos TwoCheckout.',
'more_designs' => 'Flere designs',

View File

@ -448,7 +448,6 @@ return array(
'gateway_help_1' => ':link um sich bei Authorize.net anzumelden.',
'gateway_help_2' => ':link um sich bei Authorize.net anzumelden.',
'gateway_help_17' => ':link um deine PayPal API-Signatur zu erhalten.',
'gateway_help_23' => 'Anmerkung: benutze deinen secret API key, nicht deinen publishable API key.',
'gateway_help_27' => ':link um sich bei TwoCheckout anzumelden.',
'more_designs' => 'Weitere Designs',

View File

@ -450,7 +450,6 @@ return array(
'gateway_help_1' => ':link to sign up for Authorize.net.',
'gateway_help_2' => ':link to sign up for Authorize.net.',
'gateway_help_17' => ':link to get your PayPal API signature.',
'gateway_help_23' => 'Note: use your secret API key, not your publishable API key.',
'gateway_help_27' => ':link to sign up for TwoCheckout.',
'more_designs' => 'More designs',
@ -921,7 +920,7 @@ return array(
'country' => 'Country',
'include' => 'Include',
'logo_too_large' => 'Your logo is :size, for better performance we suggest uploading an image file less than 200KB',
'logo_too_large' => 'Your logo is :size, for better PDF performance we suggest uploading an image file less than 200KB',
'import_freshbooks' => 'Import From FreshBooks',
'import_data' => 'Import Data',
'source' => 'Source',
@ -950,4 +949,9 @@ return array(
'invoices_will_create' => 'invoices will be created',
'failed_to_import' => 'The following records failed to import',
'publishable_key' => 'Publishable Key',
'secret_key' => 'Secret Key',
'missing_publishable_key' => 'Set your Stripe publishable key for an improved checkout process',
);

View File

@ -420,7 +420,6 @@ return array(
'gateway_help_1' => ':link para registrarse con Authorize.net.',
'gateway_help_2' => ':link para registrarse con Authorize.net.',
'gateway_help_17' => ':link para obtener su firma del API de PayPal.',
'gateway_help_23' => 'Nota: use use llave secreta del API, no la llave pública.',
'gateway_help_27' => ':link para registrarse con TwoCheckout.',
'more_designs' => 'Más diseños',

View File

@ -438,7 +438,6 @@ return array(
'gateway_help_1' => ':link para registrarse en Authorize.net.',
'gateway_help_2' => ':link para registrarse en Authorize.net.',
'gateway_help_17' => ':link para obtener su firma API de PayPal.',
'gateway_help_23' => 'Nota: utilizar su clave de API secreta, no es su clave de API publica.',
'gateway_help_27' => ':link para registrarse en TwoCheckout.',
'more_designs' => 'Más diseños',

View File

@ -441,7 +441,6 @@ return array(
'gateway_help_1' => ':link to sign up for Authorize.net.',
'gateway_help_2' => ':link to sign up for Authorize.net.',
'gateway_help_17' => ':link pour obtenir votre signature PayPal API.',
'gateway_help_23' => 'Note: uutilisez votre Secret API et non votre clé publiable.',
'gateway_help_27' => ':link pour vous enregistrer sur TwoCheckout.',
'more_designs' => 'Plus de modèles',

View File

@ -441,7 +441,6 @@ return array(
'gateway_help_1' => ':link to sign up for Authorize.net.',
'gateway_help_2' => ':link to sign up for Authorize.net.',
'gateway_help_17' => ':link to get your PayPal API signature.',
'gateway_help_23' => 'Note: use your secret API key, not your publishable API key.',
'gateway_help_27' => ':link to sign up for TwoCheckout.',
'more_designs' => 'Plus de modèles',

View File

@ -441,7 +441,6 @@ return array(
'gateway_help_1' => ':link to sign up for Authorize.net.',
'gateway_help_2' => ':link to sign up for Authorize.net.',
'gateway_help_17' => ':link to get your PayPal API signature.',
'gateway_help_23' => 'Note: use your secret API key, not your publishable API key.',
'gateway_help_27' => ':link to sign up for TwoCheckout.',
'more_designs' => 'More designs',

View File

@ -449,7 +449,6 @@ return array(
'gateway_help_1' => ':link to sign up for Authorize.net.',
'gateway_help_2' => ':link to sign up for Authorize.net.',
'gateway_help_17' => ':link to get your PayPal API signature.',
'gateway_help_23' => 'Note: use your secret API key, not your publishable API key.',
'gateway_help_27' => ':link to sign up for TwoCheckout.',
'more_designs' => 'More designs',

View File

@ -447,7 +447,6 @@ return array(
'gateway_help_1' => ':link for å lage en konto for Authorize.net.',
'gateway_help_2' => ':link for å lage en konto for Authorize.net.',
'gateway_help_17' => ':link for å få din PayPal API signatur.',
'gateway_help_23' => 'Info: bruk din hemmelige API nøkkel, ikke din offentlige API nøkkel.',
'gateway_help_27' => ':link for å lage en konto for TwoCheckout.',
'more_designs' => 'Flere design',

View File

@ -443,7 +443,6 @@ return array(
'gateway_help_1' => ':link om in te schrijven voor Authorize.net.',
'gateway_help_2' => ':link om in te schrijven voor Authorize.net.',
'gateway_help_17' => ':link om uw PayPal API signature te krijgen.',
'gateway_help_23' => 'Opmerking: gebruik uw gehieme API key, niet uw publiceerbare API key.',
'gateway_help_27' => ':link om in te schrijven voor TwoCheckout.',
'more_designs' => 'Meer ontwerpen',

View File

@ -441,7 +441,6 @@ return array(
'gateway_help_1' => ':link para acessar Authorize.net.',
'gateway_help_2' => ':link para acessar Authorize.net.',
'gateway_help_17' => ':link para adquirir sua "PayPal API signature".',
'gateway_help_23' => 'Aviso: use sua "Secret API Key", não a "Publishable API Key".',
'gateway_help_27' => ':link para acessar TwoCheckout.',
'more_designs' => 'Mais Modelos',

View File

@ -447,7 +447,6 @@ return array(
'gateway_help_1' => ':link för att registrera dig på Authorize.net.',
'gateway_help_2' => ':link för att registrera dig på Authorize.net.',
'gateway_help_17' => ':link för att hämta din PayPal API-nyckel.',
'gateway_help_23' => 'Observera: använd din hemliga API-nyckel, inte den publika.',
'gateway_help_27' => ':link för att registrera dig för TwoCheckout.',
'more_designs' => 'Fler fakturalayouter',

View File

@ -19,8 +19,9 @@
{!! Former::populateField('gateway_id', $accountGateway->gateway_id) !!}
{!! Former::populateField('payment_type_id', $paymentTypeId) !!}
{!! Former::populateField('recommendedGateway_id', $accountGateway->gateway_id) !!}
{!! Former::populateField('show_address', intval($accountGateway->show_address)) !!}
{!! Former::populateField('show_address', intval($accountGateway->show_address)) !!}
{!! Former::populateField('update_address', intval($accountGateway->update_address)) !!}
{!! Former::populateField('publishable_key', $accountGateway->getPublishableStripeKey() ? str_repeat('*', strlen($accountGateway->getPublishableStripeKey())) : '') !!}
@if ($config)
@foreach ($accountGateway->fields as $field => $junk)
@ -63,7 +64,7 @@
@elseif ($field == 'username' || $field == 'password')
{!! Former::text($gateway->id.'_'.$field)->label('API '. ucfirst(Utils::toSpaceCase($field))) !!}
@else
{!! Former::text($gateway->id.'_'.$field)->label(Utils::toSpaceCase($field)) !!}
{!! Former::text($gateway->id.'_'.$field)->label($gateway->id == GATEWAY_STRIPE ? trans('texts.secret_key') : Utils::toSpaceCase($field)) !!}
@endif
@endforeach
@ -78,6 +79,8 @@
@endif
@if ($gateway->id == GATEWAY_STRIPE)
{!! Former::text('publishable_key') !!}
{!! Former::select('token_billing_type_id')
->options($tokenBillingOptions)
->help(trans('texts.token_billing_help')) !!}

View File

@ -322,7 +322,7 @@
if (!loadedTabs.hasOwnProperty(target)) {
loadedTabs[target] = true;
window['load_' + target]();
if (target == 'invoices') {
if (target == 'invoices' && window.hasOwnProperty('load_recurring_invoices')) {
window['load_recurring_invoices']();
}
}

View File

@ -3,147 +3,66 @@
@section('head')
@parent
<!--
<script type="text/javascript" src="https://js.stripe.com/v2/"></script>
-->
@if ($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);
// Disable the submit button to prevent repeated clicks
$form.find('button').prop('disabled', true);
Stripe.card.createToken($form, stripeResponseHandler);
// 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;
$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')
<style type="text/css">
body {
background-color: #f8f8f8;
color: #1b1a1a;
}
.panel-body {
padding-bottom: 50px;
}
.container input[type=text],
.container input[type=email],
.container select {
font-weight: 300;
font-family: 'Roboto', sans-serif;
width: 100%;
padding: 11px;
color: #8c8c8c;
background: #f9f9f9;
border: 1px solid #ebe7e7;
border-radius: 3px;
font-size: 16px;
min-height: 42px !important;
font-weight: 400;
}
div.col-md-3,
div.col-md-5,
div.col-md-6,
div.col-md-7,
div.col-md-9,
div.col-md-12 {
margin: 6px 0 6px 0;
}
span.dropdown-toggle {
border-color: #ebe7e7;
}
.dropdown-toggle {
margin: 0px !important;
}
.container input[placeholder],
.container select[placeholder] {
color: #444444;
}
div.row {
padding-top: 8px;
}
header {
margin: 0px !important
}
@media screen and (min-width: 700px) {
header {
margin: 20px 0 75px;
float: left;
}
.panel-body {
padding-left: 150px;
padding-right: 150px;
}
}
h2 {
font-weight: 300;
font-size: 30px;
color: #2e2b2b;
line-height: 1;
}
h3 {
font-weight: 900;
margin-top: 10px;
font-size: 15px;
}
h3 .help {
font-style: italic;
font-weight: normal;
color: #888888;
}
header h3 {
text-transform: uppercase;
}
header h3 span {
display: inline-block;
margin-left: 8px;
}
header h3 em {
font-style: normal;
color: #eb8039;
}
.secure {
text-align: right;
float: right;
background: url({{ asset('/images/icon-shield.png') }}) right 22px no-repeat;
padding: 17px 55px 10px 0;
}
.secure h3 {
color: #36b855;
font-size: 30px;
margin-bottom: 8px;
margin-top: 0px;
}
.secure div {
color: #acacac;
font-size: 15px;
font-weight: 900;
text-transform: uppercase;
}
</style>
@include('payments.payment_css')
{!! Former::vertical_open($url)
->autocomplete('on')
->addClass('payment-form')
->rules(array(
'first_name' => 'required',
'last_name' => 'required',
@ -172,6 +91,8 @@ header h3 em {
<div class="container">
<p>&nbsp;</p>
<div id="js-error-message" style="display:none" class="alert alert-danger"></div>
<div class="panel panel-default">
<div class="panel-body">
@ -279,14 +200,14 @@ header h3 em {
<h3>{{ trans('texts.billing_method') }}</h3>
<div class="row">
<div class="col-md-9">
{!! Former::text($gateway->isGateway(GATEWAY_STRIPE) ? 'card_number' : 'card_number')
{!! Former::text($accountGateway->getPublishableStripeKey() ? '' : 'card_number')
->placeholder(trans('texts.card_number'))
->autocomplete('cc-number')
->data_stripe('number')
->label('') !!}
</div>
<div class="col-md-3">
{!! Former::text($gateway->isGateway(GATEWAY_STRIPE) ? 'cvv' : 'cvv')
{!! Former::text($accountGateway->getPublishableStripeKey() ? '' : 'cvv')
->placeholder(trans('texts.cvv'))
->autocomplete('off')
->data_stripe('cvc')
@ -295,7 +216,7 @@ header h3 em {
</div>
<div class="row">
<div class="col-md-6">
{!! Former::select($gateway->isGateway(GATEWAY_STRIPE) ? 'expiration_month' : 'expiration_month')
{!! Former::select($accountGateway->getPublishableStripeKey() ? '' : 'expiration_month')
->autocomplete('cc-exp-month')
->data_stripe('exp-month')
->placeholder(trans('texts.expiration_month'))
@ -314,7 +235,7 @@ header h3 em {
!!}
</div>
<div class="col-md-6">
{!! Former::select($gateway->isGateway(GATEWAY_STRIPE) ? 'expiration_year' : 'expiration_year')
{!! Former::select($accountGateway->getPublishableStripeKey() ? '' : 'expiration_year')
->autocomplete('cc-exp-year')
->data_stripe('exp-year')
->placeholder(trans('texts.expiration_year'))
@ -336,15 +257,15 @@ header h3 em {
<div class="row" style="padding-top:18px">
<div class="col-md-5">
@if ($client && $account->showTokenCheckbox())
@if ($client && $account->showTokenCheckbox())
<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">{!! trans('texts.token_billing_secure', ['stripe_link' => link_to('https://stripe.com/', 'Stripe.com', ['target' => '_blank'])]) !!}</span>
@endif
@endif
</div>
<div class="col-md-7">
@if (isset($acceptedCreditCardTypes))
<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;"/>

View File

@ -0,0 +1,130 @@
<style type="text/css">
body {
background-color: #f8f8f8;
color: #1b1a1a;
}
.panel-body {
padding-bottom: 50px;
}
.container input[type=text],
.container input[type=email],
.container select {
font-weight: 300;
font-family: 'Roboto', sans-serif;
width: 100%;
padding: 11px;
color: #8c8c8c;
background: #f9f9f9;
border: 1px solid #ebe7e7;
border-radius: 3px;
font-size: 16px;
min-height: 42px !important;
font-weight: 400;
}
div.col-md-3,
div.col-md-5,
div.col-md-6,
div.col-md-7,
div.col-md-9,
div.col-md-12 {
margin: 6px 0 6px 0;
}
span.dropdown-toggle {
border-color: #ebe7e7;
}
.dropdown-toggle {
margin: 0px !important;
}
.container input[placeholder],
.container select[placeholder] {
color: #444444;
}
div.row {
padding-top: 8px;
}
header {
margin: 0px !important
}
@media screen and (min-width: 700px) {
header {
margin: 20px 0 75px;
float: left;
}
.panel-body {
padding-left: 150px;
padding-right: 150px;
}
}
h2 {
font-weight: 300;
font-size: 30px;
color: #2e2b2b;
line-height: 1;
}
h3 {
font-weight: 900;
margin-top: 10px;
font-size: 15px;
}
h3 .help {
font-style: italic;
font-weight: normal;
color: #888888;
}
header h3 {
text-transform: uppercase;
}
header h3 span {
display: inline-block;
margin-left: 8px;
}
header h3 em {
font-style: normal;
color: #eb8039;
}
.secure {
text-align: right;
float: right;
background: url({{ asset('/images/icon-shield.png') }}) right 22px no-repeat;
padding: 17px 55px 10px 0;
}
.secure h3 {
color: #36b855;
font-size: 30px;
margin-bottom: 8px;
margin-top: 0px;
}
.secure div {
color: #acacac;
font-size: 15px;
font-weight: 900;
text-transform: uppercase;
}
</style>