-
Notifications
You must be signed in to change notification settings - Fork 1
Stripe
Documentação de referência: dj-stripe API Keys / Stripe: Build a subscription integration
- Acesse a área de API Keys no Stripe
- Insira o token de acesso privado da conta
- Execute no container da aplicação:
python manage.py djstripe_sync_modelsEste comando sincroniza os modelos do Stripe com a aplicação Django.
⚠️ A sincronização interage diretamente com os dados do Stripe. Tenha cuidado ao modificar ou excluir campos, pois as alterações podem ser irreversíveis.
Este documento detalha a integração do backend com o sistema de pagamentos Stripe, cobrindo a configuração, as operações da API GraphQL e o manuseio de webhooks.
A configuração do Stripe é gerenciada por variáveis de ambiente e difere entre os ambientes de desenvolvimento (local.py) e produção (remote.py).
As seguintes variáveis de ambiente são necessárias para a integração:
-
STRIPE_LIVE_MODE: Define se a API do Stripe operará em modo "live" (produção) ou "test". (Ex:TrueouFalse). -
STRIPE_LIVE_SECRET_KEY: Chave secreta da API do Stripe para o modo "live". -
STRIPE_TEST_SECRET_KEY: Chave secreta da API do Stripe para o modo "test". -
DJSTRIPE_WEBHOOK_SECRET: Chave secreta para validar os webhooks recebidos do Stripe, garantindo que eles são autênticos.
Nota: Para que a integração, especialmente os webhooks, funcione como esperado, o backend precisa estar em um ambiente online acessível pelo Stripe. Localmente, mesmo com as variáveis preenchidas, os eventos de webhook não serão recebidos.
Em backend/settings/local.py e backend/settings/remote.py, o dj-stripe é configurado da seguinte forma:
# Stripe
def as_bool(var):
if isinstance(var, str):
if var.lower() in ["true", "t", "1"]:
return True
return False
STRIPE_LIVE_MODE = as_bool(getenv("STRIPE_LIVE_MODE"))
STRIPE_LIVE_SECRET_KEY = getenv("STRIPE_LIVE_SECRET_KEY")
STRIPE_TEST_SECRET_KEY = getenv("STRIPE_TEST_SECRET_KEY")
DJSTRIPE_WEBHOOK_SECRET = getenv("DJSTRIPE_WEBHOOK_SECRET")
DJSTRIPE_USE_NATIVE_JSONFIELD = True
DJSTRIPE_FOREIGN_KEY_TO_FIELD = "id"A chave da API (stripe.api_key) é definida dinamicamente em backend/apps/account_payment/graphql.py com base no STRIPE_LIVE_MODE.
if settings.STRIPE_LIVE_MODE:
stripe.api_key = settings.STRIPE_LIVE_SECRET_KEY
else:
stripe.api_key = settings.STRIPE_TEST_SECRET_KEYA interação do frontend com o Stripe é feita através de mutações e queries GraphQL. Todas as mutações que envolvem dados sensíveis ou ações de usuário exigem autenticação (@login_required).
-
allStripePrice: Retorna uma lista de todos os planos (preços) disponíveis no Stripe. Permite filtrar porideactive.
-
createStripeCustomer: Cria um cliente (customer) no Stripe associado a uma conta de usuário (Account) no sistema. Se o cliente já existir, a operação é ignorada. -
updateStripeCustomer: Atualiza os dados de um cliente no Stripe, como nome, e-mail e endereço.
-
createStripeSubscription: Cria uma nova assinatura para um usuário.- Se for a primeira assinatura do usuário, um
SetupIntenté criado para configurar o método de pagamento com um trial de 7 dias. - Se não for a primeira, uma assinatura normal é criada, retornando um
client_secretpara o frontend finalizar o pagamento. - Suporta a aplicação de cupons de desconto.
- Se for a primeira assinatura do usuário, um
-
deleteStripeSubscription: Agenda o cancelamento de uma assinatura ao final do período de faturamento (at_period_end=True).
-
addStripeSubscriptionMember: Adiciona um usuário existente na plataforma a uma assinatura "Pro" existente. O usuário adicionado passa a ter os benefícios do plano. -
removeStripeSubscriptionMember: Remove um usuário de uma assinatura. O nomeupdateé um pouco enganoso; a ação real é de remoção. -
removeAllStripeSubscriptionMembers: Remove todos os membros de uma assinatura, exceto o administrador.
-
validateStripeCoupon: Verifica se um cupom de desconto é válido para um plano específico.-
Lógica de Funcionamento:
- O Stripe diferencia
Coupon(a regra de desconto, ex: "10% OFF") doPromotion Code(o código que o cliente usa, ex: "PROMO10"). - A mutação recebe o
Promotion Codee busca por ele no Stripe. - Se o
Promotion Codefor encontrado e estiver ativo, o sistema busca oCouponassociado a ele. - Verifica se o
Coupontambém é válido (não expirou). - Busca o preço do plano (
price_id) no banco de dados local para calcular o valor do desconto (se for percentual). - Calcula o valor final do desconto (seja ele fixo ou percentual).
- Retorna um objeto indicando se o cupom é
is_valid, o valor do desconto (discount_amount) e sua duração (durationeduration_in_months).
- O Stripe diferencia
-
Aplicação do Cupom:
- Se a validação for bem-sucedida, o
Promotion Code(o código original do cupom) pode ser passado para a mutaçãocreateStripeSubscriptionno campocoupon. O Stripe se encarrega de aplicar o desconto na assinatura.
- Se a validação for bem-sucedida, o
-
-
validateStripeCoupon: Verifica se um cupom de desconto é válido e retorna detalhes como o valor do desconto e a duração. -
changeUserGcpEmail: Altera o e-mail do Google Cloud (GCP) de um usuário. Se o usuário tiver uma assinatura ativa, o e-mail antigo é removido do grupo do Google e o novo é adicionado.
Os webhooks são o mecanismo pelo qual o Stripe notifica o backend sobre eventos que ocorrem de forma assíncrona (ex: pagamento confirmado, falha na renovação, etc.). O dj-stripe facilita o recebimento e processamento desses eventos.
Nota Importante: Os ambientes de
development(local) estaging(homologação) compartilham a mesma configuração de webhooks do Stripe no modo de teste. Isso significa que, ao atualizar ou testar um webhook, a alteração impactará ambos os ambientes. É necessário coordenar as atualizações para evitar interrupções.
-
add_user(email): Adiciona um usuário a um grupo específico no Google Workspace. Isso é usado para conceder acesso a recursos "Pro". -
remove_user(email): Remove um usuário do grupo do Google Workspace, revogando o acesso. -
get_subscription(event): Localiza ou cria uma representação interna da assinatura (Subscription) com base no evento recebido do Stripe.
Cada evento do Stripe é tratado por uma função específica decorada com @webhooks.handler("event.name").
-
customer.updated: Se o e-mail de um cliente for alterado no portal do Stripe, este evento atualiza o e-mail na conta de usuário (Account) correspondente no banco de dados. -
customer.subscription.createdecustomer.subscription.updated:- Manipulados pela função
handle_subscription. - Se o status da assinatura for
activeoutrialing, a assinatura interna é marcada como ativa e o usuário é adicionado ao grupo do Google. - Para outros status (ex:
canceled,unpaid), a assinatura é marcada como inativa e o usuário é removido do grupo.
- Manipulados pela função
-
customer.subscription.deleted:- Marca a assinatura interna como inativa.
- Remove o usuário do grupo do Google.
-
customer.subscription.paused:- Marca a assinatura interna como inativa.
- Remove o usuário do grupo do Google.
-
customer.subscription.resumed:- Marca a assinatura interna como ativa.
- Adiciona o usuário de volta ao grupo do Google.
-
setup_intent.succeeded:- Ocorre após o usuário configurar um método de pagamento pela primeira vez (durante o trial).
- Define o método de pagamento como padrão para o cliente no Stripe.
- Cria a assinatura com um período de trial de 7 dias.
- Cadastro e Login: O usuário se cadastra na plataforma.
- Escolha do Plano: O usuário seleciona um plano "Pro" no frontend.
-
Criação da Assinatura:
- O frontend chama a mutação
createStripeSubscription. - Como é a primeira vez, o backend cria um
SetupIntentno Stripe e retorna oclient_secret. - O frontend usa o
client_secretpara exibir o formulário de cartão de crédito do Stripe Elements.
- O frontend chama a mutação
-
Configuração do Pagamento:
- O usuário preenche os dados do cartão.
- O Stripe envia o webhook
setup_intent.succeeded.
-
Início do Trial:
- O handler
setup_intent_succeededrecebe o webhook. - Ele cria a assinatura no Stripe com 7 dias de trial.
- O Stripe, por sua vez, envia o webhook
customer.subscription.createdcom statustrialing.
- O handler
-
Concessão de Acesso:
- O handler
subscribe(viahandle_subscription) recebe o evento. - A assinatura interna é marcada como ativa.
- A função
add_useré chamada para adicionar o e-mail do usuário ao grupo do Google, liberando o acesso "Pro".
- O handler
-
Fim do Trial e Primeiro Pagamento: Após 7 dias, o Stripe tenta cobrar o valor do plano. Se for bem-sucedido, a assinatura passa para o status
active. Se falhar, passa parapast_dueouunpaid. -
Cancelamento:
- O usuário solicita o cancelamento no frontend.
- A mutação
deleteStripeSubscriptioné chamada, cancelando a renovação automática no Stripe. - Ao final do período pago, o Stripe envia o webhook
customer.subscription.deleted.
-
Revogação do Acesso:
- O handler
unsubscriberecebe o evento. - A assinatura interna é marcada como inativa.
- A função
remove_useré chamada, removendo o acesso "Pro" do usuário.
- O handler
Os testes para a integração com o Stripe estão localizados em backend/apps/account_payment/tests.py. Eles utilizam pytest e unittest.mock para simular chamadas à API do Stripe e ao dj-stripe, garantindo que as mutações GraphQL se comportem como esperado sem fazer chamadas reais à rede.
-
test_all_stripe_price_call: Verifica se a queryAllStripePriceretorna uma lista vazia quando não há preços no banco de dados de teste. -
test_create_stripe_customer_call: Garante que, ao chamar a mutaçãoCreateStripeCustomer, os métodosDJStripeCustomer.createeStripeCustomer.modifysão chamados uma vez. -
test_update_stripe_customer_call: Assegura que a mutaçãoUpdateStripeCustomerchama o métodoStripeCustomer.modifypara atualizar os dados do cliente.
- Após a sincronização, acesse
Webhook endpointsno Stripe - Crie um novo webhook para integrar o ambiente Django com o Stripe
Fluxo de Nova Assinatura (com Período de Teste)
sequenceDiagram
participant Usuário
participant Frontend
participant Backend
participant Stripe API
participant Google Workspace
Usuário->>Frontend: 1. Escolhe o plano "Pro"
Frontend->>Backend: 2. Chama a mutação `createStripeSubscription`
Backend->>Stripe API: 3. Cria um `SetupIntent` (pois é a 1ª assinatura)
Stripe API-->>Backend: 4. Retorna o `client_secret` do SetupIntent
Backend-->>Frontend: 5. Retorna o `client_secret`
Frontend->>Usuário: 6. Exibe o formulário de cartão (Stripe Elements)
Usuário->>Frontend: 7. Preenche os dados do cartão
Frontend->>Stripe API: 8. Envia os dados do cartão para o Stripe
Note over Stripe API: Stripe processa e armazena o método de pagamento.
Stripe API-->>Backend: 9. Envia webhook: `setup_intent.succeeded`
Backend->>Stripe API: 10. (No handler do webhook) Cria a assinatura com 7 dias de trial
Stripe API-->>Backend: 11. Envia webhook: `customer.subscription.created` (status: `trialing`)
Note over Backend: Handler `handle_subscription` é acionado.
Backend->>Google Workspace: 12. Adiciona usuário ao grupo "Pro" (`add_user`)
Google Workspace-->>Backend: Confirmação
Backend-->>Usuário: Acesso "Pro" liberado
Fluxo de Assinatura (Sem Período de Teste)
sequenceDiagram
participant Usuário
participant Frontend
participant Backend
participant Stripe API
participant Google Workspace
Usuário->>Frontend: 1. Escolhe o plano "Pro"
Frontend->>Backend: 2. Chama a mutação `createStripeSubscription`
Note over Backend: Backend verifica que o usuário não tem direito a trial.
Backend->>Stripe API: 3. Cria uma assinatura com `payment_behavior="default_incomplete"`
Stripe API-->>Backend: 4. Retorna o objeto da assinatura, incluindo o `client_secret` do `PaymentIntent` da primeira fatura
Backend-->>Frontend: 5. Retorna o `client_secret`
Frontend->>Usuário: 6. Exibe o formulário de cartão (Stripe Elements) para pagamento
Usuário->>Frontend: 7. Preenche os dados e confirma o pagamento
Frontend->>Stripe API: 8. Envia os dados para o Stripe confirmar o pagamento
Note over Stripe API: Stripe processa o pagamento.
Stripe API-->>Backend: 9. Envia webhook: `customer.subscription.created` (status: `active`)
Note over Backend: Handler `handle_subscription` é acionado.
Backend->>Google Workspace: 10. Adiciona usuário ao grupo "Pro" (`add_user`)
Google Workspace-->>Backend: Confirmação
Backend-->>Usuário: Acesso "Pro" liberado
Fluxo de Cancelamento de Assinatura
sequenceDiagram
participant Usuário
participant Frontend
participant Backend
participant Stripe API
participant Google Workspace
Usuário->>Frontend: 1. Solicita o cancelamento da assinatura
Frontend->>Backend: 2. Chama a mutação `deleteStripeSubscription`
Backend->>Stripe API: 3. Agenda o cancelamento no final do período (`cancel_at_period_end=true`)
Stripe API-->>Backend: Confirmação
Backend-->>Frontend: 4. Confirma que o cancelamento foi agendado
Note over Stripe API: O tempo passa...
Note over Stripe API: Fim do período de faturamento chega.
Stripe API-->>Backend: 5. Envia webhook: `customer.subscription.deleted`
Note over Backend: Handler `unsubscribe` é acionado.
Backend->>Google Workspace: 6. Remove usuário do grupo "Pro" (`remove_user`)
Google Workspace-->>Backend: Confirmação
Backend-->>Usuário: Acesso "Pro" revogado
Fluxo de Adição de Membro à Assinatura
sequenceDiagram
participant Admin
participant Frontend
participant Backend
participant Google Workspace
Admin->>Frontend: 1. Adiciona e-mail do novo membro
Frontend->>Backend: 2. Chama a mutação `addStripeSubscriptionMember`
Note over Backend: Valida se o Admin é dono da assinatura.
Backend->>Google Workspace: 3. Adiciona novo membro ao grupo "Pro" (`add_user`)
Google Workspace-->>Backend: Confirmação
Backend-->>Frontend: 4. Retorna sucesso (ok: true)
Frontend-->>Admin: 5. Confirma que o membro foi adicionado