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')