# Stripe payment form

I have a stripe payment form and when I use different forms that get hidden, I must use separate IDs for the form to appear. I have found a way to do this but looking to simplify the code if this is possible.

I have a page that has listings for purchase. Each listing can have variants. Because of this, I need to create separate Stripe JavaScript so the credit card form appears.

I am wondering if there is a simpler route to go in instead of doing the following:

  <script>

var stripe = Stripe('pk_test_1234567890');

var elements = stripe.elements();

var style = {
base: {
color: '#32325d',
lineHeight: '24px',
fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
fontSmoothing: 'antialiased',
fontSize: '16px',
'::placeholder': {
color: '#aab7c4'
}
},
invalid: {
color: '#fa755a',
iconColor: '#fa755a'
}
};

var card = elements.create('card', {style: style});

card.mount('#card-element');

var displayError = document.getElementById('card-errors');
if (event.error) {
displayError.textContent = event.error.message;
} else {
displayError.textContent = '';
}
});

var form = document.getElementById('payment_form');
event.preventDefault();

stripe.createToken(card).then(function(result) {
if (result.error) {
var errorElement = document.getElementById('card-errors');
errorElement.textContent = result.error.message;
} else {
stripeTokenHandler(result.token);
}
});
});

function stripeTokenHandler(token) {

var form = document.getElementById('payment_form');
var hiddenInput = document.createElement('input');
hiddenInput.setAttribute('type', 'hidden');
hiddenInput.setAttribute('name', 'stripeToken');
hiddenInput.setAttribute('value', token.id);
form.appendChild(hiddenInput);

// Submit the form
form.submit();
}

</script>

<script>

var stripe = Stripe('pk_test_1234567890');

var elements = stripe.elements();

var style = {
base: {
color: '#32325d',
lineHeight: '24px',
fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
fontSmoothing: 'antialiased',
fontSize: '16px',
'::placeholder': {
color: '#aab7c4'
}
},
invalid: {
color: '#fa755a',
iconColor: '#fa755a'
}
};

var card = elements.create('card', {style: style});

#HERE IS USE DIFFERENT CARD ID

card.mount('#card-element-2');

var displayError = document.getElementById('card-errors');
if (event.error) {
displayError.textContent = event.error.message;
} else {
displayError.textContent = '';
}
});

var form = document.getElementById('payment_form');
event.preventDefault();

stripe.createToken(card).then(function(result) {
if (result.error) {
var errorElement = document.getElementById('card-errors');
errorElement.textContent = result.error.message;
} else {
stripeTokenHandler(result.token);
}
});
});

function stripeTokenHandler(token) {
var form = document.getElementById('payment_form');
var hiddenInput = document.createElement('input');
hiddenInput.setAttribute('type', 'hidden');
hiddenInput.setAttribute('name', 'stripeToken');
hiddenInput.setAttribute('value', token.id);
form.appendChild(hiddenInput);

form.submit();
}

</script>


One of the forms:

    <div class="form-row">
<label for="card-element-2">
Credit or debit card
</label>
<div id="card-element-2" class="form-control">
</div>
</div>
...
...


As you see, if you look closely, there are different ids being called in each separate script. I have a commented out like #HERE I USE DIFFERENT CARD ID.

Then, in the view, I have separate credit card forms with different IDs. It is like this because when one form is appearing, another is hidden.

Is there a way where I can simplify this code and call the possibility of multiple IDs instead of having long JavaScript like the way I do?

• Welcome to Code Review. The current question title, which states your concerns about the code, applies to too many questions on this site to be useful. The site standard is for the title to simply state the task accomplished by the code. Please see How to Ask for examples, and revise the title accordingly. – Zeta Jan 18 at 7:03
• Please put your current approach in words, in addition to presenting code you think implements it. I strongly recommend adding such to the code (but not in this post - someone may have been busy for an hour or more writing an answer about just that!) as comments. – greybeard Jan 18 at 7:53
• Welcome to Code Review! I don't see any element with id="payment_form" in the sample HTML, despite my expectations after reading the JavaScript code... Is that attribute on a root element, or is it typically a child node of another element - e.g. <div id="card-element-2" class="form-control">? – Sᴀᴍ Onᴇᴌᴀ Jan 18 at 16:37
• @SᴀᴍOnᴇᴌᴀ The difference is the card-element for the card.mount('#card-element'); --- i tried using an || to solve it with the full mount line and just the id's but didn't help. I basically need a way to say "this can be card-element or card-element-2 – uno Jan 18 at 21:49

instead of manually selecting each card every time like

card.mount('#card-element-2');

var displayError = document.getElementById('card-errors');
if (event.error) {
displayError.textContent = event.error.message;
} else {
displayError.textContent = '';
}
});


you can select all the elements that start with #card-element and loop through them to add the eventListener :

document.querySelectorAll('[id^=card-element]').forEach(e => {
var card = elements.create('card', {
style: style
});

card.mount(#${e.id}); card.addEventListener('change', function(event) { var displayError = document.getElementById('card-errors'); if (event.error) { displayError.textContent = event.error.message; } else { displayError.textContent = ''; } }); });  Final code should look like : var stripe = Stripe('pk_test_1234567890'); var elements = stripe.elements(); var style = { base: { color: '#32325d', lineHeight: '24px', fontFamily: '"Helvetica Neue", Helvetica, sans-serif', fontSmoothing: 'antialiased', fontSize: '16px', '::placeholder': { color: '#aab7c4' } }, invalid: { color: '#fa755a', iconColor: '#fa755a' } }; document.querySelectorAll('[id^=card-element]').forEach(e => { var card = elements.create('card', { style: style }); card.mount(#${e.id});

var displayError = document.getElementById('card-errors');
if (event.error) {
displayError.textContent = event.error.message;
} else {
displayError.textContent = '';
}
});
});

var form = document.getElementById('payment_form');
event.preventDefault();

stripe.createToken(card).then(function(result) {
if (result.error) {
var errorElement = document.getElementById('card-errors');
errorElement.textContent = result.error.message;
} else {
stripeTokenHandler(result.token);
}
});
});

function stripeTokenHandler(token) {
var form = document.getElementById('payment_form');
var hiddenInput = document.createElement('input');
hiddenInput.setAttribute('type', 'hidden');
hiddenInput.setAttribute('name', 'stripeToken');
hiddenInput.setAttribute('value', token.id);
form.appendChild(hiddenInput);

form.submit();
}

• thanks for the input but that didn't seem to work. The forms won't appear unless the specific id is called. – uno Jan 22 at 0:12
• @uno i just noticed i forgot to add the # before e.id, it should be card.mount(#\${e.id}); or card.mount('#' + e.id); because the value of e.id would be the id without the # – Taki Jan 22 at 0:35