);
}
function PaygOrderView({ orderId, account }) {
const [o, setO] = React.useState(null);
const [err, setErr] = React.useState('');
const [rf, setRf] = React.useState('idle');
const [rfMsg, setRfMsg] = React.useState('');
const [nudge, setNudge] = React.useState(null);
const load = React.useCallback(async () => {
try {
const r = await paygJSON('/api/payg/orders/' + orderId);
if (r.ok) { setO(r.data); setErr(''); } else { setErr(r.status === 401 ? 'Please sign in to see this order.' : (r.data.message || 'Order not found.')); }
} catch (e) { setErr('No connection - retrying…'); }
}, [orderId]);
React.useEffect(() => { load(); }, [load]);
React.useEffect(() => {
if (o && ['delivered', 'credited', 'refunded', 'expired'].includes(o.status)) return;
const iv = setInterval(load, 4000);
return () => clearInterval(iv);
}, [o, load]);
React.useEffect(() => {
if (o && o.status === 'delivered') paygJSON('/api/payg/nudge').then((r) => { if (r.ok && r.data.show) setNudge(r.data); }).catch(() => {});
}, [o && o.status]);
const refund = async () => {
setRf('working'); setRfMsg('');
try {
const r = await paygJSON('/api/payg/orders/' + orderId + '/refund', { method: 'POST' });
if (r.ok) { setRf('done'); setRfMsg('Refund started - it usually shows on your card within 5-10 working days.'); load(); }
else { setRf('failed'); setRfMsg(r.data.message || 'The refund could not be started.'); }
} catch (e) { setRf('failed'); setRfMsg('No connection - please try again.'); }
};
if (err && !o) return
{err}
{account && !account.signed_in ? : null}
;
if (!o) return
Loading your order…
;
const s = o.status;
const title = { created: 'Waiting for your payment', paid: 'Payment received', rendering: 'Your video is being made', delivered: 'Your video is ready', credited: 'We could not finish your video', refunded: 'Refunded', expired: 'This payment page has expired' }[s] || s;
return (
Order #{o.order_id} · {o.label} · {PAYG_FMT(o.price_eur)} {o.price_note || 'excl. VAT'}
{title}
{(s === 'paid' || s === 'rendering') ?
This usually takes a few minutes. You can close this page - we e-mail you when it is ready, and it will be in My Videos.
: null}
{s === 'delivered' ? (
{o.result_url ? : null}
The video carries the "AI-generated" label required by the EU AI Act (article 50).
Something went wrong on our side. You have not been charged for it: the {PAYG_FMT(o.price_eur)} is on your MyAvatar account as {o.tokens} credits, ready for your next video.
Prefer the money back on your card instead?
) : null}
{s === 'refunded' ?
The payment has been refunded to your card.
: null}
{s === 'created' || s === 'expired' ? Back to the shop : null}
{nudge ? (
You have bought {nudge.count} videos in the last 30 days for {PAYG_FMT(nudge.spent_eur)}.
{nudge.saving_eur > 0
? The {nudge.plan} plan ({PAYG_FMT(nudge.plan_price_eur)}/month) would have saved you {PAYG_FMT(nudge.saving_eur)}. See plans
: Plans start at {PAYG_FMT(nudge.plans_from_eur)}/month and add your own actor library, brand kit and distribution. See plans}
Fixed price, shown before you pay. {window.MAVat ? null : ((cat && cat.tax_behavior === 'inclusive') ? 'Prices include VAT.' : 'Prices exclude VAT, which is added at checkout for your country.')}
{catErr ?
{catErr}
: null}
1What do you need?
{tile('th', 'Talking-head video', 'A presenter speaks your message to camera - up to 30 s or up to 60 s.', kind === 'talking_head', () => { setKind('talking_head'); setSku('talking_head_30'); })}
{tile('tr', 'Translate my video', 'Same voice, new language, lips re-synced. Up to ' + ((prod('translate_standard') || {}).max_minutes || 5) + ' minutes.', kind === 'translation', () => { setKind('translation'); setSku('translate_standard'); })}
{tile('fs', 'Short film', 'Cinematic scenes with a cast.', false, null, 'Coming soon')}
{tile('f35', 'Longer film', 'About 35 seconds, several scenes.', false, null, 'Coming soon')}
{tile('house', 'A House Cast presenter', 'Included in the price.', who === 'house', () => setWho('house'))}
{tile('own', 'My own digital twin', ownTwin && ownTwin.ready ? ('Your twin: ' + (ownTwin.name || 'ready')) : 'Needs your trained twin and recorded consent.', who === 'own', () => setWho('own'), ownTwin && ownTwin.ready ? null : 'Set up once first')}