495 lines
19 KiB
Vue
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>
|