From a212894a26d48d9fcfba15bf819363894e8851b0 Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Wed, 10 Dec 2025 16:12:58 -0500 Subject: [PATCH 1/6] Adyen POC 0.0 --- src/constants/endpoints.js | 8 + src/layouts/payment-adyen/payment-adyen.vue | 319 ++++++++++++++++++ .../payment-pia-return/payment-pia-return.vue | 3 + src/router/constants/routes.js | 4 + src/router/constants/routing-table.js | 15 +- src/router/methods/routes.js | 1 + 6 files changed, 349 insertions(+), 1 deletion(-) create mode 100644 src/layouts/payment-adyen/payment-adyen.vue diff --git a/src/constants/endpoints.js b/src/constants/endpoints.js index c1771b3f5..214431e5c 100644 --- a/src/constants/endpoints.js +++ b/src/constants/endpoints.js @@ -216,6 +216,14 @@ const endpoints = { url: "/order/api/v1/order/add-donation-part", method: "POST", }, + InitializeAdyenPayment: { + url: "/order/api/v1/order/payment/session/initialize-session", + method: "POST", + }, + GetAdyenSessionResult: { + url: "/order/api/v1/order/payment/session/session-result", + method: "POST", + }, }; export { endpoints }; diff --git a/src/layouts/payment-adyen/payment-adyen.vue b/src/layouts/payment-adyen/payment-adyen.vue new file mode 100644 index 000000000..d105382d6 --- /dev/null +++ b/src/layouts/payment-adyen/payment-adyen.vue @@ -0,0 +1,319 @@ + + diff --git a/src/layouts/payment-pia-return/payment-pia-return.vue b/src/layouts/payment-pia-return/payment-pia-return.vue index b1ec2b120..c905f3d78 100644 --- a/src/layouts/payment-pia-return/payment-pia-return.vue +++ b/src/layouts/payment-pia-return/payment-pia-return.vue @@ -156,6 +156,9 @@ export default { lastFour: lastFour, }; + console.log(`CCToken =`); + console.log(ccToken); + baseMixin.methods.dispatchStoreAction(storeActions.SAVE_CCTOKEN, ccToken, false); baseMixin.methods.dispatchStoreAction( storeActions.SAVE_NEXTGEN_SETTLED_AMOUNT, diff --git a/src/router/constants/routes.js b/src/router/constants/routes.js index 98d6c503a..4b51ff857 100644 --- a/src/router/constants/routes.js +++ b/src/router/constants/routes.js @@ -89,6 +89,10 @@ export const routeData = { name: "payment-pia-return", path: "/payment-pia-return", }, + PAYMENT_ADYEN: { + name: "payment-adyen", + path: "/payment-adyen", + }, CONFIRMATION: { name: "confirmation", path: "/confirmation", diff --git a/src/router/constants/routing-table.js b/src/router/constants/routing-table.js index 02a0d9384..cd41e8fcf 100644 --- a/src/router/constants/routing-table.js +++ b/src/router/constants/routing-table.js @@ -535,7 +535,7 @@ const routingTable = function () { }, { scenario: navigationScenarios.CLICKED_PAY_NOW, - destinationPageData: routeData.PAYMENT, + destinationPageData: routeData.PAYMENT_ADYEN, }, { scenario: navigationScenarios.CLICKED_INSURANCE, @@ -563,6 +563,19 @@ const routingTable = function () { }, ], }, + { + pageName: routeData.PAYMENT_ADYEN.name, + maps: [ + { + scenario: navigationScenarios.CLICKED_BACK, + destinationPageData: routeData.PAYMENT_METHOD, + }, + { + scenario: navigationScenarios.CLICKED_FORWARD, + destinationPageData: routeData.CONFIRMATION, + }, + ], + }, { pageName: routeData.PAYMENT_PIA_RETURN.name, maps: [ diff --git a/src/router/methods/routes.js b/src/router/methods/routes.js index 97af04068..cf0392ccf 100644 --- a/src/router/methods/routes.js +++ b/src/router/methods/routes.js @@ -36,6 +36,7 @@ export const routes = [ createRoute(routeData.PAYMENT_METHOD, paymentMethodBeforeEnter), createRoute(routeData.PAYMENT, paymentBeforeEnter), createRoute(routeData.PAYMENT_PIA_RETURN), + createRoute(routeData.PAYMENT_ADYEN), createRoute(routeData.CONFIRMATION), createRoute(routeData.RETURN_USER), createRoute(routeData.MOBILE_DETAILS), From 3b1b000a1387df7bf8f64a17f2a73edc1d624ce2 Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Thu, 11 Dec 2025 11:58:11 -0500 Subject: [PATCH 2/6] In progress --- src/constants/endpoints.js | 4 +- src/layouts/payment-adyen/payment-adyen.vue | 77 ++++++++++++++++----- 2 files changed, 61 insertions(+), 20 deletions(-) diff --git a/src/constants/endpoints.js b/src/constants/endpoints.js index 214431e5c..e40fce79a 100644 --- a/src/constants/endpoints.js +++ b/src/constants/endpoints.js @@ -217,11 +217,11 @@ const endpoints = { method: "POST", }, InitializeAdyenPayment: { - url: "/order/api/v1/order/payment/session/initialize-session", + url: "/payment/api/v1/payment/payment/session/initialize-session", method: "POST", }, GetAdyenSessionResult: { - url: "/order/api/v1/order/payment/session/session-result", + url: "/payment/api/v1/payment/payment/session/session-result", method: "POST", }, }; diff --git a/src/layouts/payment-adyen/payment-adyen.vue b/src/layouts/payment-adyen/payment-adyen.vue index d105382d6..86c8a67d2 100644 --- a/src/layouts/payment-adyen/payment-adyen.vue +++ b/src/layouts/payment-adyen/payment-adyen.vue @@ -42,6 +42,10 @@ import { settleAllPromises } from "@/helpers/layout-helper"; import globalMethods from "@/global-methods"; import { endpoints } from "../../constants/endpoints"; import { AppointmentTypeStrings } from "../../constants/schedule-constants"; +import baseMixin from "@/mixins/base-mixin.js"; +import { storeActions } from "@/constants/store-actions"; +import { getAmountDue } from "@/helpers/pricing-helper.js"; +import { submitWorkOrder } from "@/helpers/heritage-integration/order-helper.js"; export default { name: "payment-adyen", @@ -90,6 +94,9 @@ export default { }, async initializeAdyen() { + console.log(`Price = ${this.amountDue}`); + console.log(`Adyen Price = ${this.adyenPriceTotal}`); + const requestBody = this.adyenInitRequestInfo; console.log(`Calling with:`); @@ -153,23 +160,23 @@ export default { window.checkout = checkOut; // eslint-disable-next-line const dropin = new AdyenWeb.Dropin(checkOut, { - paymentMethodsConfiguration: { - card: { - showBrandIcon: true, // when false not showing the brand logo - hasHolderName: true, // show holder name - holderNameRequired: true, // make holder name mandatory - billingAddressRequired: true, - billingAddressAllowedCountries: ["US"], - // configure placeholders - placeholders: { - cardNumber: "1234 5678 9012 3456", - expiryDate: "MM/YY", - securityCodeThreeDigits: "123", - securityCodeFourDigits: "1234", - holderName: "J. Smith", - }, - }, - }, + // paymentMethodsConfiguration: { + // card: { + // showBrandIcon: true, // when false not showing the brand logo + // hasHolderName: true, // show holder name + // holderNameRequired: true, // make holder name mandatory + // billingAddressRequired: true, + // billingAddressAllowedCountries: ["US"], + // // configure placeholders + // placeholders: { + // cardNumber: "1234 5678 9012 3456", + // expiryDate: "MM/YY", + // securityCodeThreeDigits: "123", + // securityCodeFourDigits: "1234", + // holderName: "J. Smith", + // }, + // }, + // }, }).mount("#adyen-container"); }, @@ -218,9 +225,39 @@ export default { console.log(`CC Token generated =`); console.log(ccToken); + + await baseMixin.methods.dispatchStoreAction(storeActions.SAVE_CCTOKEN, ccToken, false); + await baseMixin.methods.dispatchStoreAction( + storeActions.SAVE_NEXTGEN_SETTLED_AMOUNT, + this.amountDue, + false + ); + + await this.saveAndSubmitWorkOrder(); }, handleFailedPayment(result) {}, handleError(error) {}, + + async saveAndSubmitWorkOrder() { + // Work order submission after successful payment. + await this.dispatchStoreAction(storeActions.RESET_SAVE_SESSION_PROMISE); + try { + await submitWorkOrder({ + pageNameToLog: "payment-adyen", + submitAfterSave: true, + }); + } catch (error) { + console.log(`error: response from submit work order: ${error.message}`); + + // navigate to error page + + //this.$refs.loadingModal.isModalVisible = false; + } + + //this.$refs.loadingModal.isModalVisible = false; + // clear order + // navigate forward + }, }, computed: { @@ -304,8 +341,12 @@ export default { return "FMG-2.0"; }, + amountDue() { + return getAmountDue(this.$store.getters.order.lineItems); + }, + adyenPriceTotal() { - return 10000; + return this.amountDue * 100; }, }, From 75417c4a34efc311020622ca98db225b25829294 Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Fri, 12 Dec 2025 14:12:20 -0500 Subject: [PATCH 3/6] Attempts to resolve PayPal --- src/layouts/payment-adyen/payment-adyen.vue | 56 +++++++++++++-------- 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/src/layouts/payment-adyen/payment-adyen.vue b/src/layouts/payment-adyen/payment-adyen.vue index 86c8a67d2..f7e85b019 100644 --- a/src/layouts/payment-adyen/payment-adyen.vue +++ b/src/layouts/payment-adyen/payment-adyen.vue @@ -79,6 +79,10 @@ export default { methods: { async backButtonAction() { // Stub, for navigating back via nav-bar. + this.$router.navigateWithoutSaving( + this.navigationScenarios.CLICKED_BACK, + this.pageName + ); }, async forwardButtonAction() { @@ -130,14 +134,15 @@ export default { value: this.adyenPriceTotal, currency: "USD", }, - locale: "en-US", + locale: "en_US", countryCode: "US", showPayButton: true, translations: { - "en-US": { + "en_US": { "creditCard.securityCode.label": "CVV/CVC", }, }, + onPaymentCompleted: (result, component) => { console.log(`Payment completed from Adyen.`); this.handleCompletedPayment(result); @@ -160,23 +165,27 @@ export default { window.checkout = checkOut; // eslint-disable-next-line const dropin = new AdyenWeb.Dropin(checkOut, { - // paymentMethodsConfiguration: { - // card: { - // showBrandIcon: true, // when false not showing the brand logo - // hasHolderName: true, // show holder name - // holderNameRequired: true, // make holder name mandatory - // billingAddressRequired: true, - // billingAddressAllowedCountries: ["US"], - // // configure placeholders - // placeholders: { - // cardNumber: "1234 5678 9012 3456", - // expiryDate: "MM/YY", - // securityCodeThreeDigits: "123", - // securityCodeFourDigits: "1234", - // holderName: "J. Smith", - // }, - // }, - // }, + paymentMethodsConfiguration: { + ideal: { + showImage: true, + }, + paypal: { + amount: { + value: this.adyenPriceTotal, + currency: "USD", + }, + environment: "test", // Change this to "live" when you're ready to accept live PayPal payments + countryCode: "US", // Only needed for test. This will be automatically retrieved when you are in production. + blockPayPalVenmoButton: false, + //blockPayPalPayLaterButton: true + }, + card: { + hasHolderName: true, + holderNameRequired: true, + billingAddressRequired: true, + name: "Credit or debit card", + }, + }, }).mount("#adyen-container"); }, @@ -235,7 +244,10 @@ export default { await this.saveAndSubmitWorkOrder(); }, - handleFailedPayment(result) {}, + async handleFailedPayment(result) { + console.log(`Result =`); + console.log(result); + }, handleError(error) {}, async saveAndSubmitWorkOrder() { @@ -273,12 +285,12 @@ export default { street: this.splitStreetAddress.street, zipCode: this.locationInfo.zipCode, stateOrProvince: this.locationInfo.state, - returnUrl: "localhost:8080/fmg/confirmation", + returnUrl: "http://localhost:8080/fmg/confirmation", email: this.$store.getters.order.customer.emailAddress, IP: "127.0.0.1", // TODO firstName: this.$store.getters.order.customer.firstName, lastName: this.$store.getters.order.customer.lastName, - idempotencyKey: `${this.$store.getters.order.referralCorrelationId}-${this.sourceSystem}`, + idempotencyKey: crypto.randomUUID(), // `${this.$store.getters.order.referralCorrelationId}-${this.sourceSystem}`, }; }, From a8453c76ae50c27edd04effb3da57a26a02d98b8 Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Mon, 15 Dec 2025 10:17:57 -0500 Subject: [PATCH 4/6] Misc --- src/layouts/payment-adyen/payment-adyen.vue | 35 ++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/layouts/payment-adyen/payment-adyen.vue b/src/layouts/payment-adyen/payment-adyen.vue index f7e85b019..e38578e54 100644 --- a/src/layouts/payment-adyen/payment-adyen.vue +++ b/src/layouts/payment-adyen/payment-adyen.vue @@ -46,6 +46,7 @@ import baseMixin from "@/mixins/base-mixin.js"; import { storeActions } from "@/constants/store-actions"; import { getAmountDue } from "@/helpers/pricing-helper.js"; import { submitWorkOrder } from "@/helpers/heritage-integration/order-helper.js"; +import { paymentMethods } from "@/constants/payment-method-constants"; export default { name: "payment-adyen", @@ -138,7 +139,7 @@ export default { countryCode: "US", showPayButton: true, translations: { - "en_US": { + en_US: { "creditCard.securityCode.label": "CVV/CVC", }, }, @@ -235,6 +236,11 @@ export default { console.log(`CC Token generated =`); console.log(ccToken); + // Save payment type. + // For now, CC. + // Need to determine payment type from Adyen response + await this.dispatchStoreAction(storeActions.SAVE_PAYMENT_METHOD_CHOICE, paymentMethods.CREDIT_CARD, false); + await baseMixin.methods.dispatchStoreAction(storeActions.SAVE_CCTOKEN, ccToken, false); await baseMixin.methods.dispatchStoreAction( storeActions.SAVE_NEXTGEN_SETTLED_AMOUNT, @@ -247,6 +253,33 @@ export default { async handleFailedPayment(result) { 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, + }; + + console.log(`Get session payload =`); + console.log(paymentSessionRequest); + + try { + const response = await globalMethods.callHttpClient({ + method: endpoints.GetAdyenSessionResult.method, + endpoint: endpoints.GetAdyenSessionResult.url, + payload: paymentSessionRequest, + logApiCall: true, + pageNameToLog: "payment-adyen", + }); + + console.log(response); + } catch (error) { + console.log(`Error getting session info after failure.`); + } + }, handleError(error) {}, From 72a3d8588eb64e8136937b1c74d0b38e8fef19e0 Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Tue, 16 Dec 2025 09:45:44 -0500 Subject: [PATCH 5/6] Afterpay return page --- package-lock.json | 41 +++++++ package.json | 1 + src/helpers/adyen-helper.js | 116 ++++++++++++++++++ src/layouts/payment-adyen/payment-adyen.vue | 67 +++++----- src/router/constants/routes.js | 5 + .../methods/route-logic/adyen-return.js | 73 +++++++++++ src/router/methods/routes.js | 2 + 7 files changed, 267 insertions(+), 38 deletions(-) create mode 100644 src/helpers/adyen-helper.js create mode 100644 src/router/methods/route-logic/adyen-return.js diff --git a/package-lock.json b/package-lock.json index 7d44b181a..2e45411d6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "Safelite", "version": "0.1.0", "dependencies": { + "@adyen/adyen-web": "^6.27.0", "@iframe-resizer/child": "^5.3.3", "axios": "^0.30.2", "bootstrap": "^5.3.3", @@ -59,6 +60,18 @@ "node": "8 || 9 || 10 || 11 || 12 || 13 || 14 || 15 || 16 || 17 || 18 || 19 || 20 || 21 || 22" } }, + "node_modules/@adyen/adyen-web": { + "version": "6.27.0", + "resolved": "https://registry.npmjs.org/@adyen/adyen-web/-/adyen-web-6.27.0.tgz", + "integrity": "sha512-6E6hqEkMkoY/FevjO53zhgeCWMt+i/PDc11MX6eVM7Fvd+Sdgc9cIwTBz+NKIXs4ZerG6n2gd+ZIZEywqF/aBQ==", + "license": "MIT", + "dependencies": { + "@types/applepayjs": "14.0.9", + "@types/googlepay": "0.7.8", + "classnames": "2.5.1", + "preact": "10.22.1" + } + }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", @@ -3173,6 +3186,12 @@ "node": ">=10.13.0" } }, + "node_modules/@types/applepayjs": { + "version": "14.0.9", + "resolved": "https://registry.npmjs.org/@types/applepayjs/-/applepayjs-14.0.9.tgz", + "integrity": "sha512-xEprYbb0TEP/XIiDPbVnTYpDai8fTFpsQfVSfTd81Is2GOMUy7ie019eyX6Mz2ECxfjoUVKaiGSL577roIeHCg==", + "license": "MIT" + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -3328,6 +3347,12 @@ "@types/send": "*" } }, + "node_modules/@types/googlepay": { + "version": "0.7.8", + "resolved": "https://registry.npmjs.org/@types/googlepay/-/googlepay-0.7.8.tgz", + "integrity": "sha512-EHl7G7jIeit/Px/fi/Du/19SZDvqrRhy0DjJX40Vzs/97m3sro9mzVyAYNC0pfzpWGDY+zPjtTLEfhV+OYWUcQ==", + "license": "MIT" + }, "node_modules/@types/graceful-fs": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", @@ -5789,6 +5814,12 @@ "dev": true, "license": "MIT" }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", + "license": "MIT" + }, "node_modules/clean-css": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", @@ -13681,6 +13712,16 @@ "dev": true, "license": "MIT" }, + "node_modules/preact": { + "version": "10.22.1", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.22.1.tgz", + "integrity": "sha512-jRYbDDgMpIb5LHq3hkI0bbl+l/TQ9UnkdQ0ww+lp+4MMOdqaUYdFc5qeyP+IV8FAd/2Em7drVPeKdQxsiWCf/A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", diff --git a/package.json b/package.json index 7d06ad112..c55259920 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "lint": "vue-cli-service lint" }, "dependencies": { + "@adyen/adyen-web": "^6.27.0", "@iframe-resizer/child": "^5.3.3", "axios": "^0.30.2", "bootstrap": "^5.3.3", diff --git a/src/helpers/adyen-helper.js b/src/helpers/adyen-helper.js new file mode 100644 index 000000000..93bd53bc8 --- /dev/null +++ b/src/helpers/adyen-helper.js @@ -0,0 +1,116 @@ +import { AdyenCheckout } from "@adyen/adyen-web/auto"; +import store from "@/store"; +import globalMethods from "@/global-methods"; +import { endpoints } from "@/constants/endpoints"; +import { AppointmentTypeStrings } from "@/constants/schedule-constants"; + +export async function getNewSession(sessionConfig) {} + +export async function getSessionInfo(sessionId, encryptedSessionResult, pageNameToLog = "") { + const order = store.getters.order; + const location = getLocationInfo(order); + const workOrder = getWorkOrderLastSixDigits(order); + const system = getSourceSystem(); + + const request = { + sessionId: sessionId, + sessionResult: encryptedSessionResult, + zipCodeCtu: location.zipCodeCtu, + workOrderNumber: workOrder, + sourceSystem: system, + }; + + const response = await globalMethods.callHttpClient({ + method: endpoints.GetAdyenSessionResult.method, + endpoint: endpoints.GetAdyenSessionResult.url, + payload: request, + logApiCall: true, + pageNameToLog: pageNameToLog, + }); + + return response?.data; +} + +export async function createAdyenCheckout({ + session: { id, sessionData }, + handlers: { onPaymentCompleted, onPaymentFailed, onError }, + options: { showPayButton = true, amount }, +}) { + const config = { + session: { + id: id, + sessionData: sessionData, + }, + clientKey: "test_WYGT4F5ZT5BRRL5MPWHK6QLYOIXZXROD", + environment: "test", + locale: "en_US", + countryCode: "US", + showPayButton: showPayButton, + translations: { + en_US: { + "creditCard.securityCode.label": "CVV/CVC", + }, + }, + onPaymentCompleted: onPaymentCompleted, + onPaymentFailed: onPaymentFailed, + onError: onError, + }; + + if (amount !== undefined && amount !== null) { + config.amount = { + value: amount, + currency: "USD", + }; + } + + console.log(`Creating checkout with configuration:`); + console.log(config); + + return await AdyenCheckout(config); +} + +function getLocationInfo(order) { + const serviceLocation = order.serviceLocation; + const isMobile = serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE; + if (isMobile) { + return { + address: serviceLocation.address, + address2: serviceLocation.address2, + city: serviceLocation.city, + state: serviceLocation.state, + zipCode: serviceLocation.zipCode, + zipCodeCtu: serviceLocation.zipCodeCtu, + }; + } else { + const providerInfo = serviceLocation.provider.address; + return { + address: providerInfo.streetAddress, + address2: "", + city: providerInfo.city, + state: providerInfo.state, + zipCode: providerInfo.zipCode, + zipCodeCtu: providerInfo.zipCodeCtu, + }; + } +} + +function getWorkOrderLastSixDigits(order) { + const workOrderNumber = order.workOrderNumber ?? ""; + const tokens = workOrderNumber.split("-"); + const lastSegment = tokens[tokens.length - 1] ?? ""; + const numDigits = lastSegment.length; + const numZeroes = 6 - numDigits; + + let result = ""; + for (let i = 0; i < numZeroes; i++) { + result += "0"; + } + + result += lastSegment; + + return result; +} + +function getSourceSystem() { + return "FMG-2.0"; +} diff --git a/src/layouts/payment-adyen/payment-adyen.vue b/src/layouts/payment-adyen/payment-adyen.vue index e38578e54..a2d7651a2 100644 --- a/src/layouts/payment-adyen/payment-adyen.vue +++ b/src/layouts/payment-adyen/payment-adyen.vue @@ -47,6 +47,8 @@ import { storeActions } from "@/constants/store-actions"; import { getAmountDue } from "@/helpers/pricing-helper.js"; import { submitWorkOrder } from "@/helpers/heritage-integration/order-helper.js"; import { paymentMethods } from "@/constants/payment-method-constants"; +import { createAdyenCheckout } from "@/helpers/adyen-helper"; +import { Dropin } from "@adyen/adyen-web/auto"; export default { name: "payment-adyen", @@ -127,45 +129,31 @@ export default { this.sessionId = responseJson.sessionId; - const configuration = { + const checkout = await createAdyenCheckout({ session: session, - clientKey: "test_WYGT4F5ZT5BRRL5MPWHK6QLYOIXZXROD", - environment: "test", - amount: { - value: this.adyenPriceTotal, - currency: "USD", - }, - locale: "en_US", - countryCode: "US", - showPayButton: true, - translations: { - en_US: { - "creditCard.securityCode.label": "CVV/CVC", + handlers: { + onPaymentCompleted: (result, component) => { + console.log(`Payment completed from Adyen.`); + this.handleCompletedPayment(result); + }, + onPaymentFailed: (result, component) => { + console.log(`Payment failed from Adyen`); + this.handleFailedPayment(result); + }, + onError: (error, component) => { + console.log(`Error from Adyen`); + this.handleError(error); }, }, - - onPaymentCompleted: (result, component) => { - console.log(`Payment completed from Adyen.`); - this.handleCompletedPayment(result); + options: { + amount: this.adyenPriceTotal, }, - onPaymentFailed: (result, component) => { - console.log(`Payment failed from Adyen`); - this.handleFailedPayment(result); - }, - onError: (error, component) => { - console.log(`Error from Adyen`); - this.handleError(error); - }, - }; + }); - console.log(`Configuration =`); - console.log(configuration); + console.log(checkout); - // eslint-disable-next-line - const checkOut = await AdyenWeb.AdyenCheckout(configuration); - window.checkout = checkOut; - // eslint-disable-next-line - const dropin = new AdyenWeb.Dropin(checkOut, { + window.checkout = checkout; + const dropin = new Dropin(checkout, { paymentMethodsConfiguration: { ideal: { showImage: true, @@ -239,7 +227,11 @@ export default { // Save payment type. // For now, CC. // Need to determine payment type from Adyen response - await this.dispatchStoreAction(storeActions.SAVE_PAYMENT_METHOD_CHOICE, paymentMethods.CREDIT_CARD, false); + await this.dispatchStoreAction( + storeActions.SAVE_PAYMENT_METHOD_CHOICE, + paymentMethods.CREDIT_CARD, + false + ); await baseMixin.methods.dispatchStoreAction(storeActions.SAVE_CCTOKEN, ccToken, false); await baseMixin.methods.dispatchStoreAction( @@ -279,7 +271,6 @@ export default { } catch (error) { console.log(`Error getting session info after failure.`); } - }, handleError(error) {}, @@ -312,18 +303,18 @@ export default { referralSequenceNumber: this.$store.getters.order.referralSequenceNumber, workOrderNumber: this.workOrderNumberLastSixDigits, zipCodeCtu: this.locationInfo.zipCodeCtu, - amount: this.adyenPriceTotal, // TODO + amount: this.adyenPriceTotal, city: this.locationInfo.city, houseNumberOrName: this.splitStreetAddress.number, street: this.splitStreetAddress.street, zipCode: this.locationInfo.zipCode, stateOrProvince: this.locationInfo.state, - returnUrl: "http://localhost:8080/fmg/confirmation", + returnUrl: "http://localhost:8080/fmg/virtual/payment/return", email: this.$store.getters.order.customer.emailAddress, IP: "127.0.0.1", // TODO firstName: this.$store.getters.order.customer.firstName, lastName: this.$store.getters.order.customer.lastName, - idempotencyKey: crypto.randomUUID(), // `${this.$store.getters.order.referralCorrelationId}-${this.sourceSystem}`, + idempotencyKey: `${this.$store.getters.order.referralCorrelationId}-${this.sourceSystem}`, }; }, diff --git a/src/router/constants/routes.js b/src/router/constants/routes.js index 4b51ff857..5090df2b3 100644 --- a/src/router/constants/routes.js +++ b/src/router/constants/routes.js @@ -93,6 +93,11 @@ export const routeData = { name: "payment-adyen", path: "/payment-adyen", }, + PAYMENT_ADYEN_RETURN: { + name: "adyen-return", + path: "/virtual/payment/return", + virtual: true, + }, CONFIRMATION: { name: "confirmation", path: "/confirmation", diff --git a/src/router/methods/route-logic/adyen-return.js b/src/router/methods/route-logic/adyen-return.js new file mode 100644 index 000000000..9bf1940ba --- /dev/null +++ b/src/router/methods/route-logic/adyen-return.js @@ -0,0 +1,73 @@ +import { AdyenCheckout } from "@adyen/adyen-web"; +import { routeData } from "@router/constants/routes"; +import { createAdyenCheckout, getSessionInfo } from "@/helpers/adyen-helper"; + +export async function adyenReturnBeforeEnter(to, from) { + console.log(`In adyen return.`); + console.log(JSON.parse(JSON.stringify(to))); + + const sessionId = to?.query?.sessionId; + const redirectResult = to?.query?.redirectResult; + + if (sessionId && redirectResult) { + const result = await finalizeAdyenPayment(sessionId, redirectResult); + console.log(`Out of promise`); + console.log(result); + + const sessionInfo = await getSessionInfo(sessionId, result.sessionResult); + + console.log(`Got session info:`); + console.log(sessionInfo); + + // TODO once afterpay info is returned, create cc token and submit order. + + return { + name: routeData.CONFIRMATION.name, + replace: true, + }; + } + + return { + name: routeData.ERROR.name, + replace: true, + }; +} + +function finalizeAdyenPayment(sessionId, redirectResult) { + return new Promise((resolve, reject) => { + createAdyenCheckout({ + session: { + id: sessionId, + }, + handlers: { + onPaymentCompleted: (result, component) => { + console.log(`Payment successful`); + console.log(result); + resolve(result); + }, + onPaymentFailed: (result, component) => { + console.log(`Payment failed`); + console.log(result); + reject("Payment failed from Adyen"); + }, + onError: (error, component) => { + console.log(`Error occured`); + console.log(error); + reject("Error from Adyen"); + }, + }, + options: {}, + }).then( + (checkout) => { + checkout.submitDetails({ + details: { + redirectResult: redirectResult, + }, + }); + }, + (error) => { + reject(error); + } + ); + }); +} diff --git a/src/router/methods/routes.js b/src/router/methods/routes.js index cf0392ccf..ec6077270 100644 --- a/src/router/methods/routes.js +++ b/src/router/methods/routes.js @@ -13,6 +13,7 @@ import { restartBeforeEnter } from "@/router/methods/route-logic/restart"; import { paymentMethodBeforeEnter } from "@/router/methods/route-logic/payment-method"; import { paymentBeforeEnter } from "@/router/methods/route-logic/payment"; import { serviceLocationBeforeEnter } from "@/router/methods/route-logic/service-location"; +import { adyenReturnBeforeEnter } from "@/router/methods/route-logic/adyen-return"; export const routes = [ // Non-virtual pages. @@ -48,4 +49,5 @@ export const routes = [ createVirtualRoute(routeData.AUTO_ROUTE, autoRouteBeforeEnter), createVirtualRoute(routeData.ERROR, errorBeforeEnter), createVirtualRoute(routeData.RESTART, restartBeforeEnter), + createVirtualRoute(routeData.PAYMENT_ADYEN_RETURN, adyenReturnBeforeEnter), ]; From 8385928d8c5c14061f0acf9b5b076888b4c3c94d Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Tue, 16 Dec 2025 11:10:56 -0500 Subject: [PATCH 6/6] Changes for Dev --- src/constants/application-config.js | 1 + src/constants/experiments.js | 1 + src/layouts/payment-adyen/payment-adyen.vue | 26 ++++++++++++++----- src/layouts/payment-method/payment-method.vue | 14 +++++++--- src/router/constants/navigation-scenarios.js | 1 + src/router/constants/routing-table.js | 4 +++ .../methods/route-logic/adyen-return.js | 2 +- 7 files changed, 39 insertions(+), 10 deletions(-) diff --git a/src/constants/application-config.js b/src/constants/application-config.js index 93a6f70d8..b739d9ff9 100644 --- a/src/constants/application-config.js +++ b/src/constants/application-config.js @@ -22,6 +22,7 @@ const applicationConfig = { PIA_ERROR_URL: location.protocol + "//" + location.host + "/fmg/payment-pia-return?src=concept-funnel", CONFIRMATION_URL: location.protocol + "//" + location.host + "/fmg/confirmation", + PIA_ADYEN_RETURN_URL: location.protocol + "//" + location.host + "/fmg/virtual/payment/return", GOOGLE_CALENDAR: "https://www.google.com/calendar/render?action=TEMPLATE", YAHOO_CALENDAR: "https://calendar.yahoo.com/?v=60", OUTLOOK_CALENDAR: diff --git a/src/constants/experiments.js b/src/constants/experiments.js index f358fc89f..0ca3987f0 100644 --- a/src/constants/experiments.js +++ b/src/constants/experiments.js @@ -33,6 +33,7 @@ const experimentSettings = { SHOW_PM_MOBILE_DAYS: "Show_PMmobileDays", SHOW_NO_PM_MOBILE_DAYS: "Show_NoPMmobileDays", SHOW_NO_MOBILE_AVAILABLE_DAYS: "Show_NoMobileAvailableDays", + USE_ADYEN_PAYMENT: "UseAdyenPayment", }; const experimentTriggers = { diff --git a/src/layouts/payment-adyen/payment-adyen.vue b/src/layouts/payment-adyen/payment-adyen.vue index a2d7651a2..5e74692d5 100644 --- a/src/layouts/payment-adyen/payment-adyen.vue +++ b/src/layouts/payment-adyen/payment-adyen.vue @@ -49,6 +49,7 @@ import { submitWorkOrder } from "@/helpers/heritage-integration/order-helper.js" import { paymentMethods } from "@/constants/payment-method-constants"; import { createAdyenCheckout } from "@/helpers/adyen-helper"; import { Dropin } from "@adyen/adyen-web/auto"; +import { applicationConfig } from "@/constants/application-config"; export default { name: "payment-adyen", @@ -279,20 +280,29 @@ export default { await this.dispatchStoreAction(storeActions.RESET_SAVE_SESSION_PROMISE); try { await submitWorkOrder({ - pageNameToLog: "payment-adyen", + pageNameToLog: this.pageName, submitAfterSave: true, }); } 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.$refs.loadingModal.isModalVisible = false; + this.$router.handleSoftError(errorPayload); } - //this.$refs.loadingModal.isModalVisible = false; // clear order - // navigate forward + await baseMixin.methods.dispatchStoreAction(storeActions.CREATE_SUBMITTED_STATE); + this.$router.navigateWithoutSaving( + this.navigationScenarios.CLICKED_FORWARD, + this.pageName + ); }, }, @@ -309,12 +319,12 @@ export default { street: this.splitStreetAddress.street, zipCode: this.locationInfo.zipCode, stateOrProvince: this.locationInfo.state, - returnUrl: "http://localhost:8080/fmg/virtual/payment/return", + returnUrl: applicationConfig.PIA_ADYEN_RETURN_URL, email: this.$store.getters.order.customer.emailAddress, IP: "127.0.0.1", // TODO firstName: this.$store.getters.order.customer.firstName, lastName: this.$store.getters.order.customer.lastName, - idempotencyKey: `${this.$store.getters.order.referralCorrelationId}-${this.sourceSystem}`, + idempotencyKey: this.idempotencyKey, }; }, @@ -373,6 +383,10 @@ export default { }; }, + idempotencyKey() { + return `${this.$store.getters.order.referralCorrelationId}-${this.sourceSystem}`; + }, + sourceSystem() { return "FMG-2.0"; }, diff --git a/src/layouts/payment-method/payment-method.vue b/src/layouts/payment-method/payment-method.vue index 955d106b5..0fa02e80a 100644 --- a/src/layouts/payment-method/payment-method.vue +++ b/src/layouts/payment-method/payment-method.vue @@ -170,6 +170,7 @@ import { consumeQueryFromStash } from "@/router/methods/helpers/querystring-stas import { debugLog } from "@/helpers/debug-log-helper"; import { ErrorMessage } from "vee-validate"; import { Field } from "vee-validate"; +import experimentMixin from "../../mixins/experiment-mixin"; defineRule("payment-method-required", required(errorMessages.OPTION_REQUIRED)); defineRule("recal-ack-required", required(errorMessages.RECAL_ACK_REQUIRED)); @@ -609,10 +610,17 @@ export default { } this.dispatchStoreAction(storeActions.RESET_SAVE_SESSION_PROMISE); - this.$router.navigateWithoutSaving( - this.navigationScenarios.CLICKED_PAY_NOW, - this.pageName + + const shouldUseAdyen = experimentMixin.methods.hasSettingEqualTo( + experimentSettings.USE_ADYEN_PAYMENT, + "true" ); + + const scenarioName = shouldUseAdyen + ? this.navigationScenarios.CLICKED_PAY_NOW_ADYEN + : this.navigationScenarios.CLICKED_PAY_NOW; + + this.$router.navigateWithoutSaving(scenarioName, this.pageName); }, async evaluatePromosAndTaxItemsOnOrder() { //Revalidate promos if there are any inactive, or active promos diff --git a/src/router/constants/navigation-scenarios.js b/src/router/constants/navigation-scenarios.js index b660afca3..c2247d59b 100644 --- a/src/router/constants/navigation-scenarios.js +++ b/src/router/constants/navigation-scenarios.js @@ -61,6 +61,7 @@ const navigationScenarios = { // Payment CLICKED_INSURANCE: "CLICKED_INSURANCE", CLICKED_PAY_NOW: "CLICKED_PAY_NOW", + CLICKED_PAY_NOW_ADYEN: "CLICKED_PAY_NOW_ADYEN", CLICKED_PAY_LATER: "CLICKED_PAY_LATER", PIA_ERROR: "PIA_ERROR", PIA_CC_ERROR: "PIA_CC_ERROR", diff --git a/src/router/constants/routing-table.js b/src/router/constants/routing-table.js index cd41e8fcf..192e1572e 100644 --- a/src/router/constants/routing-table.js +++ b/src/router/constants/routing-table.js @@ -535,6 +535,10 @@ const routingTable = function () { }, { scenario: navigationScenarios.CLICKED_PAY_NOW, + destinationPageData: routeData.PAYMENT, + }, + { + scenario: navigationScenarios.CLICKED_PAY_NOW_ADYEN, destinationPageData: routeData.PAYMENT_ADYEN, }, { diff --git a/src/router/methods/route-logic/adyen-return.js b/src/router/methods/route-logic/adyen-return.js index 9bf1940ba..a516518ac 100644 --- a/src/router/methods/route-logic/adyen-return.js +++ b/src/router/methods/route-logic/adyen-return.js @@ -1,5 +1,5 @@ import { AdyenCheckout } from "@adyen/adyen-web"; -import { routeData } from "@router/constants/routes"; +import { routeData } from "@/router/constants/routes"; import { createAdyenCheckout, getSessionInfo } from "@/helpers/adyen-helper"; export async function adyenReturnBeforeEnter(to, from) {