DigitalConsumer.ISS/src/layouts/payment-page/payment-page.vue
2024-02-20 14:21:05 -05:00

495 lines
19 KiB
Vue

<template>
<Form
ref="theForm"
v-slot="{ meta }"
@submit="onSubmit"
@invalidSubmit="onInvalidSubmit">
<div class="page-container-grouped-styles">
<div class="fade-on-route-transition position-relative">
<siteHeader cmsWidgetName="SiteHeaderWidget" />
<div class="container-fluid pb-2 payment">
<div id="card-container">
<iframe
id="card-frame"
ref="paymentFrame"
v-resize
class="hop-iframe payment-iframe"
name="card-frame"
seamless
scrolling="no">
</iframe>
</div>
<siteFooter
ref="siteFooter"
cmsWidgetName="SiteFooterWidget"
:isForwardActionDisabled="true"
isForwardButtonHidden="true"
@ForwardClicked="forwardButtonAction"
@backClicked="backButtonAction" />
</div>
</div>
<div
v-if="shouldBlockInteraction"
class="ui-block"
@click="supressClick"></div>
</div>
</Form>
<form
ref="hopForm"
id="card-data"
:target="isPaypal ? '_top' : 'card-frame'"
method="POST"
:action="checkoutUrl">
<input type="hidden" name="paymentType" :value="paymentType" />
<input type="hidden" name="sgSessionId" :value="authToken" />
<input type="hidden" name="sgAuthToken" :value="authToken" />
<input type="hidden" name="sgSignaturePublic" :value="authSignature" />
<input type="hidden" name="sgSignatureStartDate" :value="authSignatureStart" />
<input type="hidden" name="referralSeqNum" :value="referralSequenceNumber" />
<input type="hidden" name="sge_commerce_indicator_isinternet" value="true" />
<input type="hidden" name="sghopsource" value="Safelite.com" />
<input type="hidden" name="sgHtmlStyle" value="ResourceSafeliteHtml" />
<input type="hidden" name="sgErrorMessagesEmbedded" value="true" />
<input type="hidden" name="sgNoKeystrokeProcessing" value="false" />
<input type="hidden" name="maskCharacter" value="*" />
<input type="hidden" name="styleSheetCode" :value="dynamicCSSUrl" />
<input type="hidden" name="styleSheetCode2" :value="dynamicHopCSSUrl" />
<input type="hidden" name="sgReceiptResponseURL" :value="payInAdvanceResponseUrl" />
<input type="hidden" name="sgDeclineResponseURL" :value="payInAdvanceResponseUrl" />
<input type="hidden" name="sgErrorResponseURL" :value="payInAdvanceResponseUrl" />
<input type="hidden" name="sgheaderline1" :value="getHeaderLine1" />
<input type="hidden" name="sgHeader1subtitle" value="" />
<input type="hidden" name="sgheaderline2" :value="getHeaderLine2" />
<input type="hidden" name="sgheaderline3" value="" />
<input type="hidden" name="sgheaderline4" value="" />
<input type="hidden" name="sgheaderline5" value="*Required information" />
<input type="hidden" name="billTo_firstName" :value="firstName" />
<input type="hidden" name="billTo_firstNameShow" value="true" />
<input type="hidden" name="sghopmiddleInitialShow" value="false" />
<input type="hidden" name="billTo_lastName" :value="lastName" />
<input type="hidden" name="billTo_lastNameShow" value="true" />
<input type="hidden" name="billTo_street1" value="" />
<input type="hidden" name="billTo_street2" value="" />
<input type="hidden" name="billTo_city" value="" />
<input type="hidden" name="billTo_state" value="" />
<input type="hidden" name="billTo_postalCode" value="" />
<input type="hidden" name="billTo_postalCodeShow" value="true" />
<input type="hidden" name="billTo_postalCodeEnable" value="true" />
<input type="hidden" name="sgLabelPostalCode" value="Billing ZIP" />
<input type="hidden" name="sgtotalamount" :value="displayAmount" />
<input type="hidden" name="totalAmountDecimal" :value="totalAmount" />
<input type="hidden" name="lineItems" :value="payInAdvanceLineItems" />
<input type="hidden" name="sgdiscountstrikethruamount" value="" />
<input type="hidden" name="callerDisplayText" value="" />
<input
type="hidden"
name="sgtermsofusedisplaytext"
value="By selecting submit, I agree to Safelite's" />
<input type="hidden" name="sgtermsofuseurl" value="http://www.safelite.com/terms-of-use/" />
<input type="hidden" name="sgtermsofuselinkdisplaytext" value="terms of use" />
<input type="hidden" name="sgtermsofcancelrefunddisplaytext" value="and" />
<input
type="hidden"
name="sgtermsofcancelrefundlinkdisplaytext"
value="cancellation and refund policy" />
<input
type="hidden"
name="sgtermsofcancelrefundurl"
value="http://www.safelite.com/cancellation-refund-policy" />
<input
type="hidden"
name="sgErrorMessageCardVerification"
value="Credit card could not be processed" />
<input
type="hidden"
name="sgErrorMessageTimeOut"
value="For your security, this transaction has been timed out." />
<input type="hidden" name="sgLabelCardNumber" value="" />
<input type="hidden" name="sgLabelAddressLine1" :value="address1" />
<input type="hidden" name="sgLabelAddressLine2" :value="address2" />
<input type="hidden" name="sgLabelCity" :value="city" />
<input type="hidden" name="sgLabelState" :value="state" />
<input type="hidden" name="sgCtu" :value="ctu" />
<input type="hidden" name="sgWorkOrder" :value="workOrderNumber" />
<input type="hidden" name="sgEmailAddress" :value="emailAddress" />
<input type="hidden" name="paypalInvoiceNumber" :value="invoiceNumber" />
<input type="hidden" name="paypalSuccessUrl" :value="payInAdvanceResponseUrl" />
<input type="hidden" name="paypalCancelUrl" :value="payInAdvanceCancelUrl" />
<input type="hidden" name="sgCCDeclineURL" :value="payInAdvanceCancelUrl" />
<input type="hidden" name="sgCCTimeoutURL" :value="payInAdvanceCancelUrl" />
<input type="hidden" name="sgTransactionType" value="authorization" />
<input type="hidden" name="amount" :value="totalAmount" />
<input type="hidden" name="ctu" :value="ctu" />
<input type="hidden" name="orderNumber" :value="workOrderNumber" />
<input type="hidden" name="billTo_email" :value="emailAddress" />
<input type="hidden" name="useDecisionManager" value="True" />
<input type="hidden" name="correlationId" :value="referralCorrelationId" />
<input type="hidden" name="sgErrorMessageCard_CVN" value="Please enter a valid CVV" />
<input type="hidden" name="ship_to_address_line1" :value="address1" />
<input type="hidden" name="ship_to_address_line2" :value="address2" />
<input type="hidden" name="ship_to_address_city" :value="city" />
<input type="hidden" name="ship_to_address_state" :value="state" />
<input type="hidden" name="ship_to_address_country" value="US" />
<input type="hidden" name="ship_to_address_postal_code" :value="zipCode" />
<input type="hidden" name="ship_to_phone" :value="phoneNumber" />
<input type="hidden" name="calling_application" value="NextGen" />
</form>
</template>
<script>
// Components
import siteHeader from '@/iss-components/site-header/site-header.vue';
import siteFooter from '@/iss-components/site-footer/site-footer.vue';
// Supporting files
import applicationConfig from '@/constants/application-config.js';
import { fetchCmsContentForPage } from '@/helpers/cms-content-helper';
import settleAllPromises from '@/helpers/layout-helper';
import { Form } from 'vee-validate';
import BaseFormMixin from '@/mixins/base-form-mixin.js';
import { useMainStore } from '@/store';
import iframeResize from '../../../node_modules/iframe-resizer/js/iframeResizer.js';
import externalUrls from '@/router/router-constants/externalUrl-values.js';
import widgetFields from '@/constants/cms-widget-fields.js';
import paymentMethods from '@/constants/payment-method-constants';
import { AppointmentTypeStrings } from '@/constants/schedule-constants';
export default {
name: 'payment-page',
components: {
siteHeader,
siteFooter,
// eslint-disable-next-line vue/no-reserved-component-names
Form
},
directives: {
resize: {
mounted(el) {
el.addEventListener('load', () => {
iframeResize({ checkOrigin: false }, el);
});
},
unmounted(el) {
el.iFrameResizer.removeListeners();
}
}
},
mixins: [BaseFormMixin],
async beforeRouteEnter(to, from, next) {
// Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
const paymentSignaturePromise = await useMainStore().getPaymentSignature();
// Settle promises and get results
const promiseResultMap = [
{
resultKey: 'cmsContent',
promise: cmsContentPromise
},
{
resultKey: 'paymentSignature',
promise: paymentSignaturePromise
}
];
// use resultMap to populate layout content.
const resultMap = await settleAllPromises(promiseResultMap);
next((vm) => {
vm.setCmsContent(resultMap.cmsContent);
vm.$nextTick(() => {
vm.fetchSignatureInfo(resultMap.paymentSignature);
vm.setIFrameListener();
});
});
},
setup() {
const mainStore = useMainStore();
return { mainStore };
},
data() {
return {
checkoutUrl: externalUrls.SAFELITE_HOP,
payInAdvanceResponseUrl: applicationConfig.PAY_IN_ADVANCE_RESPONSE_URL,
payInAdvanceCancelUrl: applicationConfig.PAY_IN_ADVANCE_CANCEL_URL,
paymentType: this.getPaymentType(),
authToken: '',
authSignature: '',
authSignatureStart: '',
referralSequenceNumber: useMainStore().order.referralSequenceNumber,
emailAddress: useMainStore().order.customer.emailAddress,
address1: this.getAddress1(),
address2: this.getAddress2(),
city: this.getCity(),
state: this.getState(),
zipCode: this.getZipCode(),
firstName: useMainStore().order.customer.firstName,
lastName: useMainStore().order.customer.lastName,
phoneNumber: useMainStore().order.customer.phoneNumber,
ctu: useMainStore().order.serviceLocation.zipCodeCtu,
referralCorrelationId: useMainStore().order.referralCorrelationId,
workOrderNumber: this.getWorkOrderNumber(),
invoiceNumber: this.getInvoiceNumber(),
totalAmount: this.getAmountDue(),
displayAmount: this.getDisplayAmountDue(),
payInAdvanceLineItems: '',
shouldBlockInteraction: false
};
},
computed: {
dynamicCSSUrl() {
const { protocol, hostname, port } = window.location;
return `${protocol}//${hostname}:${port}/css/site-2020Styling.css`;
},
dynamicHopCSSUrl() {
const { protocol, hostname, port } = window.location;
return `${protocol}//${hostname}:${port}/css/hop-styling.css`;
},
getHeaderLine1() {
if (!this.isPaypal) {
return this.getCmsContent('PayInAdvanceHeaderLine1', widgetFields.TEXT_BLOCK_WIDGET.TEXT);
}
return '';
},
getHeaderLine2() {
if (!this.isPaypal) {
return this.getCmsContent('PayInAdvanceHeaderLine2', widgetFields.TEXT_BLOCK_WIDGET.TEXT);
}
return '';
},
isPaypal() {
if (this.paymentType === 'pp') {
return true;
}
return false;
}
},
methods:
{
arePagePrerequisitesValid() {
// Line Items
const packageReqs = !!useMainStore().order.lineItems.supportingItems;
// Service Location
const { serviceLocation } = useMainStore().order;
const mobileReqs = !!(
serviceLocation.address
&& serviceLocation.city
&& serviceLocation.state
&& serviceLocation.zipCode
);
const providerLocation = serviceLocation.provider.address;
const dropOffInshopReqs = !!(
providerLocation.streetAddress
&& providerLocation.city
&& providerLocation.state
&& providerLocation.zipCode
);
const isMobile = serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE;
const serviceLocationReqs =
(isMobile && mobileReqs) || (!isMobile && dropOffInshopReqs);
// Insurance
const isInsuranceSet = useMainStore().order.payment.isInsurance !== null;
// Schedule
const { schedule } = useMainStore().order;
const scheduleReqs = !!(
schedule.date
&& schedule.startTime
&& schedule.endTime
&& schedule.jobMaxMinutes
&& schedule.jobMinMinutes
);
// Customer
const { customer } = useMainStore().order;
const customerReqs = !!(
customer.firstName
&& customer.lastName
&& customer.phoneNumber
&& customer.emailAddress
);
const paymentMethodReqs =
useMainStore().order.payment.isPayInAdvance !== null
&& (useMainStore().order.payment.isPayInAdvance || !!useMainStore().order.payment.payInAdvanceType);
return (
packageReqs
&& serviceLocationReqs
&& isInsuranceSet
&& scheduleReqs
&& customerReqs
&& paymentMethodReqs
);
},
getWorkOrderNumber() {
const { workOrderNumber } = useMainStore().order;
if (workOrderNumber) {
const items = workOrderNumber.split('-');
if (items.length > 1) {
return items[1];
}
}
//this.backButtonAction();
return undefined;
},
getInvoiceNumber() {
const { workOrderNumber } = useMainStore().order;
if (workOrderNumber) {
return workOrderNumber.replace('-', '');
}
//this.backButtonAction();
return undefined;
},
getAddress1() {
return useMainStore().order.serviceLocation.address
? useMainStore().order.serviceLocation.address
: useMainStore().order.serviceLocation.provider.address.streetAddress;
},
getAddress2() {
return useMainStore().order.serviceLocation.address2
? useMainStore().order.serviceLocation.address2
: null;
},
getCity() {
return useMainStore().order.serviceLocation.city
? useMainStore().order.serviceLocation.city
: useMainStore().order.serviceLocation.provider.address.city;
},
getState() {
return useMainStore().order.serviceLocation.state
? useMainStore().order.serviceLocation.state
: useMainStore().order.serviceLocation.provider.address.state;
},
getZipCode() {
return useMainStore().order.serviceLocation.zipCode
? useMainStore().order.serviceLocation.zipCode
: useMainStore().order.serviceLocation.provider.address.zipCode;
},
getPaymentType() {
switch (useMainStore().order.payment.payInAdvanceType) {
case paymentMethods.AFTERPAY:
return 'ap';
case paymentMethods.CREDIT_CARD:
return 'cc';
case paymentMethods.PAYPAL:
return 'pp';
default:
return useMainStore().payment.payInAdvanceType;
}
},
getAmountDue() {
return 0;
},
getDisplayAmountDue() {
return 0;
},
fetchSignatureInfo(signatureInfo) {
this.authToken = signatureInfo.token;
this.authSignature = signatureInfo.signature;
this.authSignatureStart = signatureInfo.startDate;
this.submitHopForm();
},
submitHopForm() {
this.$nextTick(() => {
this.$refs.hopForm.submit();
const iframe = this.$refs.paymentFrame;
if (iframe) {
iframe.onload = () => {
if (iframe.contentWindow) {
if (this.paymentType === 'cc') {
iframe.contentWindow.postMessage('CreditCardChosen', '*');
} else if (this.paymentType === 'ap') {
iframe.contentWindow.postMessage('afterpay', '*');
}
}
};
}
});
},
handleIFrameContentWindowMessage(event) {
if (typeof event.data === 'string') {
if (event.data.indexOf('afterpayClosed') > -1) {
this.backButtonAction();
}
if (event.data.indexOf('creditCardSubmit') > -1) {
this.setUIBlock(true);
}
}
},
setIFrameListener() {
window.addEventListener('message', (event) =>
this.handleIFrameContentWindowMessage(event));
},
setUIBlock(val) {
this.shouldBlockInteraction = val;
},
supressClick(event) {
if (this.shouldBlockInteraction) {
event.stopPropagation();
}
},
backButtonAction() {
// page will not render with normal navigation due to navigating from the iframe
window.location = this.payInAdvanceCancelUrl;
},
forwardButtonAction() {
this.$router.navigate(this.navigationScenarios.CLICKED_FORWARD, this.$route);
},
navigateForward() {
}
}
};
</script>
<style lang="scss" scoped>
.payment p{
margin-bottom:0.75rem;
}
.hop-iframe {
position: relative;
min-height: 40.625rem;
width: 100%;
border: 0 none;
}
.ui-block {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 9999;
}
</style>