Enhance TapPay payment form styling and validation logic

This commit is contained in:
warrenchen 2025-12-10 16:43:43 +09:00
parent ce18a5eb1c
commit ea828cf99c

View File

@ -8,13 +8,70 @@
<input type="hidden" asp-for="ServerType" />
<input type="hidden" id="Prime" name="Prime" />
<table width="100%" cellspacing="2" cellpadding="1" border="0">
<style>
.tappay-table {
width: 100%;
border-collapse: separate;
border-spacing: 2px 10px;
}
.tappay-table label {
font-weight: 600;
color: #555;
}
.tappay-table .form-control,
.tappay-table .tappay-input {
height: 38px;
border: 1px solid #ccc;
border-radius: 4px;
padding: 8px;
box-sizing: border-box;
transition: border-color .2s ease, color .2s ease, box-shadow .2s ease;
}
.tappay-table .tappay-input {
width: 100%;
}
.tappay-table .has-error .form-control,
.tappay-table .has-error .tappay-input {
border-color: #e74c3c;
color: #e74c3c;
box-shadow: 0 0 0 1px rgba(231, 76, 60, 0.15);
}
.tappay-table .has-success .form-control,
.tappay-table .has-success .tappay-input {
border-color: #2ecc71;
color: #2ecc71;
box-shadow: 0 0 0 1px rgba(46, 204, 113, 0.15);
}
.tappay-table .phone-wrapper {
display: flex;
gap: 6px;
align-items: center;
}
.tappay-table .country-code-input {
max-width: 70px;
text-align: center;
}
.tappay-table .country-prefix {
font-weight: 600;
margin-right: 4px;
}
</style>
<table class="tappay-table" width="100%" cellspacing="2" cellpadding="1" border="0">
<tr class="card-number-group">
<td>
<label><span id="cardtype"></span>@T("Payment.CardNumber"):</label>
</td>
<td>
<div class="form-control card-number" style="height: 20px"></div>
<div class="form-control card-number"></div>
</td>
</tr>
<tr class="expiration-date-group">
@ -22,7 +79,7 @@
<label>@T("Payment.ExpirationDate"):</label>
</td>
<td>
<div class="form-control expiration-date" style="height: 20px"></div>
<div class="form-control expiration-date"></div>
</td>
</tr>
<tr class="ccv-group">
@ -30,23 +87,23 @@
<label>@T("Payment.CardCode"):</label>
</td>
<td>
<div class="form-control ccv" style="height: 20px"></div>
<div class="form-control ccv"></div>
</td>
</tr>
<tr>
<tr class="name-en-group">
<td>
<label>@T("Payment.CardHolderName"):</label>
</td>
<td>
<input type="text" id="CardholderNameEn" name="CardholderNameEn" maxlength="45" placeholder="@T("Payment.CardHolderName")"/>
<input class="tappay-input" type="text" id="CardholderNameEn" name="CardholderNameEn" maxlength="45" placeholder="@T("Payment.CardHolderName")"/>
</td>
</tr>
<tr>
<tr class="email-group">
<td>
<label>@T("Payment.CardHolderEmail"):</label>
</td>
<td>
<input type="text" id="CardholderEmail" name="CardholderEmail" maxlength="40" placeholder="@T("Payment.CardHolderEmail")"/>
<input class="tappay-input" type="text" id="CardholderEmail" name="CardholderEmail" maxlength="40" placeholder="@T("Payment.CardHolderEmail")"/>
</td>
</tr>
<tr id="phone_group">
@ -54,14 +111,107 @@
<label>@T("Payment.CardHolderPhone"):</label>
</td>
<td>
<div style="display: flex">
+<input type="tel" id="CardholderPhoneNumberCountryCode" name="CardholderPhoneNumberCountryCode" minlength="1" maxlength="3" placeholder="886" value="886"/>
<input type="tel" id="CardholderPhoneNumber" name="CardholderPhoneNumber" maxlength="16" placeholder="912345678"/>
<div class="phone-wrapper">
<div class="phone-code-group">
<span class="country-prefix">+</span>
<input class="tappay-input country-code-input" type="tel" id="CardholderPhoneNumberCountryCode" name="CardholderPhoneNumberCountryCode" minlength="1" maxlength="4" placeholder="886" value="886"/>
</div>
<div class="phone-number-group">
<input class="tappay-input phone-number-input" type="tel" id="CardholderPhoneNumber" name="CardholderPhoneNumber" maxlength="16" placeholder="912345678"/>
</div>
</div>
</td>
</tr>
</table>
<script asp-location="Footer">
var tappayCustomFieldState = {
nameEn: { valid: false, dirty: false },
email: { valid: false, dirty: false },
phoneCode: { valid: false, dirty: false },
phoneNumber: { valid: false, dirty: false }
};
var tappayCanGetPrime = false;
function setGroupState(selector, isValid, isDirty) {
var group = $(selector);
group.removeClass('has-error has-success');
if (isDirty) {
group.addClass(isValid ? 'has-success' : 'has-error');
}
}
function validateNameEn(markDirty) {
var value = $('#CardholderNameEn').val().trim();
tappayCustomFieldState.nameEn.dirty = tappayCustomFieldState.nameEn.dirty || markDirty || value.length > 0;
tappayCustomFieldState.nameEn.valid = /^[A-Za-z]+(?:[\s'-][A-Za-z]+)*$/.test(value) && value.length > 1;
setGroupState('.name-en-group', tappayCustomFieldState.nameEn.valid, tappayCustomFieldState.nameEn.dirty);
return tappayCustomFieldState.nameEn.valid;
}
function validateEmail(markDirty) {
var value = $('#CardholderEmail').val().trim();
tappayCustomFieldState.email.dirty = tappayCustomFieldState.email.dirty || markDirty || value.length > 0;
tappayCustomFieldState.email.valid = /^[^\s@@]+@@[^\s@@]+\.[^\s@@]+$/.test(value);
setGroupState('.email-group', tappayCustomFieldState.email.valid, tappayCustomFieldState.email.dirty);
return tappayCustomFieldState.email.valid;
}
function validatePhoneCode(markDirty) {
var value = $('#CardholderPhoneNumberCountryCode').val().trim();
tappayCustomFieldState.phoneCode.dirty = tappayCustomFieldState.phoneCode.dirty || markDirty || value.length > 0;
tappayCustomFieldState.phoneCode.valid = /^\d{1,4}$/.test(value);
setGroupState('#phone_group .phone-code-group', tappayCustomFieldState.phoneCode.valid, tappayCustomFieldState.phoneCode.dirty);
return tappayCustomFieldState.phoneCode.valid;
}
function validatePhoneNumber(markDirty) {
var value = $('#CardholderPhoneNumber').val().trim();
tappayCustomFieldState.phoneNumber.dirty = tappayCustomFieldState.phoneNumber.dirty || markDirty || value.length > 0;
tappayCustomFieldState.phoneNumber.valid = /^\d{4,16}$/.test(value);
setGroupState('#phone_group .phone-number-group', tappayCustomFieldState.phoneNumber.valid, tappayCustomFieldState.phoneNumber.dirty);
return tappayCustomFieldState.phoneNumber.valid;
}
function areCustomFieldsValid(markDirty) {
var nameValid = validateNameEn(markDirty);
var emailValid = validateEmail(markDirty);
var phoneCodeValid = validatePhoneCode(markDirty);
var phoneNumberValid = validatePhoneNumber(markDirty);
return nameValid && emailValid && phoneCodeValid && phoneNumberValid;
}
function updateSubmitButton(canGetPrime) {
if (typeof canGetPrime === 'boolean') {
tappayCanGetPrime = canGetPrime;
}
var canSubmit = tappayCanGetPrime && areCustomFieldsValid(false);
$('.button-1.payment-info-next-step-button').prop('disabled', !canSubmit);
}
$(function () {
$('#CardholderNameEn').on('input blur', function () {
validateNameEn(true);
updateSubmitButton();
});
$('#CardholderEmail').on('input blur', function () {
validateEmail(true);
updateSubmitButton();
});
$('#CardholderPhoneNumberCountryCode').on('input blur', function () {
validatePhoneCode(true);
updateSubmitButton();
});
$('#CardholderPhoneNumber').on('input blur', function () {
validatePhoneNumber(true);
updateSubmitButton();
});
// initialize default validation state
validatePhoneCode(false);
updateSubmitButton(false);
});
$.ajax({
url: "https://js.tappaysdk.com/sdk/tpdirect/v5.24.0",
@ -69,10 +219,10 @@
cache: true,
success: function() {
TPDirect.setupSDK($('#AppId').val(), $('#AppKey').val(), $('#ServerType').val());
$('.button-1.payment-info-next-step-button').attr('disabled', true);
$('.button-1.payment-info-next-step-button').prop('disabled', true);
$('.button-1.payment-info-next-step-button').removeAttr('onclick');
$('.button-1.payment-info-next-step-button').off('click');
$('.button-1.payment-info-next-step-button').click(function() {
$('.button-1.payment-info-next-step-button').click(function(event) {
event.preventDefault();
// fix keyboard issue in iOS device
@ -81,6 +231,14 @@
const tappayStatus = TPDirect.card.getTappayFieldsStatus();
console.log(tappayStatus);
tappayCanGetPrime = tappayStatus.canGetPrime;
if (!areCustomFieldsValid(true)) {
updateSubmitButton(tappayCanGetPrime);
alert('請完整填寫持卡人資訊');
return;
}
// Check TPDirect.card.getTappayFieldsStatus().canGetPrime before TPDirect.card.getPrime
if (tappayStatus.canGetPrime === false) {
alert('can not get prime');
@ -173,13 +331,7 @@
// update.canGetPrime === true
// --> you can call TPDirect.card.getPrime()
// const submitButton = document.querySelector('button[type="submit"]')
if (update.canGetPrime) {
// submitButton.removeAttribute('disabled')
$('.button-1.payment-info-next-step-button').removeAttr('disabled');
} else {
// submitButton.setAttribute('disabled', true)
$('.button-1.payment-info-next-step-button').attr('disabled', true);
}
updateSubmitButton(update.canGetPrime);
/* Change card type display when card type change */
/* ============================================== */