@@ -707,7 +707,7 @@ func (a *app) handleJoin(s ssh.Session) {
707707 wish .Println (s , "\n " + strings .Join (includes , "\n " ))
708708
709709 // 2) Founding Lifetime ($99 one-time): custom domains + Tor shell.
710- a .offerPremium (s , & u )
710+ a .offerPremium (s , in , & u )
711711 _ = s .Exit (0 )
712712}
713713
@@ -921,20 +921,20 @@ func (a *app) showPremiumWelcome(s ssh.Session, u store.User) {
921921}
922922
923923// offerPremium pitches the $99 Founding Lifetime membership — custom domains and
924- // the Tor shell. When CoinPay can mint a charge in-session it shows the exact
925- // amount and deposit address; otherwise it falls back to a pay command.
926- // Non-blocking: the member pays out of band and perks unlock on their next
927- // connect (or re-running join@).
928- func (a * app ) offerPremium (s ssh.Session , u * store.User ) {
924+ // the Tor shell — and only mints a CoinPay charge if the member explicitly opts
925+ // in at the prompt. Showing the pitch must NOT create a payment: minting on
926+ // every join@ produced a $99 invoice for everyone who connected. Non-blocking:
927+ // the member pays out of band and perks unlock on their next connect (or
928+ // re-running join@).
929+ func (a * app ) offerPremium (s ssh.Session , in * bufio.Reader , u * store.User ) {
929930 // Maybe they already paid (e.g. re-ran join@ after paying).
930931 if a .ensurePremium (u ) {
931932 a .showPremiumWelcome (s , * u )
932933 return
933934 }
934- ref := payments .PremiumReference (u .PubKeyFP )
935935
936- lines := [] string {
937- "" ,
936+ // Pitch only — no charge is created here.
937+ wish . Println ( s , " \n " + strings . Join ([] string {
938938 " ★ Founding Lifetime Member — $" + payments .PremiumAmount () + ", one-time" ,
939939 " Only the first " + payments .FoundingCap + " accounts. Pay once, keep it for life." ,
940940 "" ,
@@ -944,33 +944,44 @@ func (a *app) offerPremium(s ssh.Session, u *store.User) {
944944 " • custom domains point yourdomain.com at your homepage" ,
945945 " • Tor a “Tor shell” in your pod — everything over Tor" ,
946946 " • locked-in price founding rate is yours for life — never renew, never pay again" ,
947- "" ,
947+ }, "\n " ))
948+
949+ // Explicit opt-in. Anything but yes leaves with no payment created.
950+ wish .Print (s , "\n Become a Founding member now? Type \" yes\" for a payment address [no]: " )
951+ line , err := readLine (s , in )
952+ if err != nil || ! isYes (line ) {
953+ wish .Println (s , "\n No problem — you're a free member. Want it later? Re-run: ssh join@" + a .host + "\n " )
954+ return
948955 }
949- if c , ok , err := payments .CreatePremiumCharge (ref ); ok && err == nil {
950- // Remember the payment id so a later connect can confirm settlement.
951- if err := a .st .SetPremiumPayment (u .ID , c .ID ); err != nil {
952- log .Error ("store premium payment id" , "err" , err )
953- }
954- amount := "$" + payments .PremiumAmount () + " " + payments .PremiumCurrency ()
955- if c .CryptoAmount != "" {
956- cur := c .Currency
957- if cur == "" {
958- cur = strings .ToUpper (payments .PremiumBlockchain ())
959- }
960- amount += " (≈ " + c .CryptoAmount + " " + cur + ")"
961- }
962- lines = append (lines ,
963- " amount " + amount ,
964- " send to " + c .Address ,
965- )
966- if c .QR != "" {
967- lines = append (lines , " qr " + c .QR )
968- }
969- } else {
956+
957+ ref := payments .PremiumReference (u .PubKeyFP )
958+ c , ok , err := payments .CreatePremiumCharge (ref )
959+ if ! ok || err != nil {
970960 if err != nil {
971961 log .Error ("create premium charge" , "err" , err )
972962 }
973- lines = append (lines , " Payment is temporarily unavailable — please try again shortly." )
963+ wish .Println (s , "\n Payment is temporarily unavailable — please try again shortly.\n " )
964+ return
965+ }
966+ // Remember the payment id so a later connect can confirm settlement.
967+ if err := a .st .SetPremiumPayment (u .ID , c .ID ); err != nil {
968+ log .Error ("store premium payment id" , "err" , err )
969+ }
970+ amount := "$" + payments .PremiumAmount () + " " + payments .PremiumCurrency ()
971+ if c .CryptoAmount != "" {
972+ cur := c .Currency
973+ if cur == "" {
974+ cur = strings .ToUpper (payments .PremiumBlockchain ())
975+ }
976+ amount += " (≈ " + c .CryptoAmount + " " + cur + ")"
977+ }
978+ lines := []string {
979+ "" ,
980+ " amount " + amount ,
981+ " send to " + c .Address ,
982+ }
983+ if c .QR != "" {
984+ lines = append (lines , " qr " + c .QR )
974985 }
975986 lines = append (lines ,
976987 "" ,
@@ -980,6 +991,16 @@ func (a *app) offerPremium(s ssh.Session, u *store.User) {
980991 wish .Println (s , strings .Join (lines , "\n " ))
981992}
982993
994+ // isYes reports whether a prompt line is an affirmative opt-in.
995+ func isYes (line string ) bool {
996+ switch strings .ToLower (strings .TrimSpace (line )) {
997+ case "yes" , "y" :
998+ return true
999+ default :
1000+ return false
1001+ }
1002+ }
1003+
9831004// notifySignup emails the operator the details of a newly verified signup.
9841005// No-op when SMTP isn't configured. Subject is "bbs" per the operator's filter.
9851006func (a * app ) notifySignup (u store.User ) {
0 commit comments