Merge branch 'develop' into rlsmerge/2026.01.15-to-develop

This commit is contained in:
AdamCaouetteSafelite 2026-01-15 10:32:55 -05:00 committed by GitHub
commit b5bc983626
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 183 additions and 71 deletions

View file

@ -84,6 +84,21 @@ export function mapAdyenToFmgPaymentMethod(adyenMethod) {
return match ?? paymentMethods.CREDIT_CARD;
}
export function mapFmgToAdyenPaymentMethod(fmgMethod) {
const paymentMethodMap = {
[paymentMethods.CREDIT_CARD]: "scheme",
[paymentMethods.AFTERPAY]: "afterpaytouch",
[paymentMethods.PAYPAL]: "paypal",
[paymentMethods.APPLE_PAY]: "applepay",
[paymentMethods.PAY_NOW]: null,
[paymentMethods.LATER]: null,
};
const match = paymentMethodMap[fmgMethod];
return match;
}
export function generateCcToken(adyenSessionInfo) {
const order = store.getters.order;
const locationInfo = getLocationInfo(order);

View file

@ -4,6 +4,16 @@
<div class="container">
<div class="row">
<div class="col-12">
<div>
<alert
ref="piaErrorAlert"
class="my-4"
v-if="hasPaymentFailureError"
cmsWidgetName="PIAErrorAlertWidget"
alertClass="alert-danger"
@textLinkClicked="paymentFailedPayLater"
v-bind:isDismissible="false" />
</div>
<div id="card-container">
<div id="payment-container">
<funnelSubHeader class="py-5" cmsWidgetName="FunnelSubHeaderWidget" />
@ -45,6 +55,7 @@ import Form from "vee-validate";
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 alert from "@/ux-components/alert/alert";
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
import { settleAllPromises } from "@/helpers/layout-helper";
@ -61,7 +72,7 @@ import { applicationConfig } from "@/constants/application-config";
import { paymentMethods } from "@/constants/payment-method-constants";
import "@adyen/adyen-web/styles/adyen.css";
import { mapAdyenToFmgPaymentMethod } from "../../helpers/adyen-helper";
import { mapAdyenToFmgPaymentMethod, mapFmgToAdyenPaymentMethod } from "../../helpers/adyen-helper";
import { showFmgLoadingModal } from "../../helpers/loading-modal-helper";
import cart from "@/fmg-components/cart/cart";
import { coverageStatus } from "@/constants/insurance";
@ -75,6 +86,8 @@ export default {
data() {
return {
sessionId: String,
dropinComponent: null,
hasPaymentFailureError: false,
};
},
@ -92,9 +105,15 @@ export default {
const resultMap = await settleAllPromises(promiseResultMap);
const hasExistingPiaError = to.query?.piaFailure;
// Hydrate page with results
next(async (vm) => {
vm.setCmsContent(resultMap.cmsContent);
if (hasExistingPiaError) {
vm.hasPaymentFailureError = true;
}
});
},
@ -184,9 +203,9 @@ export default {
debugLog("providerLocation.city:", providerLocation.city, !preReqResult);
debugLog("providerLocation.state:", providerLocation.state, !preReqResult);
debugLog("providerLocation.zipCode:", providerLocation.zipCode, !preReqResult);
debugLog("", null, !preReqResult);
debugLog("isInsuranceSet:", isInsuranceSet, !preReqResult);
debugLog("", null, !preReqResult);
@ -197,7 +216,7 @@ export default {
debugLog("schedule.endTime:", schedule.endTime, !preReqResult);
debugLog("schedule.jobMaxMinutes:", schedule.jobMaxMinutes, !preReqResult);
debugLog("schedule.jobMinMinutes:", schedule.jobMinMinutes, !preReqResult);
debugLog("", null, !preReqResult);
debugLog("customerReqs:", customerReqs, !preReqResult);
@ -205,7 +224,7 @@ export default {
debugLog("customer.lastName:", customer.lastName, !preReqResult);
debugLog("customer.phoneNumber:", customer.phoneNumber, !preReqResult);
debugLog("customer.emailAddress:", customer.emailAddress, !preReqResult);
debugLog("", null, !preReqResult);
debugLog("paymentMethodReqs:", paymentMethodReqs, !preReqResult);
@ -270,7 +289,7 @@ export default {
console.log(checkout);
const dropin = new Dropin(checkout, {
const configuration = {
paymentMethodsConfiguration: {
ideal: {
showImage: true,
@ -292,7 +311,21 @@ export default {
name: "Credit or debit card",
},
},
}).mount("#adyen-container");
};
const adyenPaymentType = mapFmgToAdyenPaymentMethod(this.piaType);
if (adyenPaymentType) {
configuration.openPaymentMethod = {
type: adyenPaymentType,
};
}
const dropin = new Dropin(checkout, configuration);
this.dropinComponent = dropin;
dropin.mount("#adyen-container");
},
async handleCompletedPayment(result) {
@ -382,33 +415,33 @@ export default {
console.log(`Result =`);
console.log(result);
// Fetch session info
const paymentSessionRequest = {
zipCodeCtu: this.locationInfo.zipCodeCtu,
workOrderNumber: this.workOrderNumberLastSixDigits,
sourceSystem: this.sourceSystem,
sessionId: this.sessionId,
sessionResult: result?.sessionResult,
};
const wasPaymentCancelled = result?.resultCode === "Cancelled";
console.log(`Get session payload =`);
console.log(paymentSessionRequest);
if (!wasPaymentCancelled) {
const stringToLog = `ADYEN PAYMENT NOT AUTHORIZED. Code = ${result?.resultCode}. Id = ${this.sessionId}`;
try {
const response = await globalMethods.callHttpClient({
method: endpoints.GetAdyenSessionResult.method,
endpoint: endpoints.GetAdyenSessionResult.url,
payload: paymentSessionRequest,
logApiCall: true,
pageNameToLog: "payment-adyen",
});
await global.$logger.logError(stringToLog);
console.log(response);
} catch (error) {
console.log(`Error getting session info after failure.`);
this.hasPaymentFailureError = true;
}
this.dropinComponent?.update();
},
async handleError(error) {
console.log(error);
const wasPaymentCancelled = error?.name === "CANCEL";
if (!wasPaymentCancelled) {
const stringToLog = `ADYEN ERROR. Name = ${error?.name}. Details = ${error?.message}.`;
await global.$logger.logError(stringToLog);
this.hasPaymentFailureError = true;
}
this.dropinComponent?.update();
},
handleError(error) {},
async saveAndSubmitWorkOrder() {
// Work order submission after successful payment.
@ -421,15 +454,10 @@ export default {
} catch (error) {
console.log(`error: response from submit work order: ${error.message}`);
// navigate to error page
const errorPayload = {
cause: `Error while submitting work order after Adyen payment.`,
currentPage: this.pageName,
sessionId: this.sessionId,
idempotencyKey: this.idempotencyKey,
};
this.$router.handleSoftError(errorPayload);
this.$router.navigateWithoutSaving(
this.navigationScenarios.PIA_ERROR,
this.pageName
);
return;
}
@ -441,9 +469,44 @@ export default {
this.pageName
);
},
async paymentFailedPayLater() {
console.log(`Doing final work order submission...`);
showFmgLoadingModal(true);
await this.dispatchStoreAction(
storeActions.SAVE_PAYMENT_METHOD_CHOICE,
paymentMethods.PAY_LATER,
false
);
await this.dispatchStoreAction(storeActions.RESET_SAVE_SESSION_PROMISE);
try {
await submitWorkOrder({
pageNameToLog: this.pageName,
submitAfterSave: true,
});
await baseMixin.methods.dispatchStoreAction(storeActions.CREATE_SUBMITTED_STATE);
this.$router.navigateWithoutSaving(
this.navigationScenarios.CLICKED_FORWARD,
this.pageName
);
} catch (error) {
console.log(`Final submission failed after failed payment.`);
this.$router.handleSoftError();
return;
}
},
},
computed: {
piaType() {
return this.$store.getters.order.payment.piaType;
},
adyenInitRequestInfo() {
return {
sourceSystem: this.sourceSystem,
@ -581,6 +644,7 @@ export default {
funnelSubHeader,
navbar,
cart,
alert,
},
};
</script>

View file

@ -622,7 +622,10 @@ export default {
},
},
isMobileSelected() {
return this.appointmentType == AppointmentTypeStrings.MOBILE;
return (
this.appointmentType == AppointmentTypeStrings.MOBILE ||
(this.isServiceableMobile && !this.isServiceableInshop)
);
},
isServiceableMobile() {
if (this.isRecalibrationServiceableMobile !== null) {
@ -1134,30 +1137,7 @@ export default {
);
}
},
getTimeSlotInfo() {
// Get the current date
const currentDate = getTodayDate();
// Add 10 days to the current date
currentDate.setDate(currentDate.getDate() + 10);
// Format the date as yyyy-mm-dd
const year = currentDate.getFullYear();
const month = String(currentDate.getMonth() + 1).padStart(2, "0");
const day = String(currentDate.getDate()).padStart(2, "0");
const formattedDate = `${year}-${month}-${day}`;
return {
date: formattedDate,
endTime: "12:00",
jobMaxMinutes: "180",
jobMinMinutes: "120",
routeCode: "3357I-03357-M-I*20988*AM",
startTime: "08:00",
};
},
onShopSelected(shopQuestionPopUpData) {
async onShopSelected(shopQuestionPopUpData) {
this.resetWaitlist();
this.preSelectedDate = null;
this.shopProviderData = shopQuestionPopUpData.shopProviderData;
@ -1165,6 +1145,8 @@ export default {
const selectedProvider = this.shopProviderData.shopProviders.find((shopProvider) => {
return shopProvider.providerNumber === shopQuestionPopUpData.selectedProviderNumber;
});
const didProviderNumberChange =
oldProvider?.providerNumber !== selectedProvider?.providerNumber;
// No zipCodeData comes back if zip was not changed
if (shopQuestionPopUpData.zipCodeData) {
@ -1173,13 +1155,12 @@ export default {
selectedProvider
);
} else {
const didProviderNumberChange =
oldProvider?.providerNumber !== selectedProvider.providerNumber;
if (didProviderNumberChange) {
this.updateSelectedProvider(selectedProvider);
this.initializeDatePicker();
}
}
await this.initializeDatePicker();
this.setSelectedDateToFirstAvailable();
},
async getMoreScheduleData(startDate, endDate) {
// called only when "View more dates" is clicked; not on initial page load
@ -1717,7 +1698,6 @@ export default {
this.zipContainsMilitaryBase = newZipCode.containsMilitaryBase;
this.selectedDate = null;
this.selectedProvider = newProvider;
this.initializeDatePicker();
},
handleAppointmentTypeChange(newAppointmentType) {
this.updateFooterButtonText();

View file

@ -578,6 +578,13 @@ const routingTable = function () {
scenario: navigationScenarios.CLICKED_FORWARD,
destinationPageData: routeData.CONFIRMATION,
},
{
scenario: navigationScenarios.PIA_ERROR,
destinationPageData: routeData.PAYMENT_METHOD,
params: {
piaError: "true",
},
},
],
},
{

View file

@ -15,7 +15,30 @@ export async function adyenReturnBeforeEnter(to, from) {
const redirectResult = to?.query?.redirectResult;
if (sessionId && redirectResult) {
const result = await finalizeAdyenPayment(sessionId, redirectResult);
let result = null;
try {
result = await finalizeAdyenPayment(sessionId, redirectResult);
} catch (unexpectedResult) {
// If user cancelled payment, handle without error message
if (unexpectedResult.code === "Cancelled" || unexpectedResult.code === "CANCEL") {
return {
name: routeData.PAYMENT_ADYEN.name,
replace: true,
};
}
// Otherwise, failure scenario.
// Payment fails, so return user to payment-adyen screen to try again or pay later.
return {
name: routeData.PAYMENT_ADYEN.name,
query: {
piaFailure: true,
},
replace: true,
};
}
console.log(`Out of promise`);
console.log(result);
@ -59,7 +82,10 @@ export async function adyenReturnBeforeEnter(to, from) {
} catch (error) {
console.log(error);
return {
name: routeData.ERROR.name,
name: routeData.PAYMENT_METHOD.name,
query: {
piaError: "true",
},
replace: true,
};
}
@ -93,12 +119,32 @@ function finalizeAdyenPayment(sessionId, redirectResult) {
onPaymentFailed: (result, component) => {
console.log(`Payment failed`);
console.log(result);
reject("Payment failed from Adyen");
if (result?.resultCode !== "Cancelled") {
const stringToLog = `ADYEN PAYMENT NOT AUTHORIZED. Code = ${result?.resultCode}. Id = ${sessionId}`;
global.$logger.logError(stringToLog);
}
reject({
code: result?.resultCode,
message: "Payment failed from Adyen",
});
},
onError: (error, component) => {
console.log(`Error occured`);
console.log(error);
reject("Error from Adyen");
if (error?.name !== "CANCEL") {
const stringToLog = `ADYEN ERROR. Name = ${error?.name}. Details = ${error?.message}.`;
global.$logger.logError(stringToLog);
}
reject({
code: error?.name,
message: "Error from Adyen",
});
},
},
options: {},