Skip to content

Commit 3db29f2

Browse files
sodiumsunclaude
andcommitted
power: in-app 'Keep Power' upgrade (monthly/annual → Stripe)
Wispr model: pay inside the app, not on a marketing page. A Power-trial user now sees a 'Keep Power' block in Settings → Account (X days left) with $30/mo and $24/mo-annual buttons. _act_upgrade_power opens the Stripe checkout for the chosen interval, prefilled + client_reference_id'd with the account email so the webhook binds the payment. Shown only during a trial (plan=power + trialDaysLeft set); a paying Power user doesn't see it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019GTBZjBGurqsHUHsjaU1gA
1 parent e232ef0 commit 3db29f2

2 files changed

Lines changed: 26 additions & 0 deletions

File tree

heard/home_window.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@
2525

2626
_HTML = Path(__file__).with_name("onboarding.html")
2727

28+
# Power subscription checkout (public Stripe payment links). Opened from the
29+
# in-app "Keep Power" upgrade; the account is bound via prefilled email +
30+
# client_reference_id. Paying flips the plan to 'power' through the webhook.
31+
_POWER_MONTHLY_BUY_URL = "https://buy.stripe.com/3cIfZa2X13We0g6eac77O03" # $30/mo
32+
_POWER_ANNUAL_BUY_URL = "https://buy.stripe.com/00w6oAeFJ2Sad2S8PS77O08" # $288/yr
33+
2834
_controller = None # window-controller singleton (reused on re-open)
2935
_HeardHomeClass = None # ObjC class, built lazily on first use
3036

@@ -631,6 +637,25 @@ def _act_open_power_page(self, body):
631637
except Exception as e:
632638
_log_bridge_error("open_power_page", e)
633639

640+
def _act_upgrade_power(self, body):
641+
# Convert a Power TRIAL to a paid subscription (Wispr model: pay in
642+
# the app, not on a marketing page). Opens the Stripe checkout for the
643+
# chosen interval, prefilled + client_reference_id'd with the user's
644+
# email so the webhook binds the payment to this account.
645+
try:
646+
import urllib.parse
647+
import webbrowser
648+
649+
interval = (body.get("interval") or "monthly").strip().lower()
650+
url = _POWER_ANNUAL_BUY_URL if interval == "annual" else _POWER_MONTHLY_BUY_URL
651+
email = (config.load().get("heard_email") or "").strip()
652+
if email:
653+
q = urllib.parse.quote(email, safe="")
654+
url = f"{url}?prefilled_email={q}&client_reference_id={q}"
655+
webbrowser.open(url)
656+
except Exception as e:
657+
_log_bridge_error("upgrade_power", e)
658+
634659
def _act_manage_account(self, body):
635660
# Open the browser to manage plan / payment / email on heard.dev.
636661
try:

heard/onboarding.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,7 @@
483483
</div>
484484
${S.signedIn && S.plan!=='power' ? `<div style="border-top:1px solid var(--line);margin:14px -18px 0;padding:14px 18px 0;display:flex;align-items:center;justify-content:space-between;gap:12px"><div><div style="font-weight:600">Try Power free</div><div style="font:400 13px var(--sans);color:var(--ink-2);margin-top:2px">14 days of hands-free voice control. No card.</div></div><button class="btn btn-primary" style="padding:9px 16px" onclick="bridge('start_power_trial')">Start trial</button></div>` : ''}
485485
${S.plan==='power' && !S.powerBuild ? `<div style="border-top:1px solid var(--line);margin:14px -18px 0;padding:14px 18px 0;display:flex;align-items:center;justify-content:space-between;gap:12px"><div><div style="font-weight:600">Get the Power app</div><div style="font:400 13px var(--sans);color:var(--ink-2);margin-top:2px">You're on Power - install the Power app to unlock voice input.</div></div><button class="btn btn-primary" style="padding:9px 16px" onclick="bridge('open_power_page')">Download</button></div>` : ''}
486+
${S.plan==='power' && S.trialDaysLeft!=null ? `<div style="border-top:1px solid var(--line);margin:14px -18px 0;padding:14px 18px 0"><div style="display:flex;align-items:center;justify-content:space-between;gap:12px"><div><div style="font-weight:600">Keep Power</div><div style="font:400 13px var(--sans);color:var(--ink-2);margin-top:2px">${S.trialDaysLeft===0?'Your trial ends today':S.trialDaysLeft+' day'+(S.trialDaysLeft===1?'':'s')+' left in your trial'} - upgrade to keep hands-free voice.</div></div></div><div style="display:flex;gap:8px;margin-top:12px"><button class="btn btn-primary" style="padding:9px 16px;flex:1" onclick="bridge('upgrade_power',{interval:'monthly'})">$30 / month</button><button class="btn btn-primary" style="padding:9px 16px;flex:1" onclick="bridge('upgrade_power',{interval:'annual'})">$24 / mo · annual</button></div></div>` : ''}
486487
</div>
487488
${S.byokEnabled ? `${_HD('API keys')}
488489
<div style="font:400 12.5px/1.5 var(--sans);color:var(--ink-2);margin:-6px 0 10px">Bring your own keys. Heard sends narration <b>straight to these providers</b> using your key - never through our servers. Leave blank to use your plan’s cloud voices, or the local Kokoro voice for zero cloud.</div>

0 commit comments

Comments
 (0)