Skip to content

Commit

Permalink
clients: prevent embedded checkout to be closed while processing the …
Browse files Browse the repository at this point in the history
…checkout
  • Loading branch information
frankie567 committed Dec 18, 2024
1 parent eaeeb98 commit 2811f8a
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 3 deletions.
5 changes: 5 additions & 0 deletions clients/.changeset/weak-maps-occur.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@polar-sh/checkout': patch
---

Prevent embed to be closed while checkout is processing payment
9 changes: 9 additions & 0 deletions clients/apps/web/src/components/Checkout/CheckoutForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -791,6 +791,15 @@ const StripeCheckoutForm = (props: CheckoutFormProps) => {

setLoading(true)

if (checkout.embed_origin) {
PolarEmbedCheckout.postMessage(
{
event: 'confirmed',
},
checkout.embed_origin,
)
}

if (!checkout.is_payment_form_required) {
let updatedCheckout: CheckoutPublicConfirmed
setLoadingLabel('Processing order')
Expand Down
4 changes: 2 additions & 2 deletions clients/examples/checkout-embed/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
<body>
<div class="container">
<h1>My Product</h1>
<a href="http://127.0.0.1:8000/v1/checkout-links/polar_cl_3zLL3v_5SbV0XTIMtqGa62S2duWqV3DbY8T9vbAPL9A/redirect"
<a href="http://127.0.0.1:8000/v1/checkout-links/polar_cl_F08rlnewQlZ9mzASkZ2Zh7nNzrkR3LOckbqcuAoSbOw/redirect"
data-polar-checkout data-polar-checkout-theme="light" role="button" class="primary">
Purchase (light mode)
</a>
<a href="http://127.0.0.1:8000/v1/checkout-links/polar_cl_3zLL3v_5SbV0XTIMtqGa62S2duWqV3DbY8T9vbAPL9A/redirect"
<a href="http://127.0.0.1:8000/v1/checkout-links/polar_cl_F08rlnewQlZ9mzASkZ2Zh7nNzrkR3LOckbqcuAoSbOw/redirect"
data-polar-checkout data-polar-checkout-theme="dark" role="button" class="secondary">
Purchase (dark mode)
</a>
Expand Down
41 changes: 40 additions & 1 deletion clients/packages/checkout/src/embed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ interface EmbedCheckoutMessageClose {
event: 'close'
}

/**
* Message sent to the parent window when the checkout is confirmed.
*
* At that point, the parent window shouldn't allow to close the checkout.
*/
interface EmbedCheckoutMessageConfirmed {
event: 'confirmed'
}

/**
* Message sent to the parent window when the checkout is successfully completed.
*
Expand All @@ -31,6 +40,7 @@ interface EmbedCheckoutMessageSuccess {
type EmbedCheckoutMessage =
| EmbedCheckoutMessageLoaded
| EmbedCheckoutMessageClose
| EmbedCheckoutMessageConfirmed
| EmbedCheckoutMessageSuccess

const isEmbedCheckoutMessage = (
Expand All @@ -46,16 +56,19 @@ class EmbedCheckout {
private iframe: HTMLIFrameElement
private loader: HTMLDivElement
private loaded: boolean
private closable: boolean
private eventTarget: EventTarget

public constructor(iframe: HTMLIFrameElement, loader: HTMLDivElement) {
this.iframe = iframe
this.loader = loader
this.loaded = false
this.closable = true
this.eventTarget = new EventTarget()
this.initWindowListener()
this.addEventListener('loaded', this.loadedListener.bind(this))
this.addEventListener('close', this.closeListener.bind(this))
this.addEventListener('confirmed', this.confirmedListener.bind(this))
this.addEventListener('success', this.successListener.bind(this))
}

Expand Down Expand Up @@ -206,6 +219,11 @@ class EmbedCheckout {
listener: (event: CustomEvent<EmbedCheckoutMessageClose>) => void,
options?: AddEventListenerOptions | boolean,
): void
public addEventListener(
type: 'confirmed',
listener: (event: CustomEvent<EmbedCheckoutMessageConfirmed>) => void,
options?: AddEventListenerOptions | boolean,
): void
public addEventListener(
type: 'success',
listener: (event: CustomEvent<EmbedCheckoutMessageSuccess>) => void,
Expand Down Expand Up @@ -233,6 +251,10 @@ class EmbedCheckout {
type: 'close',
listener: (event: CustomEvent<EmbedCheckoutMessageClose>) => void,
): void
public removeEventListener(
type: 'confirmed',
listener: (event: CustomEvent<EmbedCheckoutMessageConfirmed>) => void,
): void
public removeEventListener(
type: 'success',
listener: (event: CustomEvent<EmbedCheckoutMessageSuccess>) => void,
Expand Down Expand Up @@ -276,7 +298,23 @@ class EmbedCheckout {
if (event.defaultPrevented) {
return
}
this.close()
if (this.closable) {
this.close()
}
}

/**
* Default listener for the `confirmed` event.
*
* This listener will set a flag to prevent the parent window from closing the embedded checkout.
*/
private confirmedListener(
event: CustomEvent<EmbedCheckoutMessageConfirmed>,
): void {
if (event.defaultPrevented) {
return
}
this.closable = false
}

/**
Expand All @@ -290,6 +328,7 @@ class EmbedCheckout {
if (event.defaultPrevented) {
return
}
this.closable = true
if (event.detail.redirect) {
window.location.href = event.detail.successURL
}
Expand Down

0 comments on commit 2811f8a

Please sign in to comment.