<div id="divWrapper_PaymentPopup">
<div class="PaymentPopup"></div>
</div>
<style>
.dx-dropdownlist-popup-wrapper {
height: 100;
width;
}
div#divWrapper_PaymentMethods {
display: flex;
flex-direction: column;
min-width: 100px !important;
margin: 10px 30px 10px 20px;
}
.btngroup .dx-box-flex.dx-box.dx-widget {
flex-direction: row !important;
}
.btngroup .dx-item.dx-box-item {
flex: 0 0 0 !important;
}
div#btnPayByBank,
div#btnPayByCC {
background: #fff;
border: 1px solid gray;
text-align: center;
color: #000;
margin-bottom: 20px;
max-width: 130px;
}
div#btnPayByBank.dx-state-focused,
div#btnPayByCC.dx-state-focused,
div#btnPayByBank.active,
div#btnPayByCC.active {
background: #f0f0f0;
border: 1px solid gray;
text-align: center;
color: #000;
margin-bottom: 20px;
}
div#btnPayByBank .dx-button-content,
div#btnPayByCC .dx-button-content {
height: fit-content;
height: -moz-fit-content;
padding: 7px 18px;
}
div#btnPayByBank .dx-button-content {
background: url(/ecommerce/images/bankAccount.png);
background-repeat: no-repeat;
background-position: center top;
background-size: 65px;
}
div#btnPayByCC .dx-button-content {
background: url(/ecommerce/images/creditCard.png);
background-repeat: no-repeat;
background-position: center top;
background-size: 65px;
}
div#btnPayByBank.dx-state-focused .dx-button-content {
background: url(/ecommerce/images/bankAccount-hover.png);
background-repeat: no-repeat;
background-position: center top;
background-size: 65px;
}
/* NOTE */
div#btnPayByBank.active .dx-button-content {
background: url(/ecommerce/images/bankAccount-hover.png);
background-repeat: no-repeat;
background-position: center top;
background-size: 65px;
}
/* NOTE */
div#btnPayByCC.active .dx-button-content {
background: url(/ecommerce/images/creditCard-hover.png);
background-repeat: no-repeat;
background-position: center top;
background-size: 65px;
}
div#btnPayByCC.dx-state-focused .dx-button-content {
background: url(/ecommerce/images/creditCard-hover.png);
background-repeat: no-repeat;
background-position: center top;
background-size: 65px;
}
div#btnPayByBank span.dx-button-text,
div#btnPayByCC span.dx-button-text {
white-space: normal;
line-height: 1em;
margin-top: 60px;
color:#000;
}
span.dx-button-text {
display: inline-block;
}
.PaymentPopup .dx-popup-content, .paymentWrapper .dx-popup-content/* , .dx-popup-content */{
display: flex;
/* flex-direction: row;*/
justify-content: flex-start;
height: fit-content !important;
}
.paymentWrapper .dx-popup-content {
justify-content: space-around;
display: flex;
}
#divWrapper_PaymentForm {
width: 80;
}
.referenceField {
margin-bottom: 40px;
}
#eCheckDisclaimer {
width;
margin: 20px auto;
}
#confirmationText {
width: 85;
margin: 20px auto;
text-align: center;
display: none;
}
#btnWrapper_PaymentPopup {
text-align: center;
}
.dx-button-mode-text {
background-color: transparent;
border-color: #333;
color: #333;
}
.dx-button-mode-text.dx-state-hover {
background-color: #106e0b;
border-color: transparent;
color: #fff;
}
.dx-button-mode-text.dx-state-focused {
background-color: #106e0b;
border-color: transparent;
color: #fff;
}
.card input {
background-image: url('/ecommerce/images/icons/empty-curved-32px.png');
background-position: 5px 5px;
background-repeat: no-repeat;
background-size: 40px;
padding-top: 3px;
padding-bottom: 3px;
padding-left: 50px;
padding-right: 5px;
filter);
}
.card div.dx-placeholder {
left: 42px !important;
top: 4px;
}
.card input.amex {
background-image: url('/ecommerce/images/icons/american-express-curved-32px.png') !important;
}
.card input.discover {
background-image: url('/ecommerce/images/icons/discover-curved-32px.png') !important;
}
.card input.mastercard {
background-image: url('/ecommerce/images/icons/mastercard-curved-32px.png') !important;
}
.card input.visa {
background-image: url('/ecommerce/images/icons/visa-curved-32px.png') !important;
}
.card input.valid {
filter: grayscale(0);
}
.dx-button-mode-text.dx-button-danger {
background-color: #d9534f;
border-color: transparent;
color: #fff;
}
.dx-button-mode-text.dx-button-danger.dx-state-hover {
background-color: #aa4340;
}
.dx-button-mode-text.dx-button-danger .dx-icon {
color: #fafafa;
}
.dx-overlay-content.dx-popup-normal.dx-resizable {
width !important;
overflow: scroll;
}
.paymentSubmit {
width: 155px;
padding: 0 !important;
}
.paymentCancel {
width: 155px;
padding: 0 !important;
}
@media (max-width: 768px) {
#divWrapper_PaymentForm {
width: 95;
}
.dx-overlay-content.dx-popup-normal.dx-resizable {
min-width: 400px;
}
.dx-popup-content,
.PaymentPopup .dx-popup-content {
display: flex;
flex-direction: column;
justify-content: flex-start;
padding: 0 15px !important;
/* height: 85vh !important; */
height: 95vh !important;
overflow: scroll;
}
div#divWrapper_PaymentMethods {
flex-direction: row;
width;
margin: 10px 15px 10px 0;
}
div#btnPayByBank,
div#btnPayByCC {
margin: 20px auto;
}
div#btnPayByBank .dx-button-content,
div#btnPayByCC .dx-button-content {
padding: 7px 5px 8px;
}
}
</style>
<script type="text/javascript">
/*'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Widget: ProcessPayment_Popup.html
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' (c) 2021 by Dovetail Internet Technologies, LLC
' www.dovetailinternet.com
' info@dovetailinternet.com
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' All rights reserved. Not to be used without permission.
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Development Date: May - September 2021
' Release Version: 2.20.0
' Revision: 2.0
' Last Modified: 10/6/2021
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: dspalay invoice payment options to a shopper
' and process selected payments by credit card
' or ACH based on provider options and configuration
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Usage: Requires B2B login shopper context
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Example SitePages usage:
<Control src="LoadWidgetControl.ascx"
FileLocation="ProcessPayment_Popup.html"
RequireLogin="true"
/>
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
//
// GLOBAL scope -- variables and methods that may be required from other scripts outside this widget
//
// load some global script pre-requisites
$(function () {
LoadScript(SITE_PATHS.application + '/Site/Shop/js/geographicData.js');
LoadScript(SITE_PATHS.application + '/js/jquery-creditcardvalidator/jquery.creditCardValidator.js');
});
var PaymentPopup = null,
PaymentForm = null,
EditAmount = null,
InvoiceTotal = 0,
Payment = {
Amount: 0,
Reference: 'Payment on Account',
Number: '',
CClastFour: '',
Type: '',
Verification: '',
Month: '',
Year: '',
Address1: '',
Address2: '',
City: '',
State: '',
ZipCode: '',
Country: '',
FirstName: '',
LastName: '',
Email: '',
Telephone: '',
BankAccountType: '',
BankTypeOfAccount: '',
SelectedPaymentType: '',
BaNameOnAccount1: '',
BaNameOnAccount2: '',
BaRoutingNumber: '',
BaAccountNumber: '',
AccountProcessedNumber: '',
AccountProcessedNameFirst: '',
AccountProcessedNameLast: '',
BaEmailOnAccount: '',
AddedInvoicePaymentFee: { Amount: 0.00, PaymentMethod_ID: '', Description: '' }
},
Years = GetYears(),
Months = ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12'];
var DisclaimerText = 'By checking the "I Agree" checkbox below, you authorize the information you ve provided on the above account to be used for the creation of a charge to the account listed above. You also affirm that the information you provided is correct, that you are a signer on the account above and there are available funds to cover the amount of any transactions that you authorize. If your payment cannot be completed for any reason, including insufficient funds or error in the information which you submitted, you will retain the same liability, which is your sole responsibility, for payment as though you had not attempted to make the payment.';
///-------------------------------------------------------------------------------------------------
/// <summary>
/// Shows the PaymentPopup.
/// </summary>
///-------------------------------------------------------------------------------------------------
function ShowPaymentPopup(amount, allowEditPayment) {
Payment.Amount = amount;
InvoiceTotal = amount;
EditAmount = allowEditPayment;
if (PaymentPopup) {
$('.PaymentPopup').remove();
}
if (PreferredPaymentType !== 'BA' && PreferredPaymentType !== 'CC') {
return;
}
var $paymentPopupContainer = $('<div />')
.addClass('PaymentPopup')
.appendTo($('#divWrapper_PaymentPopup'));
PaymentPopup = $paymentPopupContainer.dxPopup({
contentTemplate: $('#tmplPaymentPopup'),
wrapperAttr: {
class: "paymentWrapper"
},
animation: {
show: {
type: 'slideIn',
duration: 600,
direction: 'top'
},
hide: {
type: 'slideOut',
duration: 500,
direction: 'top'
}
},
title: 'Payment Information',
showTitle: (widgetOptions.popupShowTitle != null)
? widgetOptions.popupShowTitle
: true,
showCloseButton: (widgetOptions.popupShowCloseButton != null)
? widgetOptions.popupShowCloseButton
: true,
closeOnOutsideClick: (widgetOptions.popupCloseOnOutsideClick != null)
? widgetOptions.popupCloseOnOutsideClick
: true,
width: widgetOptions.popupWidth || null,
height: widgetOptions.popupHeight || null,
minWidth: widgetOptions.popupMinWidth || null,
minHeight: widgetOptions.popupMinHeight || 700,
maxWidth: widgetOptions.popupMaxWidth || 850,
maxHeight: widgetOptions.popupMaxHeight || 800,
dragEnabled: (widgetOptions.popupEnableDrag != null)
? widgetOptions.popupEnableDrag
: true,
resizeEnabled: (widgetOptions.popupEnableResize != null)
? widgetOptions.popupEnableResize
: true,
shading: (widgetOptions.popupModal != null)
? widgetOptions.popupModal
: false,
position: 'center center',
onShowing: function (e) {
BuildPaymentForm();
},
onHiding: function (e) {
$('#confirmationText').remove;
InvoiceGrid.deselectAll();
}
}).dxPopup("instance");
PaymentPopup.show();
};
///-------------------------------------------------------------------------------------------------
/// <summary>
/// Complete clears the payment popup form.
/// </summary>
///-------------------------------------------------------------------------------------------------
function ClearPaymentForm() {
$('#divWrapper_PaymentForm').dxForm('dispose');
PaymentForm.resetValues;
}
///-------------------------------------------------------------------------------------------------
/// <summary>
/// returns an Array of States based on the countryCode.
/// </summary>
///
/// <param name='countryCode'>
/// countryCode inthis case currency CountyCode
/// </param>
///
/// <returns>
/// returns an Array of States
/// </returns>
///-------------------------------------------------------------------------------------------------
function GetStateStoreByCountry(CountryCode) {
try {
var statesArr;
var vCountries = [];
var vCountries = _CountryStore._array.filter(getVisibleCountries => getVisibleCountries.UseCountry == true);
if (!vCountries.some(item => item.CountryCode === CountryCode.toUpperCase())) {
statesArr = [];
return statesArr;
}
if (CountryCode === null || CountryCode == "undefined" || CountryCode === "") {
statesArr = [];
return statesArr;
}
var c = GetObjectFromArrayByKeyValue(_CountryStore._array, 'CountryCode', CountryCode.toUpperCase());
if (c[0].States.length > 0) {
allStatesArr = c[0].States;
statesArr = allStatesArr.filter(getvisibleStates => getvisibleStates.UseState == true);
} else {
statesArr = [];
}
return statesArr;
} catch (err) {
statesArr = [];
return statesArr;
}
}
///-------------------------------------------------------------------------------------------------
/// <summary>
/// Builds payment form.
/// </summary>
///-------------------------------------------------------------------------------------------------
function BuildPaymentForm() {
PaymentForm = null;
var ValidationMsg = '';
if (PreferredPaymentType !== 'BA' && PreferredPaymentType !== 'CC') {
return;
}
PaymentForm = $('#divWrapper_PaymentForm').dxForm({
validationGroup: 'paymentInfoValidation',
items: [
{//SECTION 1
itemType: 'group',
cssClass: 'inputRow',
name: 'section1',
colCount: 1,
items: [
{
// If only 1 invoice is selected then the payment amount can not exceed the total due for that invoice
dataField: 'amount',
validationRules: [{
type: 'required',
message: 'The payment ammount is required.'
},
{
type: "custom",
reevaluate: true,
validationCallback: function (e) {
var realVal = e.value.replace('$', '');
//remove unneeded string chars so the value can be compaired to 0
realVal = Number(realVal.replace(/[^0-9\.]+/g, ""));
return (realVal > 0);
},
message: "The payment amount must be greater than $1.00."
}
],
editorOptions: {
value: FormatCurrency(Payment.Amount),
// read only if it can't be edited
readOnly: !EditAmount,
onChange: function (e) {
// Get number for compare
var inputVal = e.component._options._optionManager._options.value.replace('$', '');
inputVal = inputVal.replace(',', '');
inputVal = Number(inputVal.replace(/[^0-9\.]+/g, ""));
//if inputVal <= Payment.Amount then update the payment amount
if (inputVal <= InvoiceTotal) {
Payment.Amount = inputVal;
}
// if input is larger than actual owed then revert to actual owed no change to Payment.Amount
if (inputVal > InvoiceTotal) {
Payment.Amount = InvoiceTotal;
}
if (InvoicesToPay.length == 1) {
InvoicesToPay[0].AmountToPay = Payment.Amount;
}
},
onFocusOut: function (e) {
e.component.option('value', FormatCurrency(Payment.Amount));
checkForProcessingFee(Payment);
}
}
},
//Display fee if needed for InvoicePaymentFee
{
editorType: 'dxTextBox',
name: 'FeeBox',
visible: false,
label: {
location: 'left'
},
editorOptions: {
}
},
{
editorType: 'dxTextBox',
label: {
location: 'left',
text: 'Reference',
},
editorOptions: {
readOnly: true,
name: 'Reference',
value: function (e) {
var invoiceNumber = InvoicesToPay.map((item) => { return item.Invoice })
var invoiceNumberStr = invoiceNumber.join();
return invoiceNumberStr;
},
}
}
]
},
{//SECTION 2
itemType: 'group',
name: 'PayByCredit',
colCount: 1,
items: [{
itemType: 'group',
cssClass: 'inputRow',
name: 'section2',
colCount: 1,
items: [
{
dataField: 'CardHolder',
editorType: 'dxTextBox',
label: {
location: 'left'
},
validationRules: [{ type: 'required', message: 'The name of the card holder is required.' }],
editorOptions: {
value: (Payment.FirstName + ' ' + Payment.LastName).trim(),
onValueChanged: function (e) {
Payment.FirstName = e.value.substring(0, e.value.indexOf(' ')).trim();
Payment.LastName = e.value.substring(Payment.FirstName.length).trim();
}
}
},
{
dataField: 'Email',
editorType: 'dxTextBox',
label: {
location: 'left',
text: 'Receipt Email'
},
validationRules: [{
type: 'required',
message: 'The email address is required.'
},
{
type: 'email',
message: 'The email address is not valid.'
}
],
editorOptions: {
value: Payment.Email,
onValueChanged: function (e) {
Payment.Email = e.value;
}
}
},
{
dataField: 'Address1',
editorType: 'dxTextBox',
validationRules: [{
type: 'required', message: 'The address is required.'
}],
label: {
location: 'top',
text: 'Address'
},
editorOptions: {
value: Payment.Address1,
onValueChanged: function (e) {
Payment.Address1 = e.value;
},
}
},
{
dataField: 'Address2',
editorType: 'dxTextBox',
label: {
visible: false,
},
editorOptions: {
value: Payment.Address2,
onValueChanged: function (e) {
Payment.Address2 = e.value;
},
}
}
]
},
{//SECTION 3 group of items ,city,state,zip
itemType: 'group',
name: 'section3',
cssClass: 'inputRow',
colCount: 3,
items: [
{
dataField: 'City',
editorType: 'dxTextBox',
validationRules: [{ type: 'required', message: 'The city is required.' }
],
label: {
location: 'top',
text: 'City'
},
editorOptions: {
value: Payment.City,
onValueChanged: function (e) {
Payment.City = e.value;
},
}
},
{
dataField: 'State',
editorType: 'dxSelectBox',
validationRules: [],
label: {
location: 'top',
text: 'State'
},
editorOptions: {
dataSource: {
store: GetStateStoreByCountry(_Currency.CountryCode),
pageSize: 5,
paginate: true,
},
elementAttr: {
id: 'stateSelectId',
},
inputAttr: {
autocomplete: 'test-autocomplete'
},
displayExpr: 'StateName',
valueExpr: 'StateCode',
searchEnabled: true,
onValueChanged: function (e) {
Payment.State = e.value;
},
onInitialized: function (e) {
e.component.option('value', Payment.State);
}
}
},
{
dataField: 'Zip',
editorType: 'dxTextBox',
validationRules: [{ type: 'required', message: 'The zip code is required.' },
{ type: 'stringLength', min: 4, message: 'The zip code must be at least 4 digits long.' }
],
label: {
location: 'top',
text: 'Zip'
},
editorOptions: {
value: Payment.ZipCode,
onValueChanged: function (e) {
Payment.ZipCode = e.value;
},
}
},
]
},
{//SECTION 4 group of items country, phone
itemType: 'group',
name: 'section4',
cssClass: 'inputRow',
colCount: 2,
items: [{
dataField: 'Country',
editorType: 'dxSelectBox',
validationRules: [{ type: 'required', message: 'The country is required.' }],
label: {
location: 'top',
text: 'Country'
},
editorOptions: {
dataSource: {
store: _CountryStore._array.filter(getVisibleCountries => getVisibleCountries.UseCountry == true),
pageSize: 5,
paginate: true,
},
inputAttr: {
autocomplete: 'test-autocomplete'
},
displayExpr: 'CountryName',
valueExpr: 'CountryCode',
searchEnabled: true,
onValueChanged: function (e) {
Payment.Country = e.value;
PaymentForm.getEditor('State').reset();
PaymentForm.getEditor('State').option('dataSource', GetStateStoreByCountry(e.value));
},
onInitialized: function (e) {
var c = GetObjectFromArrayByKeyValue(_CountryStore._array, 'CountryCode', _Currency.CountryCode.toUpperCase());
e.component.option('value', c[0].CountryCode.toUpperCase());
Payment.Country = c[0].CountryCode.toUpperCase();
}
}
},
{
dataField: 'Phone',
editorType: 'dxTextBox',
validationRules: [{ type: 'required', message: 'The telephone number is required.' },
{ type: 'stringLength', min: 10, message: 'Phone number must be 10 or more digits long.' }
],
label: {
location: 'top',
text: 'Phone'
},
editorOptions: {
maskChar: ' ',
mask: '(###) ###-####',
maskRules: {
'#': /[01-9]/
},
maskInvalidMessage: 'Phone number must be 10 or more digits long.',
useMaskedValue: false,
value: Payment.Telephone,
onValueChanged: function (e) {
Payment.Telephone = e.value;
},
}
}]
},
{//SECTION 5 Card number
itemType: 'group',
name: 'section5',
colCount: 1,
items: [{
dataField: 'CardNumber',
editorType: 'dxTextBox',
label: {
location: 'left'
},
editorOptions: {
showClearButton: true,
placeholder: '0000 0000 0000 0000',
elementAttr: {
id: 'txtCreditCardNumber',
class: 'card'
},
onValueChanged: function (e) {
Payment.Number = e.value;
if (e.value.length > 3) {
Payment.CClastFour = e.value.substring(e.value.length - 4);
} else {
Payment.CClastFour = 'XXXX';
}
}
},
}]
},
{//SECTION 6 group of items month year cvn
itemType: 'group',
name: 'section6',
colCount: 3,
items: [{
dataField: 'CardMonth',
editorType: 'dxAutocomplete',
validationRules: [{
type: 'required',
message: 'The expiration month is required.'
},
{
type: 'custom',
reevaluate: true,
validationCallback: function (e) {
return _.contains(Months, e.value);
},
message: 'The expiration month must be between 01 and 12.'
}
],
label: {
name: 'monthLbl',
location: 'left',
text: 'Expiration'
},
editorOptions: {
dataSource: Months,
placeholder: 'MM',
searchEnabled: true,
maxLength: 2,
onValueChanged: function (e) {
Payment.Month = e.value;
}
}
},
{
dataField: 'CardYear',
editorType: 'dxAutocomplete',
validationRules: [{
type: 'required',
message: 'The expiration year is required.'
},
{
type: 'custom',
reevaluate: true,
validationCallback: function (e) {
if (isNaN(e.value)) {
return false;
}
return _.contains(Years, parseInt(e.value));
},
message: 'The expiration year is invalid.'
}],
label: {
location: 'left',
text: "/",
showColon: false,
},
editorOptions: {
items: Years,
placeholder: 'YY',
searchEnabled: true,
maxLength: 2,
onValueChanged: function (e) {
Payment.Year = e.value;
}
}
},
{
dataField: 'CVN',
editorType: 'dxTextBox',
validationRules: [{ type: 'required', message: 'The CVN is required.' }],
label: {
location: 'left'
},
editorOptions: {
placeholder: 'XXX',
maskChar: 'X',
mask: '000',
onValueChanged: function (e) {
Payment.Verification = e.value;
}
}
}],
}
]
},
{//SECTION 7 - Bank Fields
itemType: 'group',
name: 'PayByBank',
colCount: 1,
items: [{
itemType: 'group',
name: 'bankFields',
colCount: 1,
items: [
{
dataField: 'AccountType',
editorType: 'dxRadioGroup',
label: {
location: 'left',
},
validationRules: [{ type: 'required', message: 'The account type is required. Business or Personal' }],
editorOptions: {
dataSource: ['Business', 'Personal'],
layout: 'horizontal',
onValueChanged: function (e) {
Payment.BankTypeOfAccount = e.value.toUpperCase();
},
},
},
{
dataField: 'NameOnAccount',
editorType: 'dxTextBox',
label: {
location: 'left'
},
validationRules: [{
type: 'required', message: 'The name on the account is required.'
}],
editorOptions: {
value: (Payment.BaNameOnAccount1 + ' ' + Payment.BaNameOnAccount2).trim(),
onValueChanged: function (e) {
Payment.BaNameOnAccount1 = e.value.substring(0, e.value.indexOf(' ')).trim();
Payment.BaNameOnAccount2 = e.value.substring(Payment.BaNameOnAccount1.length).trim();
}
}
},
{
dataField: 'EmailOnAccount',
editorType: 'dxTextBox',
label: {
location: 'left',
text: 'Receipt Email'
},
validationRules: [{ type: 'required', message: 'The email address is required' }],
editorOptions: {
value: Payment.Email,
onValueChanged: function (e) {
Payment.BaEmailOnAccount = e.value;
}
}
},
{
dataField: 'BankingType',
editorType: 'dxRadioGroup',
label: {
location: 'left',
},
validationRules: [{ type: 'required', message: 'The banking type is required. Checking or Savings' }],
editorOptions: {
dataSource: ['Checking', 'Savings'],
layout: 'horizontal',
onValueChanged: function (e) {
Payment.BankAccountType = e.value.toUpperCase();
},
},
},
{
dataField: 'routingNumber',
editorType: 'dxTextBox',
label: {
location: 'left'
},
validationRules: [{ type: 'required', message: 'The routing number is required.' }],
editorOptions: {
onValueChanged: function (e) {
Payment.BaRoutingNumber = e.value;
}
}
},
{
dataField: 'accountNumber',
editorType: 'dxTextBox',
label: {
location: 'left'
},
validationRules: [{ type: 'required', message: 'The account number is required.' }],
editorOptions: {
onValueChanged: function (e) {
Payment.BaAccountNumber = e.value;
}
}
},
{
dataField: 'confirmAccountNumber',
editorType: 'dxTextBox',
label: {
location: 'left'
},
validationRules: [
{
type: "compare", comparisonTarget: function () {
var bankAccountNumber = PaymentForm.getEditor('accountNumber');
if (bankAccountNumber) {
return bankAccountNumber.option("value");
}
}, message: "'Account Number' and 'Confirm Account Number' do not match."
},
{
type: 'required', message: 'Please confirm the account number.'
}],
editorOptions: {
onValueChanged: function (e) {
}
}
},
{
dataField: "picture",
template: function (data, itemElement) {
itemElement.append("<img src='/ecommerce/images/check.jpg'>");
},
label: {
location: 'left',
visible: false
},
}],
},
{//SECTION disclaimer section
itemType: 'group',
name: 'disclaimer',
colCount: 1,
items: [
{
editorType: 'dxTextArea',
editorOptions: {
value: DisclaimerText,
readOnly: true,
height: 90
}
},
{
dataField: "Accepted",
editorType: 'dxCheckBox',
label: {
visible: false
},
editorOptions: {
text: 'I Agree',
onValueChanged: function (e) {
}
}
, validationRules: [{
type: "compare",
comparisonTarget: function () { return true; },
message: "You must agree to the Terms and Conditions"
}]
}]
}
]
},
///////////////////////////
{//SECTION btnSection
itemType: 'group',
cssClass: "btngroup",
name: 'btnSection',
direction: 'row',
// colCount: 2,
items: [{
itemType: 'button',
cssClass: 'paymentSubmit',
name: 'subBtn',
validationGroup: 'paymentInfoValidation',
// horizontalAlignment: 'left',
buttonOptions: {
text: 'Submit Payment',
type: 'default',
useSubmitBehavior: false,
elementAttr: {
style: 'margin-top:30px;'
},
onClick: function (e) {
ValidationMsg = '';
var result = e.validationGroup.validate();
if (result) {
//if the form validates and the fields are valid then process the payment
if (result.isValid) {
var msg = '';
var paymentEmail = Payment.Email;
if (Payment.SelectedPaymentType == 'CC') {
Payment.AccountProcessedNumber = Payment.Number;
Payment.AccountProcessedNameFirst = Payment.FirstName;
Payment.AccountProcessedNameLast = Payment.LastName;
if (Payment.AddedInvoicePaymentFee.Amount > 0) {
msg = String.Format('Do you want to proceed with a charge to your card ending {0} in the amount of {1} <br> plus a {2} of {3} as payment to your open account balance.', Payment.CClastFour, FormatCurrency(Payment.Amount), Payment.AddedInvoicePaymentFee.Description, FormatCurrency(Payment.AddedInvoicePaymentFee.Amount));
}
if (Payment.AddedInvoicePaymentFee.Amount === 0) {
msg = String.Format('Do you want to proceed with a charge to your card ending {0} in the amount of {1} as payment to your open account balance.', Payment.CClastFour, FormatCurrency(Payment.Amount));
}
}
if (Payment.SelectedPaymentType == 'BA') {
// {RoutingNumber}::{AccountNumber}::{HolderType}::{AccountType} e.g. 123456789::0000987654321::BUSINESS::SAVINGS
Payment.AccountProcessedNumber = String.Format('{0}::{1}::{2}::{3}', Payment.BaRoutingNumber, Payment.BaAccountNumber, Payment.BankTypeOfAccount, Payment.BankAccountType);
Payment.AccountProcessedNameFirst = Payment.BaNameOnAccount1;
Payment.AccountProcessedNameLast = Payment.BaNameOnAccount2;
paymentEmail = Payment.BaEmailOnAccount;
msg = String.Format('Do you want to proceed with a payment from your {0} account in the amount of {1} as payment to your open account balance.', Payment.BankAccountType, FormatCurrency(Payment.Amount));
}
var dialog = DevExpress.ui.dialog.confirm(msg, 'Confirm Payment');
dialog.done(function (confirmed) {
if (confirmed) {
// user clicked OK so go ahead and process the payment, otherwise, do nothing.
let options = {};
if (Payment.AddedInvoicePaymentFee.Amount > 0) {
options.PaymentFee = Payment.AddedInvoicePaymentFee.Amount;
options.PaymentMethod_ID = Payment.AddedInvoicePaymentFee.PaymentMethod_ID ?? '';
}
PaymentForm.option('disabled', true);
BillingLoadPanel.option('message', 'Processing Transaction...');
BillingLoadPanel.show();
MakeAJAXCall("Billing.PayCustomerInvoices", {
InvoicesToPay: InvoicesToPay,
PaymentAmount: Payment.Amount,
CustomerReference: '',
CreditCardNumber: Payment.AccountProcessedNumber,
CreditCardType: Payment.Type,
CreditCardVerification: Payment.Verification,
CreditCardExpiration: Payment.Month + '/20' + Payment.Year,
CreditCardAddress: (Payment.Address1 + ' ' + Payment.Address2).trim(),
CreditCardCity: Payment.City,
CreditCardState: Payment.State,
CreditCardZipCode: Payment.ZipCode,
CreditCardCountry: Payment.Country,
CreditCardFirstName: Payment.AccountProcessedNameFirst,
CreditCardLastName: Payment.AccountProcessedNameLast,
Email: paymentEmail,
Telephone: Payment.Telephone,
//TODO Confirm Options
Options: options
}, function (DATA) {
if (ValidAJAXResponse(DATA)) {
var response = JSON.parse(DATA);
if (response.Result.Success) {
BillingLoadPanel.hide();
PaymentPopup.hide();
ClearPaymentForm()
var alertResult = DevExpress.ui.dialog.alert(String.Format('{0} Your transaction reference number is {1}.', response.Result.Message, response.Data.TransactionID), 'Payment Successful');
alertResult.done(function () {
BillingLoadPanel.option('message', 'Updating invoice history...');
BillingLoadPanel.show();
LoadAccountDetails();
});
} else {
setTimeout(function () {
PaymentForm.option('disabled', false);
DevExpress.ui.dialog.alert(response.Result.Message, 'Payment Error');
BillingLoadPanel.hide();
}, 500);
}
}
});
}
})
}
else { // form input is not valid tell user
$.each(result.brokenRules, function () {
ValidationMsg += '<p>' + this.message + '</p>';
});
alertResult = DevExpress.ui.dialog.alert(ValidationMsg,
'Missing Information');
}
}
}
}
},
{
itemType: 'button',
// horizontalAlignment: 'right',
cssClass: 'paymentCancel',
buttonOptions: {
text: 'Cancel',
stylingMode: 'text',
type: 'danger',
useSubmitBehavior: false,
icon: 'clear',
elementAttr: {
style: 'margin-top:30px;flex-direction:row;'
},
onClick: function () {
PaymentPopup.hide();
ClearPaymentForm();
}
}
}]
}],
}).dxForm('instance');
$("#btnPayByCC").dxButton({
text: "Pay with Credit Card",
visible: IsPaymentBtnVisible(PaymentTypes, 'CC'),
type: 'danger',
onClick: function (e) {
PayWithCreditCard();
},
onContentReady: function (e) {
},
onInitialized: function (e) {
if (PreferredPaymentType == 'CC') {
PayWithCreditCard();
}
}
});
$("#btnPayByBank").dxButton({
visible: IsPaymentBtnVisible(PaymentTypes, 'BA'),
text: "Pay with Bank Account",
type: 'danger',
onClick: function (e) {
PayWithBankAccount();
},
onInitialized: function (e) {
if (PreferredPaymentType == 'BA') {
PayWithBankAccount();
}
}
});
}
///-------------------------------------------------------------------------------------------------
/// <summary>
/// Is the payment type button visable.
/// </summary>
///-------------------------------------------------------------------------------------------------
function IsPaymentBtnVisible(PaymentTypes, btnString) {
for (let i = 0; i < PaymentTypes.length; i++) {
if (PaymentTypes[i].MethodType == btnString) {
return true;
}
}
return false;
}
/**================================================================================================
** checkForProcessingFee
*? gets the Fee data. Add Fee data to Payment{} ?
*@param Payment JSON
*@return JSON in displayProcessingFee()
*================================================================================================**/
function checkForProcessingFee({ Amount, SelectedPaymentType }) {
try {
const Found = PaymentTypes.find(e => e.MethodType === SelectedPaymentType);
if (!_.isEmpty(Found.InvoicePaymentFee)) {
MakeAJAXCall("Billing.GetInvoicePaymentFee", { paymentAmount: Amount, paymentMethod_ID: Found.PaymentMethod_ID },
function (DATA) {
if (ValidAJAXResponse(DATA)) {
let data = JSON.parse(DATA);
if (data.Result.Success) {
Payment.AddedInvoicePaymentFee.Amount = data?.Data?.InvoicePaymentFee?.Amount ?? 0;
Payment.AddedInvoicePaymentFee.PaymentMethod_ID = Found.PaymentMethod_ID ?? '';
Payment.AddedInvoicePaymentFee.Description = data?.Data?.InvoicePaymentFee?.Description ?? '';
displayProcessingFee(data?.Data?.InvoicePaymentFee);
}
if (!data.Result.Success) {
console.log(data?.Data?.Result?.Message ?? 'Ajax call Billing.GetInvoicePaymentFee returned false with no Result.Message')
}
}
}
)
}
if (_.isEmpty(Found.InvoicePaymentFee)) {
//NOTE hide servfield display amount
Payment.AddedInvoicePaymentFee.Amount = 0.00;
Payment.AddedInvoicePaymentFee.PaymentMethod_ID = '';
Payment.AddedInvoicePaymentFee.Description = '';
PaymentForm.itemOption('section1.FeeBox', { visible: false });
}
} catch (error) {
console.log(error)
}
}
/**================================================================================================
** displayProcessingFee
*? shows or hides the Processing fee on the payment popup. Set defaults { 0 & ''}?
*@param InvoicePaymentFee JSON
*================================================================================================**/
function displayProcessingFee({ Amount = 0.00, Description = 'Processing Fee' } = {}) {
PaymentForm.itemOption('section1.FeeBox', { label: { text: Description } });
PaymentForm.itemOption('section1.FeeBox', { editorOptions: { value: FormatCurrency(Amount) } });
PaymentForm.itemOption('section1.FeeBox', { visible: true });
PaymentForm.itemOption('section1.amount', { editorOptions: { value: FormatCurrency(Payment.Amount) } });
PaymentForm.itemOption('section1.FeeBox', { editorOptions: { readOnly: true } });
}
///-------------------------------------------------------------------------------------------------
/// <summary>
/// Pay with bank account.
/// </summary>
///-------------------------------------------------------------------------------------------------
function PayWithBankAccount() {
Payment.SelectedPaymentType = 'BA';
checkForProcessingFee(Payment);
$('#btnPayByBank').addClass('active');
$('#btnPayByCC').removeClass('active');
//Show correct fields
PaymentForm.itemOption('PayByBank', { visible: true });
PaymentForm.itemOption('PayByCredit', { visible: false });
}
///-------------------------------------------------------------------------------------------------
/// <summary>
/// Pay with credit card.
/// </summary>
///-------------------------------------------------------------------------------------------------
function PayWithCreditCard() {
Payment.SelectedPaymentType = 'CC';
checkForProcessingFee(Payment);
$('#btnPayByCC').addClass('active');
$('#btnPayByBank').removeClass('active');
//Show the correct fields
PaymentForm.itemOption('PayByCredit', { visible: true });
//Remove the unneeded fields
PaymentForm.itemOption('PayByBank', { visible: false });
// get some editors
var ccEditor = PaymentForm.getEditor('CardNumber');
var cvnEditor = PaymentForm.getEditor('CVN');
ccInput = $('#txtCreditCardNumber').find('input');
// Setup the credit card pre-submit validation using the jquery-creditcardvalidator
ccInput.validateCreditCard(function (result) {
if (result) {
if (result.card_type !== null && result.card_type !== '') {
var val = this.val();
Payment.Type = (result.card_type.name == 'amex') ? 'American Express' : result.card_type.name;
ccInput.addClass(result.card_type.name);
ccEditor.option("isValid", result.valid);
if (result.valid) {
var newVal = val.replace(/\s+/g, '');
ccInput.addClass('valid');
if (result.card_type.name == 'visa') {
if (val.length == 16) {
ccEditor.option('mask', '0000 0000 0000 0000');
}
if (val.length == 13) {
ccEditor.option('mask', '0000 0000 000 00');
}
cvnEditor.option({
placeholder: 'XXX',
maskChar: 'X',
mask: '000'
});
}
if (result.card_type.name == 'amex') {
ccEditor.option('mask', '0000 000000 00000');
cvnEditor.option({
placeholder: 'XXXX',
maskChar: 'X',
mask: '0000'
});
}
if (result.card_type.name == 'mastercard') {
ccEditor.option('mask', '0000 0000 0000 0000');
cvnEditor.option({
placeholder: 'XXX',
maskChar: 'X',
mask: '000'
});
}
if (result.card_type.name == 'discover') {
ccEditor.option('mask', '0000 0000 0000 0000');
cvnEditor.option({
placeholder: 'XXX',
maskChar: 'X',
mask: '000'
});
}
if (result.card_type.name !== 'amex' && result.card_type.name !== 'visa'
&& result.card_type.name !== 'mastercard' && result.card_type.name !== 'discover') {
ccEditor.option('mask', '');
cvnEditor.option({
maskChar: 'X',
mask: '000'
});
}
var newVal = val.replace(/\s+/g, '');
ccEditor.option('value', newVal);
//move cursor to end of txt field after format change
var ccField = $('#txtCreditCardNumber').dxTextBox("instance");
var ccFieldEl = ccField.element();
var ccBaseInput = ccFieldEl[0].attributes[1].ownerElement.firstChild.children[0].children[0];
ccField.focus()
MoveCursorToPos(ccBaseInput, ccField._textValue.length);
}
if (!result.valid) {
ccInput.removeClass('valid');
ccEditor.option('mask', '');
cvnEditor.option({
placeholder: '000',
maskChar: 'X',
mask: '000'
});
}
}
else {
ccInput.removeClass(['visa', 'mastercard', 'amex', 'discover']);
}
// set txt to val
if (typeof val === 'undefined') {
ccEditor.option('value', '');
}
}
else {
ccInput.removeClass(['visa', 'mastercard', 'amex', 'discover']);
}
},
{
accept: ['visa', 'mastercard', 'amex', 'discover']
}
);
}
</script>
<script type="text/html" id="tmplPaymentPopup">
<div id='divWrapper_PaymentMethods'>
<div id='btnPayByBank'></div>
<div id='btnPayByCC'></div>
</div>
<div id="divWrapper_PaymentForm"></div>
</script>