Cadastro de Usuário Trial
TLDR: endpoint público
POST /v1/trials/registerque valida a campanha, protege contra abuso de trial e conta existente, cria o usuário e o vínculo de trial, e dispara e-mails transacionais.
Status: proposed Created: 2026-08-11 Owner: @prattiz
Context
O marketing usa campanhas (model Campaign) com links de rastreamento para atrair novos usuários. O fluxo de trial precisa de um endpoint público que receba email, phone e slug de campanha, valide elegibilidade e crie o usuário já vinculado ao trial da campanha — sem exigir autenticação prévia.
Objectives
- Criar endpoint
POST /v1/trials/register(público,AllowAny). - Validar que a campanha existe e está
active. - Bloquear e notificar quando o email ou phone já existem como conta padrão.
- Bloquear e notificar quando o email ou phone já foram usados em algum trial anterior.
- Criar o usuário com senha aleatória segura.
- Criar o
UserTrialvinculado à campanha e ao trial da campanha. - Enviar e-mail de boas-vindas com credenciais ao novo usuário.
Non-goals
- Criar o
Trialou aCampaignvia endpoint — são dados administrativos. - Retornar tokens JWT na resposta — o usuário fará login normalmente depois.
- Validar se o
Trialassociado à campanha está ativo — essa garantia é responsabilidade do administrador ao configurar a campanha. - Renovação de trial (
allow_renewal) — fora de escopo.
Changes
apps/trials/services.py (novo)
Módulo de serviço com a função register_user_trial(email, phone, slug):
- Busca
Campaignpeloslugcomstatus=active; lançaValidationErrorse não encontrada. - Verifica
User.objects.filter(Q(email=email) | Q(phone=phone)):- Se existir → dispara Celery task
send_trial_rejected_existing_accounte lançaValidationError.
- Se existir → dispara Celery task
- Verifica
UserTrial.objects.filter(user__email=email) | UserTrial.objects.filter(user__phone=phone)— join viaall_objectspara incluir soft-deleted:- Se existir → dispara Celery task
send_trial_rejected_already_usede lançaValidationError.
- Se existir → dispara Celery task
- Gera senha aleatória segura (
secrets.token_urlsafe(12)). - Cria o
User(email,phone,name="") comuser.set_password(password). - Calcula
started_at = now(),expires_at = started_at + timedelta(days=trial.duration_days). - Cria
UserTrial(user, trial, campaign, started_at, expires_at, status=ACTIVE). - Dispara Celery task
send_trial_welcomecom email e senha gerada. - Retorna o
UserTrialcriado.
Rastreio de origem: satisfeito pelo
UserTrial.campaignFK — não é necessário campo adicional emUser.
apps/trials/serializer.py (atualizar)
Adicionar UserTrialRegisterSerializer(serializers.Serializer):
- email — EmailField
- phone — CharField
- slug — CharField
- validate() (ou create()) delega para register_user_trial.
apps/trials/views.py (atualizar)
Adicionar UserTrialRegisterView(APIView):
- permission_classes = [AllowAny]
- authentication_classes = []
- POST: valida serializer, chama service, retorna 201.
apps/trials/urls.py (novo)
python
urlpatterns = [
path("register", UserTrialRegisterView.as_view(), name="trial-register"),
]
routes/api.py (atualizar)
Adicionar dentro do bloco v1/:
python
re_path(r"^trials\/?", include(("apps.trials.urls", "trials"), namespace="trials")),
apps/trials/tasks.py (novo)
Três tasks Celery (queue default):
send_trial_welcome(email, name, password)— chamaSendEmails.trial_welcome.send_trial_rejected_existing_account(email)— chamaSendEmails.trial_rejected_existing_account.send_trial_rejected_already_used(email)— chamaSendEmails.trial_rejected_already_used.
apps/emails/send_emails.py (atualizar)
Adicionar três métodos estáticos em SendEmails:
trial_welcome(data)— templateemails/trial_welcome.html; subject:"Seu acesso trial está liberado - Onion".trial_rejected_existing_account(data)— templateemails/trial_rejected_existing_account.html; subject:"Não foi possível criar seu trial - Onion".trial_rejected_already_used(data)— templateemails/trial_rejected_already_used.html; subject:"Não foi possível criar seu trial - Onion".
Templates de e-mail (3 novos)
Criar em templates/emails/ seguindo o padrão dos existentes:
- trial_welcome.html — boas-vindas, credenciais (email + senha), links de download.
- trial_rejected_existing_account.html — informa que já existe conta ativa.
- trial_rejected_already_used.html — informa que trial já foi utilizado anteriormente.
How to verify
POST /v1/trials/registercom slug de campanha inexistente →400com mensagem de erro.POST /v1/trials/registercom email de usuário existente →400+ e-mailtrial_rejected_existing_accountdisparado.POST /v1/trials/registercom email de usuário que já teve trial →400+ e-mailtrial_rejected_already_useddisparado.POST /v1/trials/registercom dados válidos →201,Usercriado,UserTrialcriado comstatus=active,campaignapontando para a campanha correta, e-mailtrial_welcomedisparado.python manage.py checkpassa sem erros.
Documentation
Criar .project/docs/rules/trials/user_trial_register.md com as regras de negócio de elegibilidade (conta existente bloqueia, trial anterior bloqueia, campanha inativa bloqueia).