#07 Como Criar Conta e Assinatura com Stripe no Supabase | [Série] Criando um SaaS do Zero

Samuel Leozão completes Stripe integration with Supabase, building the sign-up flow that creates user accounts, companies, and subscriptions via a Deno Edge Function. The video covers the SelectPlan component, the BaseAPI abstraction for database operations, and the full checkout process that ends with a 14-day trial subscription in Stripe.
Chapters
E aí, meus amigos, tudo bem com vocês? Samuel Leozão por aqui. Hoje a gente vai finalizar nossa integração com Stripe. Então a gente vai criar uma add function para criar o usuário no Stripe e finalizar a parte de assinatura do nosso sistema. Se você é novo no canal, aqui eu falo sobre programação, carreira e esse mundo de SaaS. Então se você gosta desse tipo de conteúdo, deixa o seu like, se inscreve aqui no canal e ative o sininho para você não perder nenhum dos próximos vídeos. Beleza? Então agora a gente vai entrar aqui no nosso 7º vídeo da série e a gente vai criar uma Edge Function de assinatura do Stripe e a gente vai adicionar também uma nova etapa para o usuário selecionar o plano escolhido e cadastrar o usuário, a empresa, iniciar o trial desse usuário de 14 dias. E hoje eu vou fazer um pouco diferente, eu já deixei o desenvolvimento todo pronto e eu vou repassar com vocês arquivo por arquivo mostrando para vocês tudo que eu fiz, cada etapa, para vocês entenderem todo o processo de desenvolvimento. Então vamos entrar aqui no código do nosso projeto. Deixa eu dar um zoom aqui. A gente tem vários arquivos alterados aqui e eu vou repassar para vocês cada coisa que a gente fez e alterou aqui no sistema. Então a primeira coisa que eu corrigi foi a questão do subscription aqui, o step, que ele não tava sendo carregado. Então quando eu entrava aqui na página, no step 2, quando eu recarregava a página, ele não carregava aqui o step 2. Então corrigi isso aqui no ng-on-init de cada um dos meus componentes de step. Então eu seto meu currentStep para o meu step atual.
Então agora a gente tem 3 steps: admin, company e plan, o plan de selecionar o plano, tá bom?
Eu também adicionei um novo componente chamado SelectPlan, e basicamente esse componente ele vai listar os planos que a gente tem no nosso banco de dados e mostrar aqui o nome, a descrição do produto e o preço dele.
Tá bom? Então, como que a gente fez isso?
Entrando aqui no nosso Select Plan, a gente tem a mesma estrutura que a gente seguiu aqui nos outros: o título, a descrição. Então aqui eu coloquei um loading, quando ele tiver carregando os planos a gente dá um loading aqui. E deixa eu mostrar isso para vocês funcionando. Não sei se vocês sabem, mas aqui na aba Network a gente consegue simular uma internet lenta. Então eu consigo vir aqui, ó, colocar o 3G. Então quando a gente muda para a próxima etapa, a gente consegue simular o loading de uma forma mais fácil, tá bom? Então basicamente esse loading tá fazendo aquilo ali. Se eu não fizesse isso, quando eu mudasse para cá, a gente nem conseguiria ver direito porque é mais rápido, mas a gente consegue simular uma internet lenta dessa forma. A gente tem aqui agora um nz-radio-group, que é esse radio button aqui que a gente tá agrupando, tá? E quando a gente dá o nz-radio-group, a gente passa o label aqui do nz-radio. A gente está passando o ID do preço, que é o que a gente vai selecionar. Eu tô fazendo um forEach, criando um card, que quando ele clicar eu vou selecionar o ID do preço. Se o preço tiver selecionado, eu dou um nz-border. Então é por isso que quando eu seleciono o preço, ele muda a borda, que é o do card.
Acho que dando um zoom fica mais fácil de ver.
Ele muda a borda. Do card aqui. Então aqui a gente tem um nz-flex na vertical, a gente colocar o preço e a descrição aqui. Depois eu tenho o nome do produto e depois o valor dele.
Então para o valor a gente pega o unit amount lá do Stripe, divide por 100, porque o unit amount ele vem um valor diferente. Se eu ver aqui no Superbase, dá um select aqui nos produtos Deixa eu dar um zoom aqui para ficar fácil de ver. O Unit Amount vem 1990, esse não é o preço, preço é 19,90. Então a gente divide ele por 100 e dá um currency aqui.
Esse currency ele vai vir do CurrencyPipe que a gente importa no nosso componente.
Deixa eu abrir aqui o nosso componente.
Então a gente tem esse CurrencyPipe aqui e BRLR para ele colocar o símbolo do real.
Tá bom, depois o product description e por último os mesmos botões da etapa anterior e de criar a conta. Na tela de company details a gente alterou aqui que não dá mais o clique para o submit, ele só vai para o subscription select plan, que a gente adicionou mais uma rota aqui, ó, de selecionar o plano.
Bom, então é por isso que quando a gente dá um próximo etapa aqui a gente vai para selecionar o plano e ele já carrega aqui normalmente, que ele já tá salvando lá no nosso local storage.
Então se a gente vem aqui em application, local storage, a gente já consegue ver que o plan tá um price ID aqui. No subscription service eu adicionei um novo form group com price ID, um novo get plan form, e aqui tem mais código que eu vou falar mais para frente com vocês.
Mas essa parte de seleção de banner, deixa eu ver aqui para o nosso componente agora.
Que que a gente faz quando a gente inicia ele?
Eu chamo essa função load, tá bom? Essa função vai dar o start no loading service.
Então a gente chama esse products API getAll aqui, ó.
Quando eu venho nessa função, eu vou explicar para vocês esses arquivos aqui, mas basicamente ele vai lá no Supabase, dá um select no produto e fazendo um inner join nos preços, tá?
Só para os produtos que estão ativos. E eu dou um return esse product aqui.
É dessa forma que a gente faz consulta no Supabase. Então, gente, é a coisa mais simples do mundo. E outra coisa que eu criei foi esse BaseAPI aqui, ó. Então toda API que a gente tiver aqui no nosso sistema, eu vou estender do BaseAPI passando a interface do meu objeto. Então Então quando eu entro aqui no base API, eu tenho aqui, ó, o Supabase que eu injeto ele aqui. Eu tenho um table name que eu vou utilizar nas minhas consultas. E aqui eu tenho as consultas base, então getById.
Eu tenho um select base aqui que ele já vai buscar com active true, um count exec, e toda essa parte do create, delete, update aqui também que a gente tem de ordenar.
Eu tenho um insert, update.
Insert, eu tenho insert many aqui também, caso eu queira inserir vários produtos ou vários itens ao mesmo tempo.
Eu tenho update, e aí aqui no update eu já passo o updatedAt e updatedAtAt, então eu já faço, quando eu chamo esse update por aqui, ele já vai atualizar a data de atualização.
E eu tenho um deleteById, inactiveById, e inactive_by_ids.
Por que que a gente faz isso?
Porque todas as tabelas no nosso sistema, quando a gente vem aqui no Firebase, todas as tabelas têm essa mesma estrutura: um id, created_at, created_by, updated_at, updated_by e active.
Aqui eu não sei se eu já falei isso com vocês no vídeo passado, mas é dessa forma aqui que a gente dá um soft delete. Então Então a gente não exclui o arquivo no banco de dados, a gente dá um active false e a gente entende que ele tá excluído e depois a gente consegue recuperar de uma forma mais fácil, tá bom? Então as nossas APIs a gente faz assim. Então eu tenho também um price API aqui que eu criei, que ele só chama superprice e eu já vou ter todo o CRUD completo para mim utilizar, tá bom? Então Eu dou um select nos produtos e quando o produto chega, o que que eu fiz? Eu dou um set aqui, dou um map para mim já selecionar o preço inicial do produto. Porque esse produtos aqui eu coloquei uma nova interface aqui dentro mesmo só para eu estender do eProduct e colocar um price como um price único, porque o meu produto aqui ele tem uma lista de preços, né? Quando a gente dá um select lá, pode ter um ou mais preços vinculados a esse produto. E eu só quero selecionar o preço inicial desse produto, tá bom?
Então eu seto esse produto aqui, eu pego o productPrices, dou 0 e retorno ele aqui. Se o meu product for maior que 0 e eu não tiver nenhum plano selecionado, eu seleciono o plano principal, eu seleciono o plano inicial, tá bom? E downloadService.stop.
Quando eu clico aqui no botão ou no card, eu chamo esse change plan aqui, que ele basicamente vai mudar o meu selectedPrice para o plano selecionado, e ele vai buscar lá o formulário do SubscriptionService e atualizar o valor do plano para ele já salvar no localStorage.
Show!
E por último, eu tenho submit, que eu vou chamar o submit lá do meu SubscriptionService. Então, pessoal, a parte de seleção de plano é basicamente isso. Essa nova etapa que a gente criou aqui, então seleciona um plano, ele já lista todos os planos para mim. Então eu vou dar um F5 aqui, ó, ele já lista, ele já seta esse plano automaticamente. Que no nosso NG On Miniature, NG FT View Init, eu pego o plan form, o price ID, e se eu tiver o price ID, eu seleciono o plano já automaticamente.
Tá, então a gente já tem uma etapa a mais de nosso sistema.
O usuário vai adicionar os detalhes do administrador.
Deixa eu colocar aqui, só vamos, um, ele mais dois, um telefone e a senha. É que eu vou vir colocar para ver aqui, ó, 210 PJ. Aqui no site eu coloco um set. E coloco o meu número. Deixa eu dar um zoom aqui um pouquinho.
Então coloquei o meu número, próxima etapa aqui eu posso selecionar o meu plano, tá? E aí depois quando eu clicar em criar conta, a gente vai falar sobre isso aqui agora. E aí agora a gente vai falar sobre essa etapa de criar a conta. Então quando a gente vem aqui no Submit, então basicamente a gente tem 3 etapas. A primeira etapa é criar admin, usuário admin. A segunda etapa é criar empresa e a terceira etapa é criar inscrição, tá? Então, primeira etapa, criar usuário admin. Eu vou pegar os valores do meu formulário: nome, e-mail, telefone e senha. Eu vou criar um payload para mim dar um sync up aqui no Supabase. Então, basicamente, eu tô falando e-mail, senha, e dou um options aqui passando full name para ele já atualizar o nome do meu usuário. Então, da mesma forma que a gente fez, que a gente faria, né, lá no login, um cadastro normal, esse aqui é um cadastro normal de um usuário, tá bom? Então, se tiver um erro ou se eu não retornar o meu usuário, eu mostro uma mensagem de erro. Se retornar meu usuário, ele vai retornar um no data.user, e eu crio uma nova variável aqui para ele.
E aí eu chamo no meu service esse método chamado updateUser.
Ele basicamente vai entrar lá na tabela de usuário e atualizar os meus dados de telefone e o status do meu usuário para ativo, tá? Por quê? Por que que a gente está fazendo isso?
Porque aqui no nosso Supabase eu alterei a tabela de usuário. Então quando eu venho aqui em users, a gente tem agora esse status de ativo e a gente tem o telefone do usuário também, tá bom?
Depois disso, eu dou um load lá no Office para ele carregar a sessão do usuário e deixar tudo certinho lá para gente.
Deixa eu remover esse console.log aqui.
Aqui eu vou retornar um novo usuário, não preciso, tá bom? Mas é isso aqui que a gente faz para a gente criar o nosso usuário administrador. O próximo passo é a gente criar a nossa empresa. Então, mesma coisa, eu pego os valores do formulário monta o meu payload para mim inserir na tabela do banco de dados.
Chamo o Supabase from company, insert.
É que eu tô dando um insert já dando um select, que eu retorno um iCompany e é um single. E aí ele vai retornar esse dado aqui nesse data. Eu vou tipar ele aqui. Aqui eu vou pegar o meu userId, porque agora eu vou criar também o meu company payload para mim vincular o meu usuário a uma empresa. Então a gente tem nossa tabela user_company, que a gente vai fazer essa vinculação do usuário a uma empresa. Então dessa forma a gente cria nossa empresa, a gente vincula o usuário a essa empresa, e aí por último, mas não menos importante, a gente dá o create_subscription. E aí aqui a gente tem a nossa linda edge function, tá bom? Mas basicamente eu vou pegar o price_id, vou pegar o meu usuário novamente e vou montar o meu payload, que eu vou passar o company_id price ID, nome, e-mail e telefone para mim criar esse usuário lá no Stripe.
Então aqui a gente chama nossa function dessa forma aqui, ó, this.superbase.functions.invoke createSubscription, e eu passo no body aqui o meu payload.
Pessoal, basicamente na parte do front-end é isso aqui que a gente fez.
A gente adicionou um usuário admin, a gente cria a nossa empresa e a gente cria a nossa assinatura pela nossa Edge Function, porque a gente não tem, a gente não criou um backend para fazer essas coisas, tá bom? Então, da parte de atualizações aqui, a gente tem aqui o nosso Base API que eu criei, eu tenho também o nosso, a minha Company interface que eu criei também, a Company e o Address.
No styles eu adicionei um w-full aqui para colocar lá no nosso subscription page. Então, da parte web do front-end, basicamente é isso aqui, tá bom?
Tem aqui algumas alterações, né? Adicionei o status e coisas desse tipo. A gente consegue ver aqui nas nossas alterações, é user interface, o admin details, que a gente fez algumas alterações aqui, né? A gente dá o current step.
Beleza. Bom, é isso da parte do front-end. E agora a gente vai entrar na nossa edge function. Eu vou mostrar para vocês como que ela tá funcionando. Então, mesmo processo que eu mostrei no vídeo passado sobre criar uma edge function, eu criei essa edge function aqui, adicionei o arquivo de env, adicionei todas as outras coisas, e a gente já tem uma edge function aqui que eu vou repassar linha por linha aqui com vocês. Que ele tá dando esse erro, às vezes o Deno dá esse erro mesmo.
Vou dar um reload aqui no meu VS Code para ver se ele para de dar esses erros.
Que que a nossa function vai fazer? Que quando a gente dá um serve, eu tô passando os headers, porque agora eu criei um arquivo aqui em shared/core e eu passo o hello de origins, porque agora eu tô chamando a minha function do meu front-end. Então eu preciso passar para minha function quais domínios podem chamar ela. Nesse caso eu tô passando o meu localhost aqui mesmo por enquanto, depois a gente vai colocar o nosso domínio para rodar.
A gente coloca esse CORS aqui e chama esse CORS readers passando origem aqui content-type application/json.
E para toda new response aqui eu passo esse reader aqui. Aqui eu tô só verificando se o usuário está autenticado, porque no caso do Stripe a gente colocou para não autenticar. Isso é normal para o usuário, para a questão do Stripe. Stripe não vai autenticar o usuário, mas aqui a gente quer que o usuário esteja autenticado E se o usuário não tiver autenticado, ele não passar um JWT, um token válido, a gente já retorna aqui um erro para ele. E aí que a gente pega os nossos dados que a gente precisa: company_id, price_id, nome, e-mail e telefone. Se ele não passar o company_id ou o price_id, a gente já retorna um erro. E aqui a gente vai criar ou obter o nosso customer.
Então basicamente é uma função que eu pego lá na tabela customers, eu dou um SELECT pelo company ID e eu pego o meu customer.
Se tiver, eu retorno ele. Se não, eu vou criar um cliente novo lá no Stripe. E é por isso que eu preciso do nome, e-mail e telefone. Então aqui eu dou um StripeClient, porque aqui a gente criou a nossa instância do Stripe. Então eu dou um StripeClient customers.create, passo os meus dados do cliente, e aí aqui eu dou um customer data, company ID, Stripe customer ID, e eu vou inserir lá na minha tabela de customers. E por que que eu consigo inserir por aqui? Porque aqui a minha chave do Supabase ela não usa o role level security. Porque eu não sei se vocês lembram, mas nas nossas policies aqui a nossa tabela customers ela tem o role level security habilitado, mas a gente não colocou nada porque o front-end não vai conseguir mexer nada nessa tabela.
É somente o backend ou uma edge function com a chave privada do Supabase que a gente consegue fazer essas alterações.
Então é isso que essa função faz, ela vai criar ou obter o meu customer, eu pego as assinaturas ativas, se for maior que zero eu falo que o cliente já possui uma ou mais assinaturas ativas. Então é basicamente dessa forma aqui que eu verifico. Se não, se ele não tiver uma assinatura, eu vou dar um StripeClient subscriptions.create Então eu passo meu customer ID, os itens que eu quero criar de assinatura, então o price, price ID. E aqui eu tô falando que eu quero dar um trial de 14 dias para esse usuário, tá bom? Então quando o usuário cadastrar no sistema, a gente vai dar 14 dias de trial para ele, e depois a gente vai fazer uma verificação para quando o cliente acessar o sistema, se ele não tiver com assinatura ativa ou paga, a gente redireciona ele para o Stripe para ele cadastrar o cartão de crédito dele.
Tá bom. E pessoal, é isso, a nossa add function, nada mais do que isso.
Isso aqui vai resolver o nosso problema, isso aqui vai criar o nosso usuário.
E depois que ele criar o nosso usuário lá no nosso SubscriptionService, ele vai redirecionar.
Depois que der tudo certo, né, ele redireciona o usuário para tela inicial e reseta o formulário. Deixa eu mostrar isso funcionando ao vivo para vocês aqui então. Então a gente tem aqui, deixa eu voltar, já preenchi os formulários, né, já preenchi essa parte aqui. Eu não tenho que ter medo de recarregar a página. Aqui no meu Stripe eu já tinha criado um aqui de teste, tá bom? Mas eu tenho o meu produto aqui no catálogo, eu tenho todas as minhas coisas aqui no Superbase também. Eu tive que alterar um produto para salvar aqui os preços dele, porque o preço do Essential não tava o que o do Pro, alguma coisa assim. Então só alterei o produto e ele já salvou aqui automaticamente. Então aqui na minha tabela eu tô com os meus produtos cadastrados, os meus preços cadastrados. Aqui eu já tenho 2 customers e 3 empresas aqui que eu fiz de teste. Mas vamos criar aqui mais uma para vocês verem. Então já tá tudo cadastrado, vou dar próxima etapa.
Eu quero só mostrar para vocês aqui o nosso passo a passo no sistema para vocês verem tudo funcionando aqui, ó. Então vou colocar um breakpoint aqui, aqui e aqui, quando eu tiver criando a inscrição. E aqui também.
Então quando eu clicar em criar conta, que que ele vai fazer?
Vai pegar os valores do formulário, vai criar o meu payload com nome, um e-mail e senha e as opções, tá bom?
Pegou o meu usuário, atualizei o meu usuário com o nome e o status dele para active. Carreguei o meu usuário. Agora eu vou criar a minha empresa. Então, form values, gera o payload da minha empresa.
Aqui, se eu entrar, eu já vejo que ele já retornou o ID da empresa, tudo certinho.
Eu vou pegar o meu user ID, vincular o meu usuário à empresa. Não deu nenhum erro.
E agora a gente vai criar a nossa subscription. Então eu pego o meu preço ID aqui que foi selecionado.
Então tem o meu payload e agora eu vou dar um F8 e ele vai criar a minha assinatura. E foi, não deu erro para criar assinatura. Vou dar mais um F8, meu usuário foi redirecionado para a tela inicial.
E agora se a gente vem aqui no Stripe novamente e a gente dá um F5, a gente vai ver que o nosso usuário foi cadastrado aqui. Samuel Rizon dev+2.
Quando eu entro aqui, eu vejo que ele tem uma assinatura que termina no dia 26 de março do Essential. Então aqui já tá tudo prontinho, meu usuário no Stripe já tá funcionando, vai ser cobrado mensalmente sobre essa assinatura, já tá tudo funcionando. E agora vamos lá no Supabase, vamos ver se foi tudo criado também. Então a gente vem aqui no table editor. Company, a gente vê que a Barbearia 2 foi criada, CNPJ, o endereço aqui também tá cadastrado. Aqui na minha tabela customers foi mais um criado também.
Subscriptions eu também tenho mais um, todos eles estão em trial. Eu tenho users aqui, eu tenho o meu mais 2.
E é isso, meus amigos, agora a gente já tá com a parte de cadastro do cliente, assinatura do cliente A gente já tá com essa parte inicial toda pronta e agora a gente vai começar realmente a desenvolver o sistema. Então, para quem pensava, né, que, ah, eu vou desenvolver um SaaS, vou fazer um SaaS, eu já vou começar a desenvolver no sistema, não, gente. A gente tem que preparar todo o ambiente, a gente tem que preparar, deixar tudo ok, tudo funcionando, principalmente essa parte de assinatura de Stripe, de cliente, de pagamento.
Isso aqui é muito importante, então tem que estar bem feito, tem que estar bem organizado.
E a gente já tá com estudo pronto para a gente começar a desenvolver em si o nosso sistema da barbearia. Se você gostou desse vídeo, não se esquece de deixar o seu like, se inscrever aqui no canal, e eu te vejo no próximo vídeo. Muito obrigado, valeu!










