-
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_models
Este 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:True
ouFalse
). -
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_KEY
A 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 porid
eactive
.
-
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_secret
para 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 Code
e busca por ele no Stripe. - Se o
Promotion Code
for encontrado e estiver ativo, o sistema busca oCoupon
associado a ele. - Verifica se o
Coupon
també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 (duration
eduration_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çãocreateStripeSubscription
no 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.created
ecustomer.subscription.updated
:- Manipulados pela função
handle_subscription
. - Se o status da assinatura for
active
outrialing
, 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
SetupIntent
no Stripe e retorna oclient_secret
. - O frontend usa o
client_secret
para 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_succeeded
recebe o webhook. - Ele cria a assinatura no Stripe com 7 dias de trial.
- O Stripe, por sua vez, envia o webhook
customer.subscription.created
com 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_due
ouunpaid
. -
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
unsubscribe
recebe 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 queryAllStripePrice
retorna 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.create
eStripeCustomer.modify
são chamados uma vez. -
test_update_stripe_customer_call
: Assegura que a mutaçãoUpdateStripeCustomer
chama o métodoStripeCustomer.modify
para atualizar os dados do cliente.
- Após a sincronização, acesse
Webhook endpoints
no 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