Skip to content

Commit 2811f8a

Browse files
committed
clients: prevent embedded checkout to be closed while processing the checkout
1 parent eaeeb98 commit 2811f8a

File tree

4 files changed

+56
-3
lines changed

4 files changed

+56
-3
lines changed

clients/.changeset/weak-maps-occur.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@polar-sh/checkout': patch
3+
---
4+
5+
Prevent embed to be closed while checkout is processing payment

clients/apps/web/src/components/Checkout/CheckoutForm.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -791,6 +791,15 @@ const StripeCheckoutForm = (props: CheckoutFormProps) => {
791791

792792
setLoading(true)
793793

794+
if (checkout.embed_origin) {
795+
PolarEmbedCheckout.postMessage(
796+
{
797+
event: 'confirmed',
798+
},
799+
checkout.embed_origin,
800+
)
801+
}
802+
794803
if (!checkout.is_payment_form_required) {
795804
let updatedCheckout: CheckoutPublicConfirmed
796805
setLoadingLabel('Processing order')

clients/examples/checkout-embed/index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@
1111
<body>
1212
<div class="container">
1313
<h1>My Product</h1>
14-
<a href="http://127.0.0.1:8000/v1/checkout-links/polar_cl_3zLL3v_5SbV0XTIMtqGa62S2duWqV3DbY8T9vbAPL9A/redirect"
14+
<a href="http://127.0.0.1:8000/v1/checkout-links/polar_cl_F08rlnewQlZ9mzASkZ2Zh7nNzrkR3LOckbqcuAoSbOw/redirect"
1515
data-polar-checkout data-polar-checkout-theme="light" role="button" class="primary">
1616
Purchase (light mode)
1717
</a>
18-
<a href="http://127.0.0.1:8000/v1/checkout-links/polar_cl_3zLL3v_5SbV0XTIMtqGa62S2duWqV3DbY8T9vbAPL9A/redirect"
18+
<a href="http://127.0.0.1:8000/v1/checkout-links/polar_cl_F08rlnewQlZ9mzASkZ2Zh7nNzrkR3LOckbqcuAoSbOw/redirect"
1919
data-polar-checkout data-polar-checkout-theme="dark" role="button" class="secondary">
2020
Purchase (dark mode)
2121
</a>

clients/packages/checkout/src/embed.ts

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,15 @@ interface EmbedCheckoutMessageClose {
1414
event: 'close'
1515
}
1616

17+
/**
18+
* Message sent to the parent window when the checkout is confirmed.
19+
*
20+
* At that point, the parent window shouldn't allow to close the checkout.
21+
*/
22+
interface EmbedCheckoutMessageConfirmed {
23+
event: 'confirmed'
24+
}
25+
1726
/**
1827
* Message sent to the parent window when the checkout is successfully completed.
1928
*
@@ -31,6 +40,7 @@ interface EmbedCheckoutMessageSuccess {
3140
type EmbedCheckoutMessage =
3241
| EmbedCheckoutMessageLoaded
3342
| EmbedCheckoutMessageClose
43+
| EmbedCheckoutMessageConfirmed
3444
| EmbedCheckoutMessageSuccess
3545

3646
const isEmbedCheckoutMessage = (
@@ -46,16 +56,19 @@ class EmbedCheckout {
4656
private iframe: HTMLIFrameElement
4757
private loader: HTMLDivElement
4858
private loaded: boolean
59+
private closable: boolean
4960
private eventTarget: EventTarget
5061

5162
public constructor(iframe: HTMLIFrameElement, loader: HTMLDivElement) {
5263
this.iframe = iframe
5364
this.loader = loader
5465
this.loaded = false
66+
this.closable = true
5567
this.eventTarget = new EventTarget()
5668
this.initWindowListener()
5769
this.addEventListener('loaded', this.loadedListener.bind(this))
5870
this.addEventListener('close', this.closeListener.bind(this))
71+
this.addEventListener('confirmed', this.confirmedListener.bind(this))
5972
this.addEventListener('success', this.successListener.bind(this))
6073
}
6174

@@ -206,6 +219,11 @@ class EmbedCheckout {
206219
listener: (event: CustomEvent<EmbedCheckoutMessageClose>) => void,
207220
options?: AddEventListenerOptions | boolean,
208221
): void
222+
public addEventListener(
223+
type: 'confirmed',
224+
listener: (event: CustomEvent<EmbedCheckoutMessageConfirmed>) => void,
225+
options?: AddEventListenerOptions | boolean,
226+
): void
209227
public addEventListener(
210228
type: 'success',
211229
listener: (event: CustomEvent<EmbedCheckoutMessageSuccess>) => void,
@@ -233,6 +251,10 @@ class EmbedCheckout {
233251
type: 'close',
234252
listener: (event: CustomEvent<EmbedCheckoutMessageClose>) => void,
235253
): void
254+
public removeEventListener(
255+
type: 'confirmed',
256+
listener: (event: CustomEvent<EmbedCheckoutMessageConfirmed>) => void,
257+
): void
236258
public removeEventListener(
237259
type: 'success',
238260
listener: (event: CustomEvent<EmbedCheckoutMessageSuccess>) => void,
@@ -276,7 +298,23 @@ class EmbedCheckout {
276298
if (event.defaultPrevented) {
277299
return
278300
}
279-
this.close()
301+
if (this.closable) {
302+
this.close()
303+
}
304+
}
305+
306+
/**
307+
* Default listener for the `confirmed` event.
308+
*
309+
* This listener will set a flag to prevent the parent window from closing the embedded checkout.
310+
*/
311+
private confirmedListener(
312+
event: CustomEvent<EmbedCheckoutMessageConfirmed>,
313+
): void {
314+
if (event.defaultPrevented) {
315+
return
316+
}
317+
this.closable = false
280318
}
281319

282320
/**
@@ -290,6 +328,7 @@ class EmbedCheckout {
290328
if (event.defaultPrevented) {
291329
return
292330
}
331+
this.closable = true
293332
if (event.detail.redirect) {
294333
window.location.href = event.detail.successURL
295334
}

0 commit comments

Comments
 (0)