-
{isStartupProgramAvailable && startupProgramAmountText && (
diff --git a/packages/manager/apps/pci-project/src/pages/creation/utils/paymentEvents.ts b/packages/manager/apps/pci-project/src/pages/creation/utils/paymentEvents.ts
new file mode 100644
index 000000000000..d53ffbc03c25
--- /dev/null
+++ b/packages/manager/apps/pci-project/src/pages/creation/utils/paymentEvents.ts
@@ -0,0 +1,46 @@
+/**
+ * Utility functions for WillPayment DOM events
+ * Centralizes DOM manipulation for better testability
+ */
+
+const EVENT_BUS_ID = 'will-payment-event-bus';
+const SAVE_PAYMENT_METHOD_EVENT = 'GO_SAVE_PAYMENT_METHOD';
+const REGISTERED_PM_EVENT = 'WP::USER_ACTION::REGISTERED_PM_SELECTED';
+
+/**
+ * Triggers the save payment method event via DOM
+ * @returns boolean indicating if the event was dispatched successfully
+ */
+export const triggerSavePaymentMethodEvent = (): boolean => {
+ const eventBus = document.getElementById(EVENT_BUS_ID);
+ if (eventBus) {
+ eventBus.dispatchEvent(new CustomEvent(SAVE_PAYMENT_METHOD_EVENT));
+ return true;
+ }
+ return false;
+};
+
+/**
+ * Sets up listener for registered payment method selection
+ * @param handler - Function to handle the event
+ * @returns cleanup function to remove the listener
+ */
+export const setupRegisteredPaymentMethodListener = (
+ handler: (event: CustomEvent) => void,
+): (() => void) | null => {
+ const eventBus = document.getElementById(EVENT_BUS_ID);
+ if (!eventBus) {
+ return null;
+ }
+
+ const eventHandler = (event: Event) => {
+ handler(event as CustomEvent);
+ };
+
+ eventBus.addEventListener(REGISTERED_PM_EVENT, eventHandler);
+
+ // Return cleanup function
+ return () => {
+ eventBus.removeEventListener(REGISTERED_PM_EVENT, eventHandler);
+ };
+};
diff --git a/packages/manager/apps/pci-project/src/pages/creation/utils/paymentLogic.ts b/packages/manager/apps/pci-project/src/pages/creation/utils/paymentLogic.ts
new file mode 100644
index 000000000000..231b610d42c7
--- /dev/null
+++ b/packages/manager/apps/pci-project/src/pages/creation/utils/paymentLogic.ts
@@ -0,0 +1,45 @@
+import {
+ ComponentStatus,
+ GlobalStateStatus,
+ PaymentMethodStatus,
+} from '@/types/WillPayment.type';
+
+/**
+ * Pure business logic functions for payment state validation
+ * These functions are easily testable and reusable
+ */
+
+/**
+ * Determines if payment method save is required based on component status
+ */
+export const isPaymentMethodSaveRequired = (
+ globalStateStatus: GlobalStateStatus | null,
+): boolean => {
+ return (
+ globalStateStatus?.componentStatus === ComponentStatus.READY_TO_GO_FORWARD
+ );
+};
+
+/**
+ * Determines if payment method has been saved successfully
+ */
+export const isPaymentMethodSaved = (
+ globalStateStatus: GlobalStateStatus | null,
+): boolean => {
+ return (
+ globalStateStatus?.componentStatus ===
+ ComponentStatus.PAYMENT_METHOD_SAVED ||
+ globalStateStatus?.paymentMethodStatus ===
+ PaymentMethodStatus.PAYMENT_METHOD_SAVED
+ );
+};
+
+/**
+ * Determines if submission should be enabled
+ */
+export const isSubmittingEnabled = (
+ hasDefaultPaymentMethod: boolean,
+ saveRequired: boolean,
+): boolean => {
+ return hasDefaultPaymentMethod || saveRequired;
+};
diff --git a/packages/manager/apps/pci-project/src/setupTests.tsx b/packages/manager/apps/pci-project/src/setupTests.tsx
index b9e1d518960b..80a16afd8be9 100644
--- a/packages/manager/apps/pci-project/src/setupTests.tsx
+++ b/packages/manager/apps/pci-project/src/setupTests.tsx
@@ -621,6 +621,12 @@ vi.mock('@ovh-ux/manager-react-shell-client', async () => {
getURL: mockGetURL,
},
},
+ environment: {
+ getUser: () => ({
+ ovhSubsidiary: 'FR',
+ language: 'fr_FR',
+ }),
+ },
}),
useOvhTracking: () => ({
trackClick: vi.fn(),
diff --git a/packages/manager/apps/pci-project/src/types/WillPayment.type.ts b/packages/manager/apps/pci-project/src/types/WillPayment.type.ts
new file mode 100644
index 000000000000..0f169e140ed6
--- /dev/null
+++ b/packages/manager/apps/pci-project/src/types/WillPayment.type.ts
@@ -0,0 +1,48 @@
+export enum PaymentMethodStatus {
+ PROCESSING = 'PROCESSING', // Waiting a backend return
+ REGISTERED = 'REGISTERED', // Done, the PM is ready to rumble
+ ERROR = 'ERROR', // Something when't wrong with the PM
+ PENDING = 'PENDING', // Waiting something, generally a user action
+ CHALLENGE_WAITING = 'CHALLENGE_WAITING', // Waiting for user action required by the PSP
+ CHALLENGE_OK = 'CHALLENGE_OK', // When challenge required by the PSP is ok
+ CHALLENGE_ERROR = 'CHALLENGE_ERROR', // When challenge required by the PSP and return an Error
+ CHALLENGE_CANCELED = 'CHALLENGE_CANCELED', // When challenge required by the PSP is canceled by the user
+ CHALLENGE_REFUSED = 'CHALLENGE_REFUSED', // When challenge required by the PSP and payment was refused
+ PAYMENT_METHOD_SAVED = 'PAYMENT_METHOD_SAVED', // When a payment is a success
+}
+
+export enum ComponentStatus {
+ PENDING = 'PENDING', // Waiting something, generally a user action
+ LOADING = 'LOADING', // When loading external resource
+ ERROR = 'ERROR', // Something when't wrong
+ WAITING_USER_ACTION = 'WAITING_USER_ACTION', // Waiting a user action
+ PROCESSING = 'PROCESSING', // Work in progress…
+ READY_TO_GO_FORWARD = 'READY_TO_GO_FORWARD', // PM and PSP are ready, the UI can go forward or wait user action
+ PAYMENT_SUCCESS = 'PAYMENT_SUCCESS', // When a payment is a success
+ PAYMENT_METHOD_SAVED = 'PAYMENT_METHOD_SAVED', // When a payment method is registered or updated
+}
+
+export type TCreditData = {
+ isCredit?: boolean;
+ creditAmount?: { value: number; text: string; currencyCode: string };
+};
+
+export type GlobalStateStatus = {
+ componentStatus: ComponentStatus;
+ paymentMethodStatus: PaymentMethodStatus;
+ error?: string | null;
+ data?: TCreditData | unknown;
+};
+
+export type TWillPaymentConfig = {
+ baseUrl: string;
+ onChange: (param: GlobalStateStatus) => void;
+ subsidiary?: string;
+ language?: string;
+ eventBus?: Element;
+ hostApp?: 'manager' | 'pci';
+ logging?: {
+ environment?: string;
+ userId?: string;
+ };
+};
diff --git a/packages/manager/apps/pci-project/src/types/module-federation.d.ts b/packages/manager/apps/pci-project/src/types/module-federation.d.ts
new file mode 100644
index 000000000000..c9415ca5c555
--- /dev/null
+++ b/packages/manager/apps/pci-project/src/types/module-federation.d.ts
@@ -0,0 +1,10 @@
+import { TWillPaymentConfig } from './WillPayment.type';
+
+declare module 'willPayment/WillPayment' {
+ export default function setupWillPayment(
+ slot: HTMLSlotElement,
+ config: {
+ configuration: TWillPaymentConfig;
+ },
+ ): void;
+}
diff --git a/packages/manager/apps/pci-project/tsconfig.json b/packages/manager/apps/pci-project/tsconfig.json
index 0bf657fc7a31..5a8ebe288a94 100644
--- a/packages/manager/apps/pci-project/tsconfig.json
+++ b/packages/manager/apps/pci-project/tsconfig.json
@@ -25,7 +25,8 @@
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"],
- "react": ["./node_modules/@types/react"]
+ "react": ["./node_modules/@types/react"],
+ "willPayment/WillPayment": ["./src/types/module-federation.d.ts"]
}
},
"include": ["src"],
diff --git a/packages/manager/apps/pci-project/vite.config.mjs b/packages/manager/apps/pci-project/vite.config.mjs
index f33ab6dc98cd..08e548c2b08c 100644
--- a/packages/manager/apps/pci-project/vite.config.mjs
+++ b/packages/manager/apps/pci-project/vite.config.mjs
@@ -1,8 +1,28 @@
import { defineConfig } from 'vite';
import { getBaseConfig } from '@ovh-ux/manager-vite-config';
import { resolve } from 'path';
+import federation from '@originjs/vite-plugin-federation';
+
+const getWillPaymentEntryUrl = () => {
+ if (process.env.LABEU || process.env.NODE_ENV === 'development') {
+ return 'https://www.build-ovh.com/order/payment/assets/remoteEntry.js';
+ }
+
+ return '/order/payment/assets/remoteEntry.js';
+};
export default defineConfig({
...getBaseConfig(),
root: resolve(process.cwd()),
+ plugins: [
+ ...getBaseConfig().plugins,
+ federation({
+ name: 'host-app',
+ remotes: {
+ willPayment: getWillPaymentEntryUrl(),
+ },
+ }),
+ ],
});
+
+
diff --git a/yarn.lock b/yarn.lock
index b65a648c4d6c..1783ca95d77d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7075,6 +7075,14 @@
resolved "https://registry.yarnpkg.com/@orchidjs/unicode-variants/-/unicode-variants-1.1.2.tgz#1fd71791a67fdd1591ebe0dcaadd3964537a824e"
integrity sha512-5DobW1CHgnBROOEpFlEXytED5OosEWESFvg/VYmH0143oXcijYTprRYJTs+55HzGM4IqxiLFSuqEzu9mPNwVsA==
+"@originjs/vite-plugin-federation@^1.3.9":
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/@originjs/vite-plugin-federation/-/vite-plugin-federation-1.4.1.tgz#e6abc8f18f2cf82783eb87853f4d03e6358b43c2"
+ integrity sha512-Uo08jW5pj1t58OUKuZNkmzcfTN2pqeVuAWCCiKf/75/oll4Efq4cHOqSE1FXMlvwZNGDziNdDyBbQ5IANem3CQ==
+ dependencies:
+ estree-walker "^3.0.2"
+ magic-string "^0.27.0"
+
"@ovh-ux/manager-common-translations@^0.10.0":
version "0.10.0"
resolved "https://registry.yarnpkg.com/@ovh-ux/manager-common-translations/-/manager-common-translations-0.10.0.tgz#b135fbf4c9fd64a6c2045ad54091a1982fbad2d0"
@@ -7228,7 +7236,7 @@
i18next "^23.8.2"
i18next-http-backend "^2.4.2"
-"@ovh-ux/manager-react-shell-client@^0.9.3":
+"@ovh-ux/manager-react-shell-client@^0.9.1", "@ovh-ux/manager-react-shell-client@^0.9.3":
version "0.9.3"
resolved "https://registry.yarnpkg.com/@ovh-ux/manager-react-shell-client/-/manager-react-shell-client-0.9.3.tgz#4f6f40ae85076390ad1c54b5c3a24207d1fab158"
integrity sha512-i86lAEyYPY2iCoGCpm8DVF0IuyIxYO+Iccav0/cw0ItUEexsvNHbDWRu9DLlIIS3Vknrx/ualDg0cEbPEA5lZw==
@@ -19351,7 +19359,7 @@ estree-walker@^2.0.1, estree-walker@^2.0.2:
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
-estree-walker@^3.0.3:
+estree-walker@^3.0.2, estree-walker@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-3.0.3.tgz#67c3e549ec402a487b4fc193d1953a524752340d"
integrity sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==