We at Pro nopCommerce are addicted at User Experience, i.e. UX. As good as we think nopCommerce already is, there are still many UX improvements that we thought can be done.
Credit card forms is one of the areas that allows a lot of room for UX enhancement. For those who are familiar with nopCommerce's typical credit card forms, you know it looks very 90-ish. Below is how a typical credit card form in nopCommerce looks like:
There are a few things we don't like about the original implementation:
- Credit card type should be inferred, instead of forcing users to choose
- Card number should be formatted. Basic verification should also be done on client side
- Expiry date should be implemented as input, drop-down list is too cumbersome
- Basic CVV / CVC format validationn should be done on client side
Luckily, there are quite a few useful jQuery libraries on client-side credit card validation, such as jQuery.payment.
Let's try to integrate jQuery.payment into the Payments.AuthorizeNet payment plugins that comes originally with nopCommerce.
Setting up jQuery.payment in nopCommerce
The first thing you need to do is to download the package from jQuery.payment project page. Once downloaded, copy jquery.payment.js into the appropriate folder shown below:
Note you need to mark the file as "Copy if newer".
Integrationg jQuery.payment into nopCommerce credit card form
Now, open PaymentInfo.cshtml from Payments.Authorizenet.
Therea are a few places you need to change. I am pasting the full source code of PaymentInfo.cshtml below.
@{
Layout = "";
}
@model Nop.Plugin.Payments.AuthorizeNet.Models.PaymentInfoModel
@using Nop.Web.Framework;
<table width="100%" cellspacing="2" cellpadding="1" border="0">
<tr>
<td>
@Html.NopLabelFor(model => model.CreditCardType, false):
</td>
<td>
<span class="cc-brand"></span>
@Html.HiddenFor(model => model.CreditCardType)
</td>
</tr>
<tr>
<td>
@Html.NopLabelFor(model => model.CardholderName, false):
</td>
<td>
@Html.TextBoxFor(model => model.CardholderName, new { style = "Width: 165px;", autocomplete = "off" })
@Html.ValidationMessageFor(model => model.CardholderName)
</td>
</tr>
<tr>
<td>
@Html.NopLabelFor(model => model.CardNumber, false):
</td>
<td>
@Html.TextBoxFor(model => model.CardNumber, new { style = "Width: 165px;", autocomplete = "off", maxlength = 22 })
@Html.ValidationMessageFor(model => model.CardNumber)
</td>
</tr>
<tr>
<td>
@Html.NopLabelFor(model => model.ExpireMonth, false):
</td>
<td>
<input type="text" class="cc-exp" placeholder="MM / YY" />
@Html.HiddenFor(model => model.ExpireMonth)
@Html.HiddenFor(model => model.ExpireYear)
</td>
</tr>
<tr>
<td>
@Html.NopLabelFor(model => model.CardCode, false):
</td>
<td>
@Html.TextBoxFor(model => model.CardCode, new { style = "Width: 60px;", autocomplete = "off", maxlength = 4 })
@Html.ValidationMessageFor(model => model.CardCode)
</td>
</tr>
</table>
<script type="text/javascript" src="~/Plugins/Payments.AuthorizeNet/Content/jquery.payment.js"></script>
<script type="text/javascript">
document.ready(function () {
$('#@Html.FieldIdFor(m => m.CardNumber)').payment('formatCardNumber');
$('.cc-exp').payment('formatCardExpiry');
$('#@Html.FieldIdFor(m => m.CardCode)').payment('formatCardCVC');
$('#@Html.FieldIdFor(m => m.CardNumber)').change(function () {
var cardType = $.payment.cardType($('#@Html.FieldIdFor(m => m.CardNumber)').val());
$('.cc-brand').text(cardType);
$('#@Html.FieldIdFor(m => m.CreditCardType)').val(cardType);
});
$('.cc-exp').change(function () {
$('#@Html.FieldIdFor(m => m.ExpireMonth)').val($(this).val().substring(0, 2));
$('#@Html.FieldIdFor(m => m.ExpireYear)').val('20' + $(this).val().substring(5));
});
$('.payment-info-next-step-button').removeAttr('onclick');
$('.payment-info-next-step-button').click(function (e) {
e.preventDefault();
var valid = true;
if (!$.payment.validateCardNumber($('#@Html.FieldIdFor(m => m.CardNumber)').val())) {
alert('Invalid card number');
valid = false;
}
if (!$.payment.validateCardExpiry($('.cc-exp').payment('cardExpiryVal'))) {
alert('Invalid card expiry');
valid = false;
}
var cardType = $.payment.cardType($('#@Html.FieldIdFor(m => m.CardNumber)').val());
if (!$.payment.validateCardCVC($('#@Html.FieldIdFor(m => m.CardCode)').val(), cardType)) {
alert('Invalid card CVC');
valid = false;
}
if (valid)
PaymentInfo.save();
});
});
</script>
nopCommerce-jQuery.payment integration explained
Below are screenshots showing how the form looks like after integration with jQuery.payment.
[Credit card form when empty]
[Credit card form when filled]
[Credit card form with invalid data]
Notice that:
- There is no need for users to select the credit card type. It is automatically inferred.
- Card number and expiry date are properly formatted better visualization (notice the spaces and slash).
- Basic validation is done on the client side.
A better credit card form for nopCommerce
What I have shown here is just a very basic integration of nopCommerce with jQuery.payment. If you are adventurous enough, it is absolutely possible to create a beautoful credit card form like below. All you need to do is to extend the code I've shared here, plus some other CSS codes to beautify the form.