diff --git a/.changeset/fix-html-text-overrides.md b/.changeset/fix-html-text-overrides.md
new file mode 100644
index 00000000..dd5da1f3
--- /dev/null
+++ b/.changeset/fix-html-text-overrides.md
@@ -0,0 +1,5 @@
+---
+'mppx': patch
+---
+
+Fixed Tempo HTML pay button text overrides and make the HTML page title follow a custom `paymentRequired` label when `title` is omitted.
diff --git a/src/server/Transport.test.ts b/src/server/Transport.test.ts
index 2a231e67..a8f6ce55 100644
--- a/src/server/Transport.test.ts
+++ b/src/server/Transport.test.ts
@@ -269,6 +269,26 @@ describe('http', () => {
expect(body).toContain('Gotta Pay')
})
+ test('uses paymentRequired as the title when title is omitted', async () => {
+ const transport = Transport.http()
+ const request = new Request('https://example.com', {
+ headers: { Accept: 'text/html' },
+ })
+
+ const response = await transport.respondChallenge({
+ challenge,
+ input: request,
+ html: {
+ ...htmlOptions,
+ text: { paymentRequired: 'Gotta Pay' },
+ },
+ })
+
+ const body = await response.text()
+ expect(body).toContain('
Gotta Pay')
+ expect(body).toContain('Gotta Pay')
+ })
+
test('applies custom theme logo', async () => {
const transport = Transport.http()
const request = new Request('https://example.com', {
diff --git a/src/server/internal/html/config.ts b/src/server/internal/html/config.ts
index 365f55d3..89a9cd02 100644
--- a/src/server/internal/html/config.ts
+++ b/src/server/internal/html/config.ts
@@ -172,7 +172,15 @@ export function resolveOptions(options: Options): {
},
(options.theme as never) ?? {},
)
- const text = sanitizeRecord(mergeDefined(defaultText, (options.text as never) ?? {}))
+ const textOverrides = (options.text as Text | undefined) ?? undefined
+ const mergedText = mergeDefined(defaultText, (textOverrides as never) ?? {})
+ const text = sanitizeRecord({
+ ...mergedText,
+ title:
+ typeof textOverrides?.title === 'string' && textOverrides.title.length > 0
+ ? mergedText.title
+ : mergedText.paymentRequired,
+ })
return { theme, text }
}
diff --git a/src/tempo/server/internal/html/main.ts b/src/tempo/server/internal/html/main.ts
index e305d27e..148cb623 100644
--- a/src/tempo/server/internal/html/main.ts
+++ b/src/tempo/server/internal/html/main.ts
@@ -73,8 +73,8 @@ const provider = Provider.create({
})
const button = document.createElement('button')
-button.innerHTML =
- 'Continue with '
+const buttonLabel = c.text.pay === 'Pay' ? 'Continue with' : c.text.pay
+button.innerHTML = `${buttonLabel} `
button.onclick = async () => {
try {
c.error()
diff --git a/test/html/server.ts b/test/html/server.ts
index 2483ef5b..0500fb9b 100644
--- a/test/html/server.ts
+++ b/test/html/server.ts
@@ -41,6 +41,19 @@ export async function startServer(port: number): Promise {
],
secretKey: 'test-html-server-secret-key',
})
+ const tempoCustomTextMppx = Mppx.create({
+ methods: [
+ tempo.charge({
+ account,
+ currency: '0x20c0000000000000000000000000000000000000',
+ feePayer: true,
+ html: { text: { pay: 'Buy Now' } },
+ recipient: account.address,
+ testnet: true,
+ }),
+ ],
+ secretKey: 'test-html-server-secret-key',
+ })
const stripeMppx = stripeEnabled
? Mppx.create({
methods: [
@@ -81,6 +94,17 @@ export async function startServer(port: number): Promise {
return result.withReceipt(Response.json({ url: 'https://example.com/photo.jpg' }))
}
+ if (url.pathname === '/tempo/charge-custom-text') {
+ const result = await tempoCustomTextMppx.tempo.charge({
+ amount: '0.01',
+ description: 'Random stock photo',
+ })(request)
+
+ if (result.status === 402) return result.challenge
+
+ return result.withReceipt(Response.json({ url: 'https://example.com/photo.jpg' }))
+ }
+
if (url.pathname === '/stripe/charge') {
if (!stripeMppx) return new Response('Not Found', { status: 404 })
diff --git a/test/html/tempo.test.ts b/test/html/tempo.test.ts
index b9f03bfa..d840f8d1 100644
--- a/test/html/tempo.test.ts
+++ b/test/html/tempo.test.ts
@@ -17,6 +17,14 @@ test('charge via tempo html payment page', async ({ page }) => {
await expect(page.locator('body')).toContainText('"url":', { timeout: 30_000 })
})
+test('charge via tempo html payment page respects custom pay text', async ({ page }) => {
+ await page.goto('/tempo/charge-custom-text', {
+ waitUntil: 'domcontentloaded',
+ })
+
+ await expect(page.getByRole('button', { name: /buy now tempo/i })).toBeVisible()
+})
+
test('service worker endpoint returns javascript', async ({ page }) => {
const response = await page.goto('/tempo/charge?__mppx_worker')
expect(response?.headers()['content-type']).toContain('application/javascript')