Skip to content

feat: update client paywall design#3

Open
marcelosalloum wants to merge 7 commits intomainfrom
feat/update-client-paywall-design
Open

feat: update client paywall design#3
marcelosalloum wants to merge 7 commits intomainfrom
feat/update-client-paywall-design

Conversation

@marcelosalloum
Copy link
Contributor

What

Update client paywall design to match stellar design system.

Further work

Although the design was made using pure css with AI for speed, I'll open another PR later migrating it to use the Stellar Design System components

@marcelosalloum marcelosalloum self-assigned this Mar 5, 2026
Copilot AI review requested due to automatic review settings March 5, 2026 06:27
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Updates the paywall/demo UI styling and copy to better match the Stellar design direction, and adds a few UX improvements (favicons, nav link, dynamic asset labels) across the paywall package and the simple-paywall example app.

Changes:

  • Restyle the paywall CSS/markup and the simple-paywall client/server pages (fonts, layout, colors, icons).
  • Make paywall messaging show the configured asset code (and add a Circle faucet link for testnet USDC).
  • Add CLIENT_HOME_URL to link the server-rendered protected page header back to the client app.

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
packages/paywall/src/browser/styles.css Updates paywall look-and-feel (colors, spacing, layout).
packages/paywall/src/browser/baseTemplate.ts Adds Stellar favicons to the paywall HTML template.
packages/paywall/src/browser/StellarPaywall.tsx Improves paywall copy to use a dynamic asset code + testnet faucet CTA.
examples/simple-paywall/server/src/views/protected.ts Redesigns the unlocked/protected page HTML/CSS and adds optional brand link.
examples/simple-paywall/server/src/routes/protected.ts Passes CLIENT_HOME_URL into the protected view and strips {{TX_LINK}} when paywall is disabled.
examples/simple-paywall/server/src/middleware/txHashInjector.ts Updates injected Stellar Expert link markup to match new styling.
examples/simple-paywall/server/src/config/env.ts Adds CLIENT_HOME_URL env accessor.
examples/simple-paywall/server/src/app.ts Updates CSP for Google Fonts CSS on /protected.
examples/simple-paywall/server/.env.example Documents CLIENT_HOME_URL.
examples/simple-paywall/client/src/pages/TryIt.tsx Updates layout/styling and uses PAYMENT_PRICE in copy.
examples/simple-paywall/client/src/pages/Home.tsx Updates layout/styling, resource links, and wallet list to use real URLs.
examples/simple-paywall/client/src/index.css Imports Google Fonts and sets base colors/fonts.
examples/simple-paywall/client/src/constants.ts Exposes PAYMENT_PRICE.
examples/simple-paywall/client/src/config/env.ts Adds PAYMENT_PRICE config support.
examples/simple-paywall/client/src/components/Layout.tsx Restyles header/footer and adds “Try the demo” CTA behavior.
examples/simple-paywall/client/index.html Adds multiple favicon sizes.
README.md Documents CLIENT_HOME_URL.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

href: "https://github.com/coinbase/x402/pull/711",
href: "https://github.com/coinbase/x402/tree/main/typescript/packages/mechanisms/stellar",
description:
"The pull request adding Stellar blockchain support to the x402 protocol (client, facilitator, and server).",
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

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

This resource now links to the package directory, but the description still says it's "The pull request adding Stellar blockchain support...". Update the description to match the new URL (or change the URL back to the PR) so users aren’t misled.

Suggested change
"The pull request adding Stellar blockchain support to the x402 protocol (client, facilitator, and server).",
"TypeScript package providing Stellar blockchain support for the x402 protocol (client, facilitator, and server).",

Copilot uses AI. Check for mistakes.
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'", "https://w.soundcloud.com"],
styleSrc: ["'self'", "'unsafe-inline'"],
styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"],
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

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

The /protected CSP allows Google Fonts CSS via styleSrc, but it still blocks the actual font files fetched from https://fonts.gstatic.com because there is no font-src directive (so defaultSrc 'self' applies). Add an explicit fontSrc (and include https://fonts.gstatic.com) so Inter/Inconsolata load correctly.

Suggested change
styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"],
styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"],
fontSrc: ["'self'", "https://fonts.gstatic.com"],

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +3
export function protectedPageHtml(homeUrl?: string): string {
const brand = homeUrl
? `<a href="${homeUrl}" class="nav-link">
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

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

homeUrl is interpolated directly into an href attribute without escaping or scheme validation. If CLIENT_HOME_URL contains quotes or a javascript: URL, it can break out of the attribute and inject markup/script into the page. Consider validating to only allow http(s) URLs (or a relative path) and HTML-escaping the value before embedding it.

Suggested change
export function protectedPageHtml(homeUrl?: string): string {
const brand = homeUrl
? `<a href="${homeUrl}" class="nav-link">
function escapeHtml(value: string): string {
return value
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
function sanitizeHomeUrl(homeUrl?: string): string | undefined {
if (!homeUrl) {
return undefined;
}
const trimmed = homeUrl.trim();
if (!trimmed) {
return undefined;
}
const lower = trimmed.toLowerCase();
// Disallow javascript: and other similar script URLs
if (lower.startsWith('javascript:')) {
return undefined;
}
const isAbsoluteHttp = /^https?:\/\//i.test(trimmed);
const isRelativePath = trimmed.startsWith('/');
if (!isAbsoluteHttp && !isRelativePath) {
// Only allow http(s) URLs or root-relative paths
return undefined;
}
return escapeHtml(trimmed);
}
export function protectedPageHtml(homeUrl?: string): string {
const safeHomeUrl = sanitizeHomeUrl(homeUrl);
const brand = safeHomeUrl
? `<a href="${safeHomeUrl}" class="nav-link">

Copilot uses AI. Check for mistakes.
Comment on lines 150 to 154
if (tokenBalanceFormatted === "") {
setStatus(statusInfo("Checking USDC balance..."));
setStatus(statusInfo(`Checking ${assetCode} balance...`));
await refreshBalance();
if (Number(tokenBalanceFormatted) < amount) {
setStatus(
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

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

After await refreshBalance(), the subsequent Number(tokenBalanceFormatted) < amount check still uses the pre-refresh tokenBalanceFormatted value captured in the callback closure. On first pay attempt (when tokenBalanceFormatted === ""), this will typically compare "" (=> 0) and can incorrectly show an insufficient-balance error. Consider having refreshBalance() return the formatted/decoded balance (or using tokenBalanceRaw), and compare against that returned value instead of the stale state variable.

Copilot uses AI. Check for mistakes.
},
{
title: "HTTP 402 Protocol",
title: "HTTPS 402 Protocol",
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

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

The feature title says "HTTPS 402 Protocol", but the protocol/status code being referenced is HTTP 402 (not HTTPS 402). This looks like a typo in user-facing copy.

Suggested change
title: "HTTPS 402 Protocol",
title: "HTTP 402 Protocol",

Copilot uses AI. Check for mistakes.
@marcelosalloum marcelosalloum changed the title Feat: update client paywall design feat: update client paywall design Mar 5, 2026
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