Kundenansicht neu E-Fix M6: Zahlungsknöpfe sind nach der Rückkehr von Stripe wieder bedienbar
Nach "Zahlungspflichtig bestellen" setzt die Seite den Knopf auf "Weiter zur Zahlung…" (gesperrt) und leitet zu Stripe. Bricht der Kunde per Zurück-Wischgeste ab, stellt Safari die Seite aus dem Bfcache her: der Knopf blieb gesperrt, ein neuer Versuch ging nur nach Neuladen. Nur ansicht-wechsel.js hatte einen pageshow-Handler. Neuer Helfer beiRueckkehr(el, fn) in ansicht-hilfen.js (pageshow mit persisted, entfernt sich, sobald das Element nicht mehr im Dokument steht); genutzt von der Bestellseite und von "Zahlungsart aktualisieren" (Verträge). Speicher und Gutschrift leiten nicht zu Stripe, dort ist nichts zu tun. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
8bb02f3949
commit
140275022d
5 files changed
+38
-2
No files matched your search
@@ -9,7 +9,7 @@
|
||||
|
||||
import { api } from './api.js'
|
||||
import { h, leeren } from './dom.js'
|
||||
import { K, ladeAbo, sichereAdresse, meldeTitel, fmtEuro, fmtDatum, ABO_NICHT_GELADEN } from './ansicht-hilfen.js'
|
||||
import { K, ladeAbo, sichereAdresse, meldeTitel, beiRueckkehr, fmtEuro, fmtDatum, ABO_NICHT_GELADEN } from './ansicht-hilfen.js'
|
||||
import { baueBlock, baueEinzelheiten, baueKundenartZeile, baueLinkzeile, hatWortlaut, kundenartVon, leistungLink } from './bestell-bausteine.js'
|
||||
|
||||
const laeufe = new WeakMap() // Wurzel → Nummer des jüngsten Aufrufs
|
||||
@@ -114,6 +114,9 @@ function bestellung(daten, productKey, { viewAs, kundenart }) {
|
||||
baueKundenartZeile(kundenart, T.kundenart, { id: 'abo-kundenart' }),
|
||||
einzelheiten)
|
||||
|
||||
// Zurück von der Zahlungsseite (Bfcache): der Knopf ist wieder bedienbar (E-Fix M6, wie ansicht-wechsel.js)
|
||||
beiRueckkehr(abschnitt, () => { kaufen.disabled = viewAs; kaufen.textContent = T.knopf || KNOPF })
|
||||
|
||||
// Das Häkchen schaltet die Zeilen „Fällig“ und „Beginn“ um
|
||||
haken?.addEventListener('change', () => {
|
||||
const neu = faelligBeginn({ lage: haken.checked ? 'wunsch' : 'frist', erste, frist })
|
||||
|
||||
@@ -146,6 +146,21 @@ export function meldeTitel(el, text) {
|
||||
el.dispatchEvent(new CustomEvent('kn:titel', { bubbles: true, detail: { text } }))
|
||||
}
|
||||
|
||||
/**
|
||||
* Zurück aus dem Bfcache (E-Fix M6): kommt der Kunde von der Zahlungsseite von Stripe per Wischgeste zurück, stellt Safari die Seite so wieder her,
|
||||
* wie sie war (Knopf gesperrt, "Weiter zur Zahlung…"). fn läuft bei pageshow mit persisted, solange `el` im Dokument steht; ist es ersetzt worden,
|
||||
* entfernt sich der Hörer. Ohne Fenster tut es nichts.
|
||||
*/
|
||||
export function beiRueckkehr(el, fn) {
|
||||
const win = globalThis.window
|
||||
if (!win || typeof win.addEventListener !== 'function') return
|
||||
const hoerer = (ev) => {
|
||||
if (!el.isConnected) { win.removeEventListener('pageshow', hoerer); return }
|
||||
if (ev.persisted) fn()
|
||||
}
|
||||
win.addEventListener('pageshow', hoerer)
|
||||
}
|
||||
|
||||
/** Zähler neu laden lassen: was = 'chat' | 'vertraege' | 'alle'. Ohne Dokument tut es nichts. */
|
||||
export function meldeZaehlerNeu(was) {
|
||||
if (typeof document === 'undefined' || typeof document.dispatchEvent !== 'function' || typeof CustomEvent === 'undefined') return
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
import { api } from './api.js'
|
||||
import { h, leeren } from './dom.js'
|
||||
import { K, zuAnsicht, fmtEuro, fmtDatum, fmtTagMonat, sichereAdresse } from './ansicht-hilfen.js'
|
||||
import { K, zuAnsicht, fmtEuro, fmtDatum, fmtTagMonat, sichereAdresse, beiRueckkehr } from './ansicht-hilfen.js'
|
||||
|
||||
const laeufe = new WeakMap() // Reiter-Inhalt → Nummer des jüngsten Aufrufs
|
||||
const TEXT_UST = 'Gemäß § 19 UStG ohne Umsatzsteuer.'
|
||||
@@ -70,6 +70,7 @@ function zahlungsKnopf(viewAs) {
|
||||
knopf.disabled = false
|
||||
}
|
||||
})
|
||||
beiRueckkehr(knopf, () => { knopf.disabled = viewAs }) // zurück von Stripe (Bfcache): wieder bedienbar (E-Fix M6)
|
||||
return [knopf, viewAs ? h('span', { class: K.meldung }, TEXT_ANSICHTSMODUS) : null, fehler] // ohne Umhüllung: der Knopf ist das Element mit diesem Text
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@ test('Zahlungsproblem: Knopf öffnet das Stripe-Portal; Widerruf nur in der Fris
|
||||
const daten = aboAntwort({ subscription: abo({ paymentProblem: true, withdrawalOpen: true, withdrawalUntil: '2026-10-17T21:59:00.000Z' }) })
|
||||
const b = await zeige(fx, daten); assert.match(fx.text(b), /Bitte aktualisiere deine Zahlungsart, damit deine Website online bleibt\./); assert.match(fx.finde(b, 'a[href="/widerrufen.html"]').textContent, /^Vertrag widerrufen · bis 17\.10\./)
|
||||
await fx.klick(fx.finde(b, (e) => e.textContent === 'Zahlungsart aktualisieren')); assert.equal(fx.window.location.href, 'https://billing.stripe.com/x')
|
||||
const portal = fx.finde(b, (e) => e.textContent === 'Zahlungsart aktualisieren'); assert.equal(portal.hasAttribute('disabled'), true, 'während der Weiterleitung gesperrt')
|
||||
fx.window.dispatchEvent({ type: 'pageshow', persisted: true }); assert.equal(portal.hasAttribute('disabled'), false, 'M6: zurück von Stripe wieder bedienbar')
|
||||
const ansicht = await zeige(fx, daten, me({ viewAs: true })); assert.equal(fx.finde(ansicht, (e) => e.textContent === 'Zahlungsart aktualisieren').hasAttribute('disabled'), true)
|
||||
assert.equal(fx.finde(await zeige(fx, WARTUNG), 'a[href="/widerrufen.html"]'), null)
|
||||
}))
|
||||
|
||||
@@ -99,6 +99,21 @@ test('Privat und leer: ein Häkchen aus dem Wortlaut, ungehakt nach der Frist, g
|
||||
}
|
||||
}))
|
||||
|
||||
test('M6: zurück von der Zahlungsseite (Wischgeste, Bfcache) ist der Knopf wieder bedienbar; ein später entfernter Knopf bekommt nichts mehr', mit({ 'POST /api/abo/checkout': { url: 'https://checkout.stripe.test/x' } }, async (fx) => {
|
||||
const w = await zeige(fx, aboNeu(), meMit('privat'))
|
||||
const knopf = fx.finde(w, '#abo-buy')
|
||||
await fx.klick(knopf); await fx.ruhe()
|
||||
assert.deepEqual([knopf.hasAttribute('disabled'), knopf.textContent], [true, 'Weiter zur Zahlung…'])
|
||||
fx.window.dispatchEvent({ type: 'pageshow', persisted: false }); assert.equal(knopf.hasAttribute('disabled'), true, 'ein normales Laden (nicht persisted) ändert nichts')
|
||||
fx.window.dispatchEvent({ type: 'pageshow', persisted: true })
|
||||
assert.deepEqual([knopf.hasAttribute('disabled'), knopf.textContent], [false, 'Zahlungspflichtig bestellen'])
|
||||
// der Knopf kann wieder bestellen
|
||||
await fx.klick(knopf); await fx.ruhe(); assert.equal(fx.fetchLog.filter((a) => a.url === '/api/abo/checkout').length, 2)
|
||||
// die Seite wurde ersetzt: der Hörer entfernt sich und fasst den alten Knopf nicht mehr an
|
||||
w.replaceChildren(); knopf.disabled = true
|
||||
fx.window.dispatchEvent({ type: 'pageshow', persisted: true }); assert.equal(knopf.hasAttribute('disabled'), true)
|
||||
}))
|
||||
|
||||
test('Der Body enthält nie kundenart oder startChoice', mit({ 'POST /api/abo/checkout': { url: 'https://checkout.stripe.test/x' } }, async (fx) => {
|
||||
for (const m of [meMit('unternehmen'), meMit('privat'), meMit('')]) {
|
||||
const w = await zeige(fx, aboNeu(), m)
|
||||
|
||||
Reference in new issue
Block a user