mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-11 14:00:29 +00:00
Preserve typed hosted trial issuance conflicts
Keep hosted trial request-verification and checkout pages aligned with the canonical issuance conflict type so duplicate recovery-email usage is surfaced distinctly from organization reuse.
This commit is contained in:
@@ -264,7 +264,9 @@ still use a secure hosted Stripe-backed session internally, but the customer
|
||||
copy must present the flow as starting a trial for the originating Pulse
|
||||
instance, not as a generic purchase funnel. Recovery-contact fields such as
|
||||
work email and optional company name must remain clearly secondary to the
|
||||
instance-bound entitlement handoff.
|
||||
instance-bound entitlement handoff. Hosted form-stage issuance conflicts must
|
||||
also preserve the canonical reason shape: duplicate recovery-email usage must
|
||||
not be flattened into an organization-level message.
|
||||
That same hosted owner also applies after Stripe returns to
|
||||
`/trial-signup/complete`: customer-facing completion failures must stay inside
|
||||
owned trial UX rather than dropping raw control-plane error strings, and they
|
||||
|
||||
@@ -664,7 +664,7 @@ func (h *TrialSignupHandlers) HandleRequestVerification(w http.ResponseWriter, r
|
||||
return
|
||||
}
|
||||
if conflict != nil {
|
||||
data.ErrorMessage = "This organization has already used a Pulse Pro trial. Upgrade the existing account or contact support if you need help."
|
||||
data.ErrorMessage = trialSignupIssuanceConflictMessage(conflict)
|
||||
h.renderTrialSignupPage(w, r, http.StatusConflict, data)
|
||||
return
|
||||
}
|
||||
@@ -842,7 +842,7 @@ func (h *TrialSignupHandlers) HandleCheckout(w http.ResponseWriter, r *http.Requ
|
||||
return
|
||||
}
|
||||
if conflict != nil {
|
||||
data.ErrorMessage = "This organization has already used a Pulse Pro trial. Upgrade the existing account or contact support if you need help."
|
||||
data.ErrorMessage = trialSignupIssuanceConflictMessage(conflict)
|
||||
h.renderTrialSignupPage(w, r, http.StatusConflict, data)
|
||||
return
|
||||
}
|
||||
@@ -1428,6 +1428,17 @@ func trialSignupFailureDataForPage(cfg *CPConfig, data trialSignupPageData, kind
|
||||
return page
|
||||
}
|
||||
|
||||
func trialSignupIssuanceConflictMessage(conflict *TrialSignupIssuanceConflict) string {
|
||||
switch {
|
||||
case conflict == nil:
|
||||
return "This Pulse Pro trial request conflicts with a previous trial issuance. Upgrade the existing account or contact support if you need help."
|
||||
case conflict.Kind == trialSignupConflictEmail:
|
||||
return "This recovery email has already used a Pulse Pro trial. Upgrade the existing account or contact support if you need help."
|
||||
default:
|
||||
return "This organization has already used a Pulse Pro trial. Upgrade the existing account or contact support if you need help."
|
||||
}
|
||||
}
|
||||
|
||||
func buildCPURL(baseURL, path string, query url.Values) string {
|
||||
base := strings.TrimSpace(baseURL)
|
||||
if base == "" {
|
||||
|
||||
@@ -186,7 +186,7 @@ func TestTrialSignupHandleRequestVerificationRejectsEmailThatAlreadyUsedTrial(t
|
||||
if rec.Code != http.StatusConflict {
|
||||
t.Fatalf("status=%d, want %d body=%q", rec.Code, http.StatusConflict, rec.Body.String())
|
||||
}
|
||||
if !strings.Contains(rec.Body.String(), "organization has already used a Pulse Pro trial") {
|
||||
if !strings.Contains(rec.Body.String(), "recovery email has already used a Pulse Pro trial") {
|
||||
t.Fatalf("expected duplicate trial message, got %q", rec.Body.String())
|
||||
}
|
||||
}
|
||||
@@ -409,6 +409,37 @@ func TestTrialSignupHandleCheckoutCreatesFreshSessionWhenRecordAlreadyHasSession
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrialSignupHandleCheckoutRejectsEmailThatAlreadyUsedTrial(t *testing.T) {
|
||||
h, store, sender := newTrialSignupTestHandler(t)
|
||||
rawToken := requestTrialVerification(t, h, sender)
|
||||
verifiedToken := verifyTrialRequest(t, h, rawToken)
|
||||
requestID := parseVerifiedTokenRequestID(t, h, verifiedToken)
|
||||
if err := store.MarkTrialIssued(requestID, h.now().UTC()); err != nil {
|
||||
t.Fatalf("MarkTrialIssued: %v", err)
|
||||
}
|
||||
|
||||
form := url.Values{
|
||||
"org_id": {"default"},
|
||||
"return_url": {"https://pulse.example.com/auth/trial-activate"},
|
||||
"instance_token": {"tsi_test"},
|
||||
"name": {"Test User"},
|
||||
"email": {"owner@example.com"},
|
||||
"company": {"Pulse Labs"},
|
||||
}
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/trial-signup/checkout", strings.NewReader(form.Encode()))
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
rec := httptest.NewRecorder()
|
||||
|
||||
h.HandleCheckout(rec, req)
|
||||
|
||||
if rec.Code != http.StatusConflict {
|
||||
t.Fatalf("status=%d, want %d body=%q", rec.Code, http.StatusConflict, rec.Body.String())
|
||||
}
|
||||
if !strings.Contains(rec.Body.String(), "recovery email has already used a Pulse Pro trial") {
|
||||
t.Fatalf("expected duplicate email trial message, got %q", rec.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrialSignupHandleCompleteRedirectsWithActivationToken(t *testing.T) {
|
||||
pub, priv, err := ed25519.GenerateKey(nil)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user