@@ -127,6 +127,38 @@ func TestUserBillingSettingsFreeUserShowsUpgradeCTA(t *testing.T) { |
| 127 | 127 | } |
| 128 | 128 | } |
| 129 | 129 | |
| 130 | +// Regression: a Stripe customer record is created when the user |
| 131 | +// starts checkout, well before any payment lands. A user who |
| 132 | +// abandoned checkout has a customer_id but no subscription_id and |
| 133 | +// must still see "Upgrade to Pro", not "Manage or cancel". |
| 134 | +func TestUserBillingSettingsCustomerButNoSubscriptionShowsUpgrade(t *testing.T) { |
| 135 | + t.Parallel() |
| 136 | + ctx := context.Background() |
| 137 | + srv, pool, captor := newTestServerWithPoolOptions(t, authTestOptions{ |
| 138 | + BillingEnabled: true, |
| 139 | + Stripe: &fakeUserStripeRemote{supportsPro: true}, |
| 140 | + }) |
| 141 | + cli, userID := newBillingTestUser(t, srv, pool, captor, "abandoned") |
| 142 | + deps := userbilling.Deps{Pool: pool} |
| 143 | + if _, err := userbilling.SetStripeCustomerForPrincipal(ctx, deps, userbilling.PrincipalForUser(userID), "cus_abandoned"); err != nil { |
| 144 | + t.Fatalf("SetStripeCustomerForPrincipal: %v", err) |
| 145 | + } |
| 146 | + |
| 147 | + resp := cli.get(t, "/settings/billing") |
| 148 | + defer func() { _ = resp.Body.Close() }() |
| 149 | + body, _ := io.ReadAll(resp.Body) |
| 150 | + s := string(body) |
| 151 | + if !strings.Contains(s, "SUMMARY=Current plan|Free;") { |
| 152 | + t.Errorf("user with no subscription should still be Free: %s", s) |
| 153 | + } |
| 154 | + if !strings.Contains(s, "CHECKOUT=true;") { |
| 155 | + t.Errorf("expected CanStartCheckout=true (abandoned-checkout user): %s", s) |
| 156 | + } |
| 157 | + if !strings.Contains(s, "MANAGE=false;") { |
| 158 | + t.Errorf("user with customer_id but no subscription must NOT see Manage: %s", s) |
| 159 | + } |
| 160 | +} |
| 161 | + |
| 130 | 162 | func TestUserBillingSettingsProUserShowsPlanCardAndPortal(t *testing.T) { |
| 131 | 163 | t.Parallel() |
| 132 | 164 | ctx := context.Background() |