|
21 | 21 | let status = $state('Ready to generate your first ticket.'); |
22 | 22 | let tier = $state<'FREE' | 'EVENT' | 'POWER'>('FREE'); |
23 | 23 |
|
24 | | - let tierPaid = $state<'FREE' | 'EVENT' | 'POWER'>('FREE'); |
25 | | -
|
26 | 24 | function getCheckoutLink(plan: 'EVENT' | 'POWER') { |
27 | 25 | const amountXlm = TIERS[plan].priceXlm; |
28 | 26 | const memo = encodeURIComponent(`EZWALLET_${plan}`); |
29 | 27 | return `stellar:${PLATFORM_PAYMENT_DESTINATION}?amount=${amountXlm}&memo=${memo}`; |
30 | 28 | } |
31 | 29 |
|
32 | | - function confirmPlanPaid(plan: 'EVENT' | 'POWER') { |
33 | | - tierPaid = plan; |
34 | | - tier = plan; |
35 | | - status = `${TIERS[plan].name} unlocked for this session.`; |
36 | | - } |
| 30 | + function checkoutSelectedTier() { |
| 31 | + if (tier === 'FREE') { |
| 32 | + status = 'Free plan is active — no checkout required.'; |
| 33 | + return; |
| 34 | + } |
37 | 35 |
|
38 | | - function requiresPaidPlan() { |
39 | | - return count > 1; |
| 36 | + const checkoutLink = getCheckoutLink(tier); |
| 37 | + window.open(checkoutLink, '_blank', 'noopener,noreferrer'); |
| 38 | + status = `Opening ${TIERS[tier].name} checkout in Stellar wallet…`; |
40 | 39 | } |
41 | 40 |
|
| 41 | + $effect(() => { |
| 42 | + if (tier === 'FREE' && count > 1) { |
| 43 | + count = 1; |
| 44 | + status = 'Free plan allows 1 ticket per batch. Upgrade to Event or Power for larger runs.'; |
| 45 | + } |
| 46 | + }); |
42 | 47 |
|
43 | 48 | async function login() { |
44 | 49 | await auth.login(); |
|
66 | 71 | function bulk() { |
67 | 72 | const max = TIERS[tier].maxBatch ?? 1; |
68 | 73 |
|
69 | | - if (requiresPaidPlan() && tier === 'FREE') { |
| 74 | + if (count > 1 && tier === 'FREE') { |
70 | 75 | status = 'Free includes 1 generation only. Choose Event or Power, then use the one-tap Stellar checkout below.'; |
71 | 76 | return; |
72 | 77 | } |
73 | 78 |
|
74 | | - if (tier !== 'FREE' && tierPaid !== tier) { |
75 | | - status = `Complete ${TIERS[tier].name} checkout first, then tap "I paid" to unlock generation.`; |
76 | | - return; |
77 | | - } |
78 | 79 | if (count > max) { |
79 | 80 | status = `${TIERS[tier].name} limit is ${max}`; |
80 | 81 | return; |
|
198 | 199 | </label> |
199 | 200 | <div class="pill-row"> |
200 | 201 | <button class="soft-button" onclick={() => (count = 1)}>1</button> |
201 | | - <button class="soft-button" onclick={() => (count = 10)}>10</button> |
202 | | - <button class="soft-button" onclick={() => (count = 100)}>100</button> |
203 | | - <button class="soft-button" onclick={() => (count = 1000)}>1000</button> |
| 202 | + <button class="soft-button" onclick={() => (count = 10)} disabled={tier === 'FREE'}>10</button> |
| 203 | + <button class="soft-button" onclick={() => (count = 100)} disabled={tier === 'FREE'}>100</button> |
| 204 | + <button class="soft-button" onclick={() => (count = 1000)} disabled={tier !== 'POWER'}>1000</button> |
204 | 205 | </div> |
205 | 206 | <label>Batch count |
206 | | - <input type="number" bind:value={count} min="1" /> |
| 207 | + <input type="number" bind:value={count} min="1" max={TIERS[tier].maxBatch ?? undefined} /> |
207 | 208 | </label> |
208 | 209 | <div class="action-row"> |
209 | 210 | <button class="holo-button" onclick={bulk}>Generate batch</button> |
|
221 | 222 | <div class="plan-card"><strong>Power Pack</strong><span>{TIERS.POWER.maxBatch} • {TIERS.POWER.priceXlm} XLM</span></div> |
222 | 223 | <div class="plan-card"><strong>Custom / Enterprise</strong><span>Contact for event ops scale</span></div> |
223 | 224 | </div> |
224 | | - <p class="review">One-tap checkout (Apple Pay-style flow): choose plan → pay in wallet → tap I paid → generate.</p> |
225 | | - <div class="pill-row"> |
226 | | - <a class="holo-button" href={getCheckoutLink('EVENT')}>Pay {TIERS.EVENT.priceXlm} XLM · Event Pack</a> |
227 | | - <button class="soft-button" onclick={() => confirmPlanPaid('EVENT')}>I paid Event Pack</button> |
228 | | - </div> |
229 | | - <div class="pill-row"> |
230 | | - <a class="holo-button" href={getCheckoutLink('POWER')}>Pay {TIERS.POWER.priceXlm} XLM · Power Pack</a> |
231 | | - <button class="soft-button" onclick={() => confirmPlanPaid('POWER')}>I paid Power Pack</button> |
| 225 | + <p class="review">One-tap checkout flow: choose your plan, open wallet checkout, and continue generating. No manual ‘I paid’ confirmation step.</p> |
| 226 | + <div class="action-row"> |
| 227 | + <button class="holo-button" onclick={checkoutSelectedTier} disabled={tier === 'FREE'}> |
| 228 | + {#if tier === 'EVENT'}Upgrade · {TIERS.EVENT.priceXlm} XLM{/if} |
| 229 | + {#if tier === 'POWER'}Upgrade · {TIERS.POWER.priceXlm} XLM{/if} |
| 230 | + {#if tier === 'FREE'}Select Event or Power to upgrade{/if} |
| 231 | + </button> |
232 | 232 | </div> |
233 | 233 | <label>Platform destination |
234 | 234 | <input value={PLATFORM_PAYMENT_DESTINATION} readonly /> |
|
274 | 274 | input, select { width: 100%; margin-top: .35rem; background: #0a0a0a; color: #f4f4f5; border: 1px solid rgba(255,255,255,.18); border-radius: 10px; padding: .58rem .66rem; } |
275 | 275 | input:focus, select:focus, button:focus, a:focus { outline: 2px solid #9ee7ff; outline-offset: 2px; } |
276 | 276 | .pill-row,.action-row { display:flex; gap:.5rem; flex-wrap:wrap; margin:.5rem 0; } |
277 | | - .asset-pill,.soft-button,.holo-button { border-radius: 999px; border:1px solid rgba(255,255,255,.2); padding:.5rem .85rem; color:#f4f4f5; background:#11131a; } |
| 277 | + .asset-pill,.soft-button,.holo-button { border-radius: 999px; border:1px solid rgba(255,255,255,.2); padding:.5rem .85rem; color:#f4f4f5; background:#11131a; transition: all .18s ease; } |
278 | 278 | .asset-pill.active { border-color: rgba(255,255,255,.35); box-shadow: 0 0 0 1px #9ee7ff44; } |
279 | 279 | .holo-button { background: linear-gradient(110deg, #f5f5f5, #9ca3af, #e5e7eb, #c0c0c0, #f8fafc, #a3a3a3); color:#060709; font-weight:700; } |
| 280 | + button:disabled { opacity:.45; cursor:not-allowed; } |
| 281 | + .soft-button:hover,.holo-button:hover,.asset-pill:hover { transform: translateY(-1px); } |
280 | 282 | .soft-link { align-self:center; color:#dbe5f3; text-decoration:underline; } |
281 | 283 | .qr-frame { background: linear-gradient(160deg,#1e232e,#11131a); border:1px solid rgba(255,255,255,.35); border-radius:14px; padding: 1rem; min-height: 230px; display:grid; place-items:center; } |
282 | 284 | .qr-image { background:white; padding:12px; border-radius:10px; width:min(320px,100%); height:auto; } |
|
0 commit comments