added paypal errorhandling and modals
This commit is contained in:
CarlNation 2023-10-05 08:51:16 -04:00
parent 165bd30729
commit 1dba4521c5
15 changed files with 262 additions and 53 deletions

View file

@ -3,6 +3,9 @@ const queryStrings = {
START_TYPE: "start_type",
ZIP_CODE: "zipcode",
PROMO: "promo",
ERROR: "error",
TOKEN: "token",
PAYERID: "payerid",
};
export { queryStrings };

View file

@ -86,6 +86,7 @@ const storeActions = {
SAVE_CUSTOMER_DETAILS: "saveCustomerDetails",
SAVE_SERVICE_LOCATION_TECH_NOTES: "saveServiceLocationTechNotes",
SAVE_WORK_ORDER_FLAG: "saveWorkOrderFlag",
SAVE_PIA_ERROR_CODE: "savePiaErrorCode",
};
export { storeActions };

View file

@ -1,5 +1,6 @@
const storeMutations = {
UPDATE_WORK_ORDER_FLAG: "updateWorkOrderFlag",
UPDATE_PIA_ERROR_CODE: "updatePiaErrorCode",
// VEHICLE MUTATIONS
UPDATE_YEAR: "updateYear",
@ -47,6 +48,7 @@ const storeMutations = {
UPDATE_SAVED_SESSION_ID: "updateSavedSessionId",
UPDATE_CRM_CUSTOMER_ID: "updateCrmCustomerId",
UPDATE_PAY_NOW_TYPE: "updatePayNowType",
WORK_ORDER_NUMBER: "updateWorkOrderNumber",
// EVENT BUS MUTATIONS
ADD_EVENT_TO_BUS: "addEventToBus",

View file

@ -135,6 +135,7 @@ async function saveSessionHelper(pageNameToLog) {
savedSessionId: savedSessionInfo.data.savedSessionId,
crmCustomerId: savedSessionInfo.data.crmCustomerId?.toString(),
eon: savedSessionInfo.data.eon,
workOrderNumber: savedSessionInfo.data.workOrderNumber,
},
false
);

View file

@ -1,6 +1,7 @@
<template>
<div class="page-container-grouped-styles position-relative">
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
<div>{{ workOrderNumber }}</div>
<div>
<div class="container-fluid pb-2">
<div class="row justify-content-md-center">
@ -41,12 +42,16 @@ export default {
];
const resultMap = await settleAllPromises(promiseResultMap);
// Call the "next" function to complete the transition to this page.
next((vm) => {
vm.setCmsContent(resultMap.cmsContent);
});
},
data() {
return {
workOrderNumber: store.getters.order.workOrderNumber,
};
},
methods: {
arePagePrerequisitesValid() {
// Service Location

View file

@ -45,6 +45,11 @@ function setupMocks() {
store.getters = {
damage: {},
lineItems: [],
order: {
payment: {
piaErrorCode: "",
},
},
};
const wrapper = shallowMount(

View file

@ -1,6 +1,6 @@
<template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }">
<loadingModal ref="loadingModal" />
<loadingModal notFullScreen ref="loadingModal" />
<div class="container-fluid page-container-grouped-styles">
<div class="row">
<div class="col">
@ -21,6 +21,16 @@
:availableLineItems="cartItems"
servicePackageOptionsCmsName="ServicePackageTitle" />
<div>
<alert
ref="paypalErrorAlert"
class="my-4"
v-if="displayPaypalAlert"
cmsWidgetName="PaypalErrorAlertWidget"
alertClass="alert-danger"
v-bind:isDismissible="false" />
</div>
<add-vaps-modal-buttons
:cart="cartItems"
:wiperItems="availableWipers"
@ -56,7 +66,6 @@
alt="PayPal" />
</button>
</div>
<navbar
cmsWidgetName="FunnelFooterWidget"
:isForwardActionDisabled="!meta.valid"
@ -78,6 +87,7 @@ import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue";
import cart from "@/fmg-components/cart/cart";
import addVapsModalButtons from "@/fmg-components/add-vaps-modal-buttons/add-vaps-modal-buttons";
import { submitWorkOrder } from "@/helpers/heritage-integration/order-helper.js";
import alert from "@/ux-components/alert/alert";
import baseMixin from "@/mixins/base-mixin.js";
import { settleAllPromises } from "@/helpers/layout-helper";
@ -163,19 +173,25 @@ export default {
availableLineItems: null,
supportingItems: null,
pricedGlassParts: null,
displayPaypalAlert: this.getPaypalAlert(),
};
},
methods: {
arePagePrerequisitesValid() {
return true;
},
getPaypalAlert() {
return store.getters.order.payment.piaErrorCode ? true : false;
},
vapsItemsSelectedAction(vapsItemsSelected) {
this.cartItems = vapsItemsSelected;
},
backButtonAction() {
this.dispatchStoreAction(storeActions.SAVE_PIA_ERROR_CODE, null, false);
this.$router.navigateWithoutSaving(this.navigationScenarios.CLICKED_BACK, this.$route);
},
async forwardButtonAction() {
this.dispatchStoreAction(storeActions.SAVE_PIA_ERROR_CODE, null, false);
this.dispatchStoreAction(this.storeActions.SAVE_WORK_ORDER_FLAG, true, false);
await submitWorkOrder({ pageNameToLog: "payment-method" });
this.$router.navigateWithoutSaving(
@ -184,12 +200,26 @@ export default {
);
},
async ccdPayment() {
//leaving site, clear any save session promises that we will not be able to resolve when we return
this.$refs.loadingModal.showModal();
this.dispatchStoreAction(storeActions.SAVE_PIA_ERROR_CODE, null, false);
await this.dispatchStoreAction(storeActions.RESET_SAVE_SESSION_PROMISE);
await this.dispatchStoreAction(storeActions.SAVE_PAY_NOW_TYPE, "cc", false);
this.$router.navigateWithSaving(this.navigationScenarios.CLICKED_PAY_NOW, this.$route);
this.$router.navigateWithoutSaving(
this.navigationScenarios.CLICKED_PAY_NOW,
this.$route
);
},
async paypal() {
//leaving site, clear any save session promises that we will not be able to resolve when we return
this.$refs.loadingModal.showModal();
this.dispatchStoreAction(storeActions.SAVE_PIA_ERROR_CODE, null, false);
await this.dispatchStoreAction(storeActions.RESET_SAVE_SESSION_PROMISE);
await this.dispatchStoreAction(storeActions.SAVE_PAY_NOW_TYPE, "pp", false);
this.$router.navigateWithSaving(this.navigationScenarios.CLICKED_PAY_NOW, this.$route);
this.$router.navigateWithoutSaving(
this.navigationScenarios.CLICKED_PAY_NOW,
this.$route
);
},
},
computed: {
@ -207,6 +237,7 @@ export default {
Form,
loadingModal,
cart,
alert,
addVapsModalButtons,
},
};

View file

@ -0,0 +1,66 @@
<template>
<div class="confirm" ref="confirm" @click="forwardButtonAction"></div>
</template>
<script>
import { submitWorkOrder } from "@/helpers/heritage-integration/order-helper.js";
import { queryStrings } from "@/constants/query-strings";
import { getQuerystringParameter } from "@/helpers/querystring-helper";
import { storeActions } from "@/constants/store-actions";
import baseMixin from "@/mixins/base-mixin.js";
export default {
name: "payment-pia-return",
async mounted() {
const piaError = getQuerystringParameter(queryStrings.ERROR);
const token = getQuerystringParameter(queryStrings.TOKEN);
const payerId = getQuerystringParameter(queryStrings.PAYERID);
if (piaError) {
baseMixin.methods.dispatchStoreAction(
storeActions.SAVE_PAYPAL_ERROR_CODE,
piaError,
false
);
this.$router.navigateWithoutSaving(this.navigationScenarios.PIA_ERROR, this.$route);
} else {
if (token && payerId) {
baseMixin.methods.dispatchStoreAction(
storeActions.SAVE_PAYPAL_ERROR_CODE,
null,
false
);
baseMixin.methods.dispatchStoreAction(
storeActions.SAVE_WORK_ORDER_FLAG,
true,
false
);
await submitWorkOrder({ pageNameToLog: "payment-pia-return" });
this.$refs.confirm.click();
} else {
baseMixin.methods.dispatchStoreAction(
storeActions.SAVE_PAYPAL_ERROR_CODE,
piaError,
false
);
this.$router.navigateWithoutSaving(this.navigationScenarios.PIA_ERROR, this.$route);
}
}
},
methods: {
arePagePrerequisitesValid() {
return true;
},
forwardButtonAction() {
this.$router.navigateWithoutSaving(this.navigationScenarios.PIA_SUCCESS, this.$route);
},
},
components: {},
};
</script>
<style lang="scss">
.confirm {
visibility: hidden;
}
</style>

View file

@ -1,15 +1,23 @@
<template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }">
<loadingModal notFullScreen ref="loadingModal" />
<div class="page-container-grouped-styles">
<loadingModal ref="loadingModal" />
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
<funnelSubHeader class="mb-5" cmsWidgetName="FunnelSubHeaderWidget" leftAlignHeader />
<funnelHeader cmsWidgetName="FunnelHeaderWidget" v-if="!isPaypal" />
<funnelSubHeader
class="mb-5"
cmsWidgetName="FunnelSubHeaderWidget"
leftAlignHeader
v-if="!isPaypal" />
<hr class="mt-0" />
<iframe
class="hop-iframe payment-iframe"
name="card-frame"
seamless
scrolling="no"></iframe>
<div id="card-container">
<iframe
class="hop-iframe payment-iframe"
name="card-frame"
id="card-frame"
seamless
scrolling="no">
</iframe>
</div>
<div class="fade-on-route-transition sub-container make-tall">
<navbar
cmsWidgetName="FunnelFooterWidget"
@ -24,7 +32,7 @@
<form
ref="myForm"
id="card-data"
:target="paymentType == 'pp' ? '_top' : 'card-frame'"
:target="isPaypal ? '_top' : 'card-frame'"
method="POST"
:action="checkoutUrl">
<input type="hidden" name="paymentType" :value="paymentType" />
@ -64,23 +72,17 @@
name="sgErrorResponseURL"
value="https://fixmyglasstest.safelite.com/FixMyGlass/Payment/Error.aspx" />
<input type="hidden" name="sgheaderline1" value="Submit payment information" />
<input type="hidden" name="sgheaderline1" :value="sgHeaderline1" />
<input type="hidden" name="sgHeader1subtitle" value="" />
<input
type="hidden"
name="sgheaderline2"
value="Save time when you pay for your service today! Please select a payment option." />
<input type="hidden" name="sgheaderline2" :value="sgHeaderline2" />
<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="MATT" />
<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="CAIMI" />
<input type="hidden" name="billTo_lastName" :value="lastName" />
<input type="hidden" name="billTo_lastNameShow" value="true" />
<input type="hidden" name="billTo_street1" value="" />
@ -93,9 +95,9 @@
<input type="hidden" name="billTo_postalCodeEnable" value="true" />
<input type="hidden" name="sgLabelPostalCode" value="Billing ZIP" />
<input type="hidden" name="sgtotalamount" value="$212.83" />
<input type="hidden" name="totalAmountDecimal" value="212.83" />
<input type="hidden" name="lineItems" value="Labor|189.99|1||Repair supplies|7.99|1" />
<input type="hidden" name="sgtotalamount" :value="displayAmount" />
<input type="hidden" name="totalAmountDecimal" :value="totalAmount" />
<input type="hidden" name="lineItems" :value="lineItems" />
<input type="hidden" name="sgdiscountstrikethruamount" value="" />
@ -127,14 +129,14 @@
value="For your security, this transaction has been timed out." />
<input type="hidden" name="sgLabelCardNumber" value="" />
<input type="hidden" name="sgLabelAddressLine1" value="Billing address" />
<input type="hidden" name="sgLabelAddressLine2" value="Apt. number" />
<input type="hidden" name="sgLabelCity" value="City" />
<input type="hidden" name="sgLabelState" value="State" />
<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="01820" />
<input type="hidden" name="sgCtu" :value="ctu" />
<input type="hidden" name="sgWorkOrder" :value="workOrderNumber" />
<input type="hidden" name="sgEmailAddress" value="builddigitaltest@safelite.com" />
<input type="hidden" name="sgEmailAddress" :value="emailAddress" />
<input type="hidden" name="paypalInvoiceNumber" :value="invoiceNumber" />
<input type="hidden" name="payPalSuccessUrl" :value="payPalSuccessUrl" />
<input type="hidden" name="paypalCancelUrl" :value="payPalCancelUrl" />
@ -149,25 +151,24 @@
value="https://fixmyglasstest.safelite.com/FixMyGlass/Payment.aspx?error=ccTimeout" />
<input type="hidden" name="sgTransactionType" value="authorization" />
<input type="hidden" name="amount" value="212.83" />
<input type="hidden" name="ctu" value="01820" />
<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="builddigitaltest@safelite.com" />
<input type="hidden" name="billTo_email" :value="emailAddress" />
<input type="hidden" name="useDecisionManager" value="True" />
<input type="hidden" name="correlationId" value="84cae814-d1b6-433e-aa34-7a39ec49ee97" />
<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="SAFELITE AUTOGLASS - COLUMBUS, OH NCR-CTU" />
<input type="hidden" name="ship_to_address_line2" value="760 DEARBORN PARK LN" />
<input type="hidden" name="ship_to_address_city" value="WORTHINGTON" />
<input type="hidden" name="ship_to_address_state" value="OH" />
<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="43085" />
<input type="hidden" name="ship_to_phone" value="8888888888" />
<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>
@ -176,10 +177,11 @@
import funnelHeader from "@/fmg-components/funnel-header/funnel-header";
import navbar from "@/fmg-components/nav-bar/nav-bar";
import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header";
import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue";
import { storeActions } from "@/constants/store-actions";
import store from "@/store";
import { externalUrls } from "@/router/router-constants/externalUrl-values";
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
import { settleAllPromises } from "@/helpers/layout-helper";
@ -193,14 +195,30 @@ export default {
checkoutUrl: externalUrls.SAFELITE_HOP,
payPalSuccessUrl: externalUrls.PAYPAL_SUCCESS_URL,
payPalCancelUrl: externalUrls.PAYPAL_CANCEL_URL,
paymentType: store.getters.order.payment.payNowType,
paymentType: this.getPaymentType(),
authToken: "",
authSignature: "",
authSignatureStart: "",
referralSequenceNumber: store.getters.order.referralSequenceNumber,
// TODO: replace these random numbers
emailAddress: store.getters.order.customer.emailAddress,
address1: this.getAddress1(),
address2: this.getAddress2(),
city: this.getCity(),
state: this.getState(),
zipCode: this.getZipCode(),
firstName: store.getters.order.customer.firstName,
lastName: store.getters.order.customer.lastName,
phoneNumber: store.getters.order.customer.phoneNumber,
ctu: store.getters.order.serviceLocation.zipCodeCtu,
referralCorrelationId: store.getters.order.referralCorrelationId,
// TODO: replace these
workOrderNumber: Math.floor(Math.random() * 90000) + 10000,
invoiceNumber: "1820" + Math.floor(Math.random() * 90000) + 10000,
invoiceNumber: Math.floor(Math.random() * 90000) + 10000,
totalAmount: "321.00", // TODO: get from cart?
displayAmount: "$321.00", // TODO: get from cart?
lineItems: "Labor|189.99|1||Repair supplies|7.99|1",
sgHeaderline1: this.getHeaderLine1(),
sgHeaderline2: this.getHeaderLine2(),
};
},
async mounted() {
@ -230,11 +248,58 @@ export default {
// TODO
return true;
},
getAddress1() {
return store.getters.order.serviceLocation.address
? store.getters.order.serviceLocation.address
: store.getters.order.serviceLocation.provider.address.streetAddress;
},
getAddress2() {
return store.getters.order.serviceLocation.address2
? store.getters.order.serviceLocation.address2
: null;
},
getCity() {
return store.getters.order.serviceLocation.city
? store.getters.order.serviceLocation.city
: store.getters.order.serviceLocation.provider.address.city;
},
getState() {
return store.getters.order.serviceLocation.state
? store.getters.order.serviceLocation.state
: store.getters.order.serviceLocation.provider.address.state;
},
getZipCode() {
return store.getters.order.serviceLocation.zipCode
? store.getters.order.serviceLocation.zipCode
: store.getters.order.serviceLocation.provider.address.zipCode;
},
getPaymentType() {
return store.getters.order.payment.payNowType;
},
getHeaderLine1() {
if (!this.isPaypal()) {
return "Submit payment information";
}
return "";
},
getHeaderLine2() {
if (!this.isPaypal()) {
return "Save time when you pay for your service today! Please select a payment option.";
}
return "";
},
isPaypal() {
if (this.getPaymentType() == "pp") {
return true;
}
return false;
},
backButtonAction() {
this.$router.navigateWithoutSaving(this.navigationScenarios.CLICKED_BACK, this.$route);
},
forwardButtonAction() {},
async fetchSignatureInfo() {
this.$refs.loadingModal.showModal();
const signatureInfo = await this.dispatchStoreAction(storeActions.GET_SIGNATURE);
this.authToken = signatureInfo.data.token;
this.authSignature = signatureInfo.data.signature;
@ -250,6 +315,7 @@ export default {
navbar,
funnelSubHeader,
Form,
loadingModal,
},
};
</script>

View file

@ -53,8 +53,8 @@ const routes = [
await GoToFunnelStartOn404(next);
}
// On entering the funnel "fresh", read cookie information, decide what to do next.
if (from.redirectedFrom === undefined) {
// On entering the funnel "fresh", read cookie information, decide what to do next. payment pages used to return from safelitehop so exclude here
if (from.redirectedFrom === undefined && !to.query.fmgPage.startsWith("payment")) {
// clear the saveSessionPromise - if it exists in the vuex store but a new instance was created
// the saveSessionPromise will no longer point to a valid promise
baseMixin.methods.dispatchStoreAction(storeActions.RESET_SAVE_SESSION_PROMISE);

View file

@ -18,6 +18,7 @@ const fmgPageValues = {
REVIEW: "review",
PAYMENT_METHOD: "payment-method",
PAYMENT: "payment",
PAYMENT_PIA_RETURN: "payment-pia-return",
CONFIRMATION: "confirmation",
};

View file

@ -54,6 +54,8 @@ const navigationScenarios = {
// Payment
CLICKED_PAY_NOW: "CLICKED_PAY_NOW",
PIA_ERROR: "PIA_ERROR",
PIA_SUCCESS: "PIA_SUCCESS",
};
export { navigationScenarios };

View file

@ -501,6 +501,19 @@ const routingTable = function (store) {
},
],
},
{
fmgPageValue: fmgPageValues.PAYMENT_PIA_RETURN,
maps: [
{
scenario: navigationScenarios.PIA_ERROR,
destinationFmgPageValue: fmgPageValues.PAYMENT_METHOD,
},
{
scenario: navigationScenarios.PIA_SUCCESS,
destinationFmgPageValue: fmgPageValues.CONFIRMATION,
},
],
},
{
fmgPageValue: fmgPageValues.CONFIRMATION,
},

View file

@ -93,6 +93,7 @@ const getDefaultState = () => {
},
parentAccountNumber: 0,
payNowType: "cc",
piaErrorCode: null,
},
schedule: {
date: null,
@ -107,6 +108,7 @@ const getDefaultState = () => {
referralDate: null,
referralCorrelationId: null,
eon: null,
workOrderNumber: null,
},
applicationUser: {
eventBus: [],
@ -218,6 +220,12 @@ export const mutations = {
updatePayNowType(state, payNowType) {
state.order.payment.payNowType = payNowType;
},
updatePiaErrorCode(state, piaErrorCode) {
state.order.payment.piaErrorCode = piaErrorCode;
},
updateWorkOrderNumber(state, workOrderNumber) {
state.order.workOrderNumber = workOrderNumber;
},
updateInsuranceVerifiedStatus(state, isVerified) {
state.order.payment.insuranceCoverage.isVerified = isVerified;
},
@ -957,6 +965,7 @@ export const actions = {
parentAccountNumber,
savedSessionId,
crmCustomerId,
workOrderNumber,
}
) {
context.commit(storeMutations.UPDATE_REFERRAL_NUMBER, referralNumber);
@ -967,6 +976,7 @@ export const actions = {
context.commit(storeMutations.UPDATE_PARENT_ACCT_NUMBER, parentAccountNumber);
context.commit(storeMutations.UPDATE_SAVED_SESSION_ID, savedSessionId);
context.commit(storeMutations.UPDATE_CRM_CUSTOMER_ID, crmCustomerId);
context.commit(storeMutations.WORK_ORDER_NUMBER, workOrderNumber);
},
logPageView(
@ -1680,6 +1690,9 @@ export const actions = {
saveWorkOrderFlag(context, submitAfterSave) {
context.commit(storeMutations.UPDATE_WORK_ORDER_FLAG, submitAfterSave);
},
savePiaErrorCode(context, piaErrorCode) {
context.commit(storeMutations.UPDATE_PIA_ERROR_CODE, piaErrorCode);
},
// Business domain actions

View file

@ -8,7 +8,7 @@ process.env.VUE_APP_CURRENT_ENVIRONMENT = "Localhost";
//process.env.VUE_APP_SAFELITE_HOP = "http://localhost:60966/fmgCheckoutEmbedded.aspx";
process.env.VUE_APP_SAFELITE_HOP = "https://sv2-safelitehop-sys.safelite.com/fmgCheckoutEmbedded.aspx";
process.env.VUE_APP_PAYPAL_SUCCESS_URL = "http://localhost:8080/fmg/?fmgPage=payment-method&src=concept-funnel";
process.env.VUE_APP_PAYPAL_SUCCESS_URL = "http://localhost:8080/fmg/?fmgPage=payment-pia-return&src=concept-funnel";
process.env.VUE_APP_PAYPAL_CANCEL_URL = "http://localhost:8080/fmg/?fmgPage=payment-method&src=concept-funnel";
// GA & GTM