Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: sends modal event data to modal wrapper for use in hooks #1170

Open
wants to merge 7 commits into
base: feature/DTCRCMERC-3611-modal-lander-v6
Choose a base branch
from

Conversation

danzhaaspaypal
Copy link
Contributor

@danzhaaspaypal danzhaaspaypal commented Jan 28, 2025

Description

Sends messages to browser modal wrapper on modal events onShow, onClose, and onCalculate.

Screenshots

Testing instructions

See corresponding PR 637 in core-web-sdk for testing instructions.

@danzhaaspaypal danzhaaspaypal marked this pull request as ready for review February 26, 2025 19:53
Comment on lines +41 to +54
export class PostMessengerMessage {
constructor(type, eventName, eventPayload) {
this.eventName = eventName;
this.id = createUUID();

if (type === 'ack') {
this.type = POSTMESSENGER_EVENT_TYPES.ACK;
this.eventPayload = POSTMESSENGER_ACK_PAYLOAD;
} else if (type === 'message') {
this.type = POSTMESSENGER_EVENT_TYPES.MESSAGE;
this.eventPayload = eventPayload;
}
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be a class or could it just be a createMessage(type, eventName, eventPayload) that returns a simple object? Using a class here without any methods that just holds data seems like may be reaching for a more complex data structure.

If we include sendEvent as a method on this class then it makes more sense. Maybe this could look like

const event = new PostMessengerEvent(/* ... */);
event.sendTo(clientOrigin);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I appreciate the suggestion building off my idea to make it an object. Considered adding a method to that class, then realized we would get a DataCloneError due to postMessage trying to clone the object and it having a function. So I think your suggestion we toss the class in favor of a function returning a simple object is the way to go.

Comment on lines +10 to +24
function createUUID() {
// crypto.randomUUID() is only available in HTTPS secure environments and modern browsers
if (typeof crypto !== 'undefined' && crypto && crypto.randomUUID instanceof Function) {
return crypto.randomUUID();
}

const validChars = '0123456789abcdefghijklmnopqrstuvwxyz';
const stringLength = 32;
let randomId = '';
for (let index = 0; index < stringLength; index++) {
const randomIndex = Math.floor(Math.random() * validChars.length);
randomId += validChars.charAt(randomIndex);
}
return randomId;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left a similar comment on a previous PR. Is there an existing util in belter that serves the same purpose that we could leverage?

Copy link
Contributor Author

@danzhaaspaypal danzhaaspaypal Mar 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #1161 for response

Comment on lines +10 to +14
export const POSTMESSENGER_EVENT_NAMES = {
CALCULATE: 'paypal-messages-modal-calculate',
CLOSE: 'paypal-messages-modal-close',
SHOW: 'paypal-messages-modal-show'
};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be moved into the postMessage file to keep it all together?

return randomId;
}

export function sendEvent(payload, trustedOrigin) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The trustedOrigin originates from props.origin which won't exist for all integration (e.g. lander pages). Should we have some quick short-circuit logic that early returns if the origin is undefined or empty?

// send event ack so PostMessenger will stop reposting event
sendEventAck(id, clientOrigin);
// send event ack with original event id so PostMessenger will stop reposting event
sendEvent(new PostMessengerMessage('ack', id));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this missing the clientOrigin as the second argument?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oop, will fix

this.eventPayload = POSTMESSENGER_ACK_PAYLOAD;
} else if (type === 'message') {
this.type = POSTMESSENGER_EVENT_TYPES.MESSAGE;
this.eventPayload = eventPayload;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to be extra cautious here. This is the data that we are exposing publicly for anyone to access. Right now nothing sensitive is being exposed, but in the future that could accidentally change. Can we add an extra layer of validation here by only allowing explicitly approved fields to be included? Something like createSafePayload(eventPayload) that returns an object that only includes allow listed fields managed by the createSafePayload function. We should also add an explicit comment that explains the context and reason for this as it may seem redundant.

@@ -115,10 +129,10 @@ const setupBrowser = props => {
});
},
onClose: ({ linkName }) => {
if (isIframe && document.referrer) {
const targetOrigin = new window.URL(document.referrer).origin;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing this will break some existing integrations as this value is used by integrations not passing in the props.origin manually today. Does this by chance work in place of the manual origin query param we introduced for our needs to simplify the new integration?`

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh good catch! I can make the document.referrer's origin a fallback in the case the origin isn't passed manually so both cases are covered

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants