Skip to main content

Custom Screen before a Paywall

In onboarding flows, you need to automatically associate user’s email from the payment provider with one in the Custom screen

A
Written by Alexander Karpovich
Updated this week

Case

Objective: Capture the email entered by the user on a custom screen and pass it into the Paddle or Stripe checkout flow.


Flow:

  1. User sees a custom screen before the paywall.

  2. User enters their email and submits the form.

  3. The screen validates the email.

  4. If valid, the app triggers the event LeaveCustomScreenWithEmail with the provided email.

  5. Paddle or Stripe receives this email and pre-fills it into the payment form.


Solution

To implement this, you can use the special event:

LeaveCustomScreenWithEmail

When called, it passes the user’s email to the payment provider.

Event structure:

{ "email": "[email protected]" }

Example of a Custom Screen

Here’s a working example of a custom HTML screen that collects and validates email before proceeding:

<html>
<head>
<meta charset='UTF-8'>
<title>Some custom screen</title>
<!-- OO-PORT-MESSAGE-HANDLER-START -->
<!-- DO NOT REMOVE THIS CODE -->
<script>
window.onboardingOnlineCustomScreenData = {
inputs: {},
labels: {},
userData: {},
customData: {}
};
window.leaveCustomScreenWithEmail = () => {};
window.backScreen = () => {};
window.acceptCookie = () => {};
window.declineCookie = () => {};
window.onmessage = (event) => {
if (typeof (event.data) === 'object' && event.data.eventName === 'InitCustomScreen') {
window.onboardingOnlineCustomScreenData.inputs = event.data.args.inputs;
window.onboardingOnlineCustomScreenData.labels = event.data.args.labels;
window.onboardingOnlineCustomScreenData.userData = event.data.args.userData;
window.onboardingOnlineCustomScreenData.customData = event.data.args.customData;
const port = event.ports[0];
if (port) {
window.leaveCustomScreenWithEmail = (email) => {
port.postMessage({
eventName: 'LeaveCustomScreenWithEmail',
args: { email },
}, []);
};
window.backScreen = () => {
port.postMessage({
eventName: 'BackScreen',
args: null,
}, []);
};
window.acceptCookie = () => {
port.postMessage({
eventName: 'AcceptCookie',
args: null,
}, []);
};
window.declineCookie = () => {
port.postMessage({
eventName: 'DeclineCookie',
args: null,
}, []);
};
}
}
};
</script>
<!-- OO-PORT-MESSAGE-HANDLER-END -->
<style>
body {
background-color: #ffffff;
font-family: Arial, sans-serif;
}
#custom-form {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100vh;
gap: 16px;
}
input {
width: 300px;
padding: 12px 14px;
font-size: 16px;
border: 1px solid #ccc;
border-radius: 8px;
outline: none;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
transition: border-color 0.2s, box-shadow 0.2s;
}
input:focus {
border-color: #4a90e2;
box-shadow: 0 0 6px rgba(74,144,226,0.4);
}
button {
width: 300px;
padding: 12px 14px;
font-size: 16px;
cursor: pointer;
border: none;
border-radius: 8px;
background-color: #4a90e2;
color: white;
font-weight: bold;
transition: background-color 0.2s;
}
button:hover {
background-color: #357abd;
}
.error {
color: red;
font-size: 14px;
}
</style>
</head>
<body>
<form id='custom-form'>
<input type='email' id='email' name='email' placeholder='Enter your email' required autofocus />
<span id='error-message' class='error'></span>
<button id='submit-button'>Next screen</button>
</form>
<script>
const form = document.getElementById('custom-form');
const emailInput = document.getElementById('email');
const errorMessage = document.getElementById('error-message');



form.addEventListener('submit', function(event) {
event.preventDefault();
const email = emailInput.value.trim();



if (!validateEmail(email)) {
errorMessage.textContent = 'Please enter a valid email address';
emailInput.style.borderColor = 'red';
return;
}



errorMessage.textContent = '';
emailInput.style.borderColor = '#ccc';
window.leaveCustomScreenWithEmail(email);
});



function validateEmail(email) {
const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return re.test(email.toLowerCase());
}
</script>
</body>
</html>

How It Works

  • The custom form captures the user’s email.

  • Basic validation ensures only valid addresses proceed.

  • On success, it triggers:

window.leaveCustomScreenWithEmail(email);
  • The event passes { "email": "[email protected]" } back to the app.

  • Paddle or Stripe automatically uses this email in their checkout flow.

Did this answer your question?