Merge branch 'master' of github.com:invoiceninja/invoiceninja
This commit is contained in:
commit
7ff7eea3a9
3 changed files with 372 additions and 85 deletions
|
|
@ -322,7 +322,167 @@
|
|||
@endif
|
||||
|
||||
@if ($accountGateway->isGateway(GATEWAY_WEPAY) && $account->token_billing_type_id == TOKEN_BILLING_DISABLED)
|
||||
{{--- do nothing ---}}
|
||||
{{--- do nothing --}}
|
||||
@elseif($accountGateway->isGateway(GATEWAY_STRIPE))
|
||||
|
||||
<div class="ninja stripe">
|
||||
<div class="row">
|
||||
<div class="field">
|
||||
<div id="card-number" class="input empty"></div>
|
||||
<label for="card-number" data-tid="card_number_label">Card number</label>
|
||||
<div class="baseline"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="field half-width">
|
||||
<div id="card-expiry" class="input empty"></div>
|
||||
<label for="card-expiry" data-tid="card_expiry_label">Expiration</label>
|
||||
<div class="baseline"></div>
|
||||
</div>
|
||||
<div class="field half-width">
|
||||
<div id="card-cvc" class="input empty"></div>
|
||||
<label for="card-cvc" data-tid="card_cvc_label">CVC</label>
|
||||
<div class="baseline"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="card-errors" role="alert"></div>
|
||||
</div>
|
||||
|
||||
@include("payments.stripe.credit_card_stripe_css")
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
// Create a Stripe client.
|
||||
var stripe = Stripe('{{ $accountGateway->getPublishableKey() }}');
|
||||
|
||||
// Create an instance of Elements.
|
||||
var elements = stripe.elements();
|
||||
|
||||
// Custom styling can be passed to options when creating an Element.
|
||||
// (Note that this demo uses a wider set of styles than the guide below.)
|
||||
var elementStyles = {
|
||||
base: {
|
||||
color: '#32325D',
|
||||
fontWeight: 500,
|
||||
fontFamily: 'Source Code Pro, Consolas, Menlo, monospace',
|
||||
fontSize: '16px',
|
||||
fontSmoothing: 'antialiased',
|
||||
|
||||
'::placeholder': {
|
||||
color: '#CFD7DF',
|
||||
},
|
||||
':-webkit-autofill': {
|
||||
color: '#e39f48',
|
||||
},
|
||||
},
|
||||
invalid: {
|
||||
color: '#E25950',
|
||||
|
||||
'::placeholder': {
|
||||
color: '#FFCCA5',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
var elementClasses = {
|
||||
focus: 'focused',
|
||||
empty: 'empty',
|
||||
invalid: 'invalid',
|
||||
};
|
||||
|
||||
var cardNumber = elements.create('cardNumber', {
|
||||
style: elementStyles,
|
||||
classes: elementClasses,
|
||||
});
|
||||
cardNumber.mount('#card-number');
|
||||
|
||||
var cardExpiry = elements.create('cardExpiry', {
|
||||
style: elementStyles,
|
||||
classes: elementClasses,
|
||||
});
|
||||
cardExpiry.mount('#card-expiry');
|
||||
|
||||
var cardCvc = elements.create('cardCvc', {
|
||||
style: elementStyles,
|
||||
classes: elementClasses,
|
||||
});
|
||||
cardCvc.mount('#card-cvc');
|
||||
|
||||
|
||||
cardNumber.addEventListener('change', function(event){
|
||||
var displayError = document.getElementById('card-errors');
|
||||
if (event.error) {
|
||||
displayError.textContent = event.error.message;
|
||||
} else {
|
||||
displayError.textContent = '';
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
cardExpiry.addEventListener('change', function(event){
|
||||
var displayError = document.getElementById('card-errors');
|
||||
if (event.error) {
|
||||
displayError.textContent = event.error.message;
|
||||
} else {
|
||||
displayError.textContent = '';
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
cardCvc.addEventListener('change', function(event){
|
||||
var displayError = document.getElementById('card-errors');
|
||||
if (event.error) {
|
||||
displayError.textContent = event.error.message;
|
||||
} else {
|
||||
displayError.textContent = '';
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
function releaseSubmitButton(){
|
||||
$('.payment-form').find('button').prop('disabled', false);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Handle form submission.
|
||||
var form = document.getElementById('payment-form');
|
||||
form.addEventListener('submit', function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
var options = {
|
||||
address_zip: document.getElementById('postal_code').value,
|
||||
};
|
||||
|
||||
stripe.createToken(cardNumber, options).then(function(result) {
|
||||
if (result.error) {
|
||||
// Inform the user if there was an error.
|
||||
var errorElement = document.getElementById('card-errors');
|
||||
errorElement.textContent = result.error.message;
|
||||
releaseSubmitButton();
|
||||
} else {
|
||||
// Send the token to your server.
|
||||
stripeTokenHandler(result.token);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
function stripeTokenHandler(token) {
|
||||
// Insert the token ID into the form so it gets submitted to the server
|
||||
var form = document.getElementById('payment-form');
|
||||
var hiddenInput = document.createElement('input');
|
||||
hiddenInput.setAttribute('type', 'hidden');
|
||||
hiddenInput.setAttribute('name', 'sourceToken');
|
||||
hiddenInput.setAttribute('value', token.id);
|
||||
form.appendChild(hiddenInput);
|
||||
|
||||
// Submit the form
|
||||
form.submit();
|
||||
}
|
||||
|
||||
</script>
|
||||
@else
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
|
|
@ -463,4 +623,4 @@
|
|||
|
||||
{!! Former::close() !!}
|
||||
|
||||
@stop
|
||||
@stop
|
||||
|
|
@ -5,89 +5,7 @@
|
|||
|
||||
@if ($accountGateway->getPublishableKey())
|
||||
<script type="text/javascript" src="https://js.stripe.com/v2/"></script>
|
||||
<script type="text/javascript">
|
||||
Stripe.setPublishableKey('{{ $accountGateway->getPublishableKey() }}');
|
||||
$(function() {
|
||||
$('.payment-form').unbind('submit').submit(function(event) {
|
||||
event.preventDefault();
|
||||
<script type="text/javascript" src="https://js.stripe.com/v3/"></script>
|
||||
|
||||
if ($('[name=plaidAccountId]').length) {
|
||||
return;
|
||||
}
|
||||
|
||||
var $form = $(this);
|
||||
|
||||
var data = {
|
||||
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()
|
||||
};
|
||||
|
||||
// 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 ($('#cvv').val() != ' ') {
|
||||
data.cvc = $('#cvv').val();
|
||||
if (!Stripe.card.validateCVC(data.cvc)) {
|
||||
$('#js-error-message').html('{{ trans('texts.invalid_cvv') }}').fadeIn();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($form.find('button').is(':disabled')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Disable the submit button to prevent repeated clicks
|
||||
$form.find('button').prop('disabled', true);
|
||||
$('#js-error-message').hide();
|
||||
|
||||
Stripe.card.createToken(data, 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;
|
||||
if (token) {
|
||||
// Insert the token into the form so it gets submitted to the server
|
||||
$form.append($('<input type="hidden" name="sourceToken"/>').val(token));
|
||||
// and submit
|
||||
$form.get(0).submit();
|
||||
} else {
|
||||
$('#js-error-message').html('An error occurred').fadeIn();
|
||||
$form.find('button').prop('disabled', false);
|
||||
logError('STRIPE_ERROR: ' + JSON.stringify(response));
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@endif
|
||||
@stop
|
||||
|
|
|
|||
209
resources/views/payments/stripe/credit_card_stripe_css.blade.php
Normal file
209
resources/views/payments/stripe/credit_card_stripe_css.blade.php
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
<style>
|
||||
.ninja.stripe {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.ninja.stripe * {
|
||||
font-family: Source Code Pro, Consolas, Menlo, monospace;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.ninja.stripe .row {
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
margin: 0 5px 10px;
|
||||
}
|
||||
|
||||
.ninja.stripe .field {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.ninja.stripe .field.half-width {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.ninja.stripe .field.quarter-width {
|
||||
width: calc(25% - 10px);
|
||||
}
|
||||
|
||||
.ninja.stripe .baseline {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
background-color: #cfd7df;
|
||||
transition: background-color 0.3s cubic-bezier(0.165, 0.84, 0.44, 1);
|
||||
}
|
||||
|
||||
.ninja.stripe label {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
bottom: 8px;
|
||||
color: #cfd7df;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
transform-origin: 0 50%;
|
||||
cursor: text;
|
||||
transition-property: color, transform;
|
||||
transition-duration: 0.3s;
|
||||
transition-timing-function: cubic-bezier(0.165, 0.84, 0.44, 1);
|
||||
}
|
||||
|
||||
.ninja.stripe .input {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
padding-bottom: 7px;
|
||||
color: #32325d;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.ninja.stripe .input::-webkit-input-placeholder {
|
||||
color: transparent;
|
||||
transition: color 0.3s cubic-bezier(0.165, 0.84, 0.44, 1);
|
||||
}
|
||||
|
||||
.ninja.stripe .input::-moz-placeholder {
|
||||
color: transparent;
|
||||
transition: color 0.3s cubic-bezier(0.165, 0.84, 0.44, 1);
|
||||
}
|
||||
|
||||
.ninja.stripe .input:-ms-input-placeholder {
|
||||
color: transparent;
|
||||
transition: color 0.3s cubic-bezier(0.165, 0.84, 0.44, 1);
|
||||
}
|
||||
|
||||
.ninja.stripe .input.StripeElement {
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s cubic-bezier(0.165, 0.84, 0.44, 1);
|
||||
will-change: opacity;
|
||||
}
|
||||
|
||||
.ninja.stripe .input.focused,
|
||||
.ninja.stripe .input:not(.empty) {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.ninja.stripe .input.focused::-webkit-input-placeholder,
|
||||
.ninja.stripe .input:not(.empty)::-webkit-input-placeholder {
|
||||
color: #cfd7df;
|
||||
}
|
||||
|
||||
.ninja.stripe .input.focused::-moz-placeholder,
|
||||
.ninja.stripe .input:not(.empty)::-moz-placeholder {
|
||||
color: #cfd7df;
|
||||
}
|
||||
|
||||
.ninja.stripe .input.focused:-ms-input-placeholder,
|
||||
.ninja.stripe .input:not(.empty):-ms-input-placeholder {
|
||||
color: #cfd7df;
|
||||
}
|
||||
|
||||
.ninja.stripe .input.focused + label,
|
||||
.ninja.stripe .input:not(.empty) + label {
|
||||
color: #aab7c4;
|
||||
transform: scale(0.85) translateY(-25px);
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.ninja.stripe .input.focused + label {
|
||||
color: #24b47e;
|
||||
}
|
||||
|
||||
.ninja.stripe .input.invalid + label {
|
||||
color: #ffa27b;
|
||||
}
|
||||
|
||||
.ninja.stripe .input.focused + label + .baseline {
|
||||
background-color: #24b47e;
|
||||
}
|
||||
|
||||
.ninja.stripe .input.focused.invalid + label + .baseline {
|
||||
background-color: #e25950;
|
||||
}
|
||||
|
||||
.ninja.stripe input, .ninja.stripe button {
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
outline: none;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
.ninja.stripe input:-webkit-autofill {
|
||||
-webkit-text-fill-color: #e39f48;
|
||||
transition: background-color 100000000s;
|
||||
-webkit-animation: 1ms void-animation-out;
|
||||
}
|
||||
|
||||
.ninja.stripe .StripeElement--webkit-autofill {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.ninja.stripe input, .ninja.stripe button {
|
||||
-webkit-animation: 1ms void-animation-out;
|
||||
}
|
||||
|
||||
.ninja.stripe button {
|
||||
display: block;
|
||||
width: calc(100% - 30px);
|
||||
height: 40px;
|
||||
margin: 40px 15px 0;
|
||||
background-color: #24b47e;
|
||||
border-radius: 4px;
|
||||
color: #fff;
|
||||
text-transform: uppercase;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ninja.stripe input:active {
|
||||
background-color: #159570;
|
||||
}
|
||||
|
||||
.ninja.stripe .error svg {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
||||
.ninja.stripe .error svg .base {
|
||||
fill: #e25950;
|
||||
}
|
||||
|
||||
.ninja.stripe .error svg .glyph {
|
||||
fill: #fff;
|
||||
}
|
||||
|
||||
.ninja.stripe .error .message {
|
||||
color: #e25950;
|
||||
}
|
||||
|
||||
.ninja.stripe .success .icon .border {
|
||||
stroke: #abe9d2;
|
||||
}
|
||||
|
||||
.ninja.stripe .success .icon .checkmark {
|
||||
stroke: #24b47e;
|
||||
}
|
||||
|
||||
.ninja.stripe .success .title {
|
||||
color: #32325d;
|
||||
font-size: 16px !important;
|
||||
}
|
||||
|
||||
.ninja.stripe .success .message {
|
||||
color: #8898aa;
|
||||
font-size: 13px !important;
|
||||
}
|
||||
|
||||
.ninja.stripe .success .reset path {
|
||||
fill: #24b47e;
|
||||
}
|
||||
</style>
|
||||
Loading…
Add table
Reference in a new issue