diff --git a/src/helpers/adyen-helper.js b/src/helpers/adyen-helper.js index 8de92bf89..5cd7a9e77 100644 --- a/src/helpers/adyen-helper.js +++ b/src/helpers/adyen-helper.js @@ -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); diff --git a/src/layouts/payment-adyen/payment-adyen.vue b/src/layouts/payment-adyen/payment-adyen.vue index b407f9d55..42b7d03fb 100644 --- a/src/layouts/payment-adyen/payment-adyen.vue +++ b/src/layouts/payment-adyen/payment-adyen.vue @@ -4,6 +4,16 @@
+
+ +
@@ -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, }, }; diff --git a/src/layouts/schedule/schedule.vue b/src/layouts/schedule/schedule.vue index 1ebf47c7e..87b542b80 100644 --- a/src/layouts/schedule/schedule.vue +++ b/src/layouts/schedule/schedule.vue @@ -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(); diff --git a/src/router/constants/routing-table.js b/src/router/constants/routing-table.js index 192e1572e..48cd49b3f 100644 --- a/src/router/constants/routing-table.js +++ b/src/router/constants/routing-table.js @@ -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", + }, + }, ], }, { diff --git a/src/router/methods/route-logic/adyen-return.js b/src/router/methods/route-logic/adyen-return.js index d94de4c38..f15168861 100644 --- a/src/router/methods/route-logic/adyen-return.js +++ b/src/router/methods/route-logic/adyen-return.js @@ -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: {},