Aula 93 – Django – Ecommerce – Método de Pagamento
Aula 93 – Django – Ecommerce – Método de Pagamento
![Loja Online - Django Loja Online - Django](https://www.codigofluente.com.br/wp-content/uploads/2019/01/aula-11-loja-online-ecommerce-dj.jpg)
Loja Online – Django
Voltar para página principal do blog
Todas as aulas desse curso
Aula 92 Aula 94
Redes Sociais do Código Fluente:
Scarlett Finch
Scarlett Finch é uma influenciadora virtual criada com IA.
Ela é 🎤 cantora e 🎶compositora pop britânica , 24 anos de idade, adora o Brasil e em especial o Rio de Janeiro.
Siga a Scarlett Finch no Instagram:
Conecte-se comigo!
LinkedIn: Fique à vontade para me adicionar no LinkedIn.
Ao conectar-se comigo, você terá acesso a atualizações regulares sobre desenvolvimento web, insights profissionais e oportunidades de networking no setor de tecnologia.
GitHub: Siga-me no GitHub para ficar por dentro dos meus projetos mais recentes, colaborar em código aberto ou simplesmente explorar os repositórios que eu contribuo, o que pode ajudar você a aprender mais sobre programação e desenvolvimento de software.
Recursos e Afiliados
Explorando os recursos abaixo, você ajuda a apoiar nosso site.
Somos parceiros afiliados das seguintes plataformas:
- https://heygen.com/ – Eleve a produção de seus vídeos com HeyGen! Com esta plataforma inovadora, você pode criar vídeos envolventes utilizando avatares personalizados, ideal para quem busca impactar e conectar com audiências em todo o mundo. HeyGen transforma a maneira como você cria conteúdo, oferecendo ferramentas fáceis de usar para produzir vídeos educativos, demonstrações de produtos e muito mais. Descubra o poder de comunicar através de avatares interativos e traga uma nova dimensão para seus projetos. Experimente HeyGen agora e revolucione sua forma de criar vídeos!
- letsrecast.ai – Redefina a maneira como você consome artigos com Recast. Esta plataforma transforma artigos longos em diálogos de áudio que são informativos, divertidos e fáceis de entender. Ideal para quem está sempre em movimento ou busca uma forma mais conveniente de se manter informado. Experimente Recast agora.
- dupdub.com – Explore o universo do marketing digital com DupDub. Esta plataforma oferece ferramentas inovadoras e soluções personalizadas para elevar a sua estratégia de marketing online. Ideal para empresas que buscam aumentar sua visibilidade e eficiência em campanhas digitais. Descubra mais sobre DupDub.
- DeepBrain AI Studios – Revolucione a criação de conteúdo com a tecnologia de inteligência artificial da DeepBrain AI Studios. Esta plataforma avançada permite que você crie vídeos interativos e apresentações utilizando avatares digitais gerados por IA, que podem simular conversas reais e interações humanas. Perfeito para educadores, criadores de conteúdo e empresas que querem inovar em suas comunicações digitais. Explore DeepBrain AI Studios.
- Audyo.ai – Transforme a maneira como você interage com conteúdo auditivo com Audyo.ai. Esta plataforma inovadora utiliza inteligência artificial para criar experiências de áudio personalizadas, melhorando a acessibilidade e a compreensão de informações através de podcasts, transcrições automáticas e síntese de voz avançada. Ideal para profissionais de mídia, educadores e qualquer pessoa que deseje acessar informações auditivas de maneira mais eficiente e envolvente. Descubra Audyo.ai e suas possibilidades.
- Acoust.io – Transforme sua produção de áudio com Acoust.io. Esta plataforma inovadora fornece uma suite completa de ferramentas para criação, edição e distribuição de áudio, ideal para artistas, produtores e empresas de mídia em busca de excelência e inovação sonora. Acoust.io simplifica o processo de levar suas ideias à realidade, oferecendo soluções de alta qualidade que elevam seus projetos de áudio. Experimente Acoust.io agora e descubra um novo patamar de possibilidades para seu conteúdo sonoro.
- Hostinger – Hospedagem web acessível e confiável. Ideal para quem busca soluções de hospedagem de sites com excelente custo-benefício e suporte ao cliente robusto. Saiba mais sobre a Hostinger.
- Digital Ocean – Infraestrutura de nuvem para desenvolvedores. Oferece uma plataforma de nuvem confiável e escalável projetada especificamente para desenvolvedores que precisam de servidores virtuais, armazenamento e networking. Explore a Digital Ocean.
- One.com – Soluções simples e poderosas para o seu site. Uma escolha ideal para quem busca registrar domínios, hospedar sites ou criar presença online com facilidade e eficiência. Visite One.com.
Educação e Networking
Amplie suas habilidades e sua rede participando de cursos gratuitos e comunidades de desenvolvedores:
- Digital Innovation One – Cursos gratuitos com certificado.
- Workover – Aprenda Python3 gratuitamente.
- Comunidades de desenvolvedores para networking:
Canais do Youtube
Explore nossos canais no YouTube para uma variedade de conteúdos educativos e de entretenimento, cada um com um foco único para enriquecer sua experiência de aprendizado e lazer.
Toti
Toti: Meu canal pessoal, onde posto clips artesanais de músicas que curto tocar, dicas de teoria musical, entre outras coisas.
Scarlett Finch
Scarlett Finch: Cantora e influenciadora criada com IA.
Lofi Music Zone Beats
Lofi Music Zone Beats: O melhor da música Lofi para estudo, trabalho e relaxamento, criando o ambiente perfeito para sua concentração.
Backing Track / Play-Along
Backing Track / Play-Along: Acompanhe faixas instrumentais para prática musical, ideal para músicos que desejam aprimorar suas habilidades.
Código Fluente
Código Fluente: Aulas gratuitas de programação, devops, IA, entre outras coisas.
Putz!
Putz!: Canal da banda Putz!, uma banda virtual, criada durante a pandemia com mais 3 amigos, Fábio, Tatá e Lula.
PIX para doações
![PIX Nubank](https://www.codigofluente.com.br/wp-content/uploads/2021/12/kaya-qr-code.png)
PIX Nubank
Aula 93 – Django – Ecommerce – Método de Pagamento
Código da aula: Github
O que faremos agora é coletar as informações do cartão de crédito e faremos isso usando o Stripes Elements Quick Start.
Lembrando que agora estamos usando um arquivo .env para guardar as nossas variáveis.
Ele tá assim agora:
.env
STRIPE_API_KEY=sk_test_51H*****************************************************feVIeNEBT38006wubaFh6
STRIPE_PUB_KEY=pk_test_51H*****************************************************F7JnJadRkV31ZrMAmOJXI
SECRET_KEY=xj**********************************^1o^
DEBUG=True
ALLOWED_HOSTS=127.0.0.1, .localhost
Settings
Vamos refatorar o settings.py para melhorar ele de acordo com as boas práticas.
O que tá em vermelho é como tava, o que tá em azul é como vai ficar.
e_commerce/e_commerce/settings.py
import os
import environ
import stripe
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Setup environment variables
env = environ.Env(DEBUG=(bool, False),)
environ.Env.read_env(os.path.join(BASE_DIR, '.env'))
print(f"ALLOWED_HOSTS: {env.list('ALLOWED_HOSTS', default=[])}")
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
# SECRET_KEY = 'xjmv-0^l__duq4-xp54m94bsf02lx4&1xka_ykd_(7(5#9^1o^'
SECRET_KEY = env('SECRET_KEY', default='xjmv-0^l__duq4-xp54m94bsf02lx4&1xka_ykd_(7(5#9^1o^')
# SECURITY WARNING: don't run with debug turned on in production!
# DEBUG = True
DEBUG = env.bool('DEBUG', default=True)
ALLOWED_HOSTS = env.list('ALLOWED_HOSTS', default=[])
# Stripe Configuration
STRIPE_API_KEY = env('STRIPE_API_KEY')
STRIPE_PUB_KEY = env('STRIPE_PUB_KEY')
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# our apps
'addresses',
'analytics',
'billing',
'accounts',
'carts',
'orders',
'products',
'search',
'tags',
]
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
AUTH_USER_MODEL = 'accounts.User' # changes the built-in user model to ours
FORCE_SESSION_TO_ONE = False
FORCE_INACTIVE_USER_ENDSESSION = False
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
LOGOUT_REDIRECT_URL = '/login/'
ROOT_URLCONF = 'e_commerce.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'e_commerce.wsgi.application'
# Database
# https://docs.djangoproject.com/en/2.1/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
# Password validation
# https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/2.1/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.1/howto/static-files/
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static_local")
]
STATIC_ROOT = os.path.join(os.path.dirname(BASE_DIR), "static_cdn", "static_root")
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(os.path.dirname(BASE_DIR), "static_cdn", "media_root")
Views do Billing
Importações e Configuração do Stripe
import stripe
: Importa a biblioteca Stripe.stripe.api_key = settings.STRIPE_API_KEY
: Configura a chave da API do Stripe a partir das configurações do Django.
View para Renderizar a Página de Método de Pagamento
def payment_method_view(request)
: View que renderiza o templatepayment-method.html
passando a chave pública do Stripe.context = {'publish_key': settings.STRIPE_PUB_KEY}
: Passa a chave pública para o contexto do template.
Criação do PaymentIntent
@csrf_exempt @require_POST def create_payment_intent(request)
: View que cria um PaymentIntent no Stripe.data = json.loads(request.body)
: Lê os dados da requisição.amount = calculate_order_amount(data['items'])
: Calcula o valor do pedido.intent = stripe.PaymentIntent.create(amount=amount, currency='usd', payment_method_types=['card'])
: Cria um PaymentIntent no Stripe.return JsonResponse({'clientSecret': intent.client_secret})
: Retorna oclientSecret
para o frontend.
Cálculo do Valor do Pedido
def calculate_order_amount(items)
: Função a ser implementada, para calcular o valor do pedido. Aqui, o valor é fixo em 1400 (14.00 USD).
Abra o views do billing e sobrescreva com conteúdo abaixo.
django_ecommerce/e_commerce/billing/views.py
from django.shortcuts import render
from django.http import JsonResponse
from django.views.decorators.http import require_POST
from django.views.decorators.csrf import csrf_exempt
from django.conf import settings
import stripe
import json
stripe.api_key = settings.STRIPE_API_KEY
def payment_method_view(request):
# Adicione um log para verificar se a chave está sendo passada
# print(f"Publish Key na view: {settings.STRIPE_PUB_KEY}")
context = {'publish_key': settings.STRIPE_PUB_KEY}
return render(request, 'billing/payment-method.html', context)
@csrf_exempt
@require_POST
def create_payment_intent(request):
data = json.loads(request.body)
try:
# Calcular o valor com base nos itens enviados
# Substitua esta função pela sua lógica de cálculo de preços
amount = calculate_order_amount(data['items'])
intent = stripe.PaymentIntent.create(
amount=amount,
currency='usd',
payment_method_types=['card'],
)
return JsonResponse({'clientSecret': intent.client_secret})
except Exception as e:
return JsonResponse({'error': str(e)}, status=400)
def calculate_order_amount(items):
# Substitua esta função pela sua lógica de cálculo de preços
return 1400 # preço de exemplo
Template payment-method.html
Crie uma pasta chamada templates dentro de billing e dentro dessa pasta crie uma subpasta chamada billing, dentro crie um arquivo chamado payment-method.html ficando assim: billing/templates/billing/payment-method.html.
Partes relevantes do Template
Chave Pública do Stripe
<div id="stripe-key" data-publish-key="{{ publish_key }}" hidden></div>
: Um elemento oculto que armazena a chave pública do Stripe. Essa chave é necessária para inicializar o Stripe no JavaScript.
Formulário de Pagamento
<form id="payment-form" method="POST">
: Formulário para coleta dos dados do cartão.{% csrf_token %}
: Protege o formulário contra ataques CSRF.<div id="payment-element" class='form-control'>
: Um contêiner onde o Stripe irá inserir os elementos de pagamento.<button class='btn btn-primary my-3' id="submit">
: Botão para submeter o formulário.
Coloque o conteúdo abaixo no arquivo e salve.
billing/templates/billing/payment-method.html
{% extends 'base.html' %} {% load static %} {% block content %} <div class='col-10 col-md-6 mx-auto'> <h1>Add Payment Method</h1> <div id="stripe-key" data-publish-key="{{ publish_key }}" hidden></div> <form id="payment-form" method="POST"> {% csrf_token %} <div id="payment-element" class='form-control'> <!-- Stripe Elements will be inserted here --> </div> <button class='btn btn-primary my-3' id="submit"> <div class="spinner hidden" id="spinner"></div> <span id="button-text">Submit Payment</span> </button> <div id="payment-message" class="hidden"></div> <div id="next-url" data-url="/"></div> </form> </div> {% endblock %}
Javascript Stripe Payment
Estamos usando jQuery e Ajax no projeto, elas são ferramentas importantes para o desenvolvimento web e servem a propósitos distintos:
Ajax (Asynchronous JavaScript and XML) é uma técnica que permite que páginas da web façam requisições ao servidor sem recarregar completamente a página. Isso torna as páginas da web mais dinâmicas e responsivas, pois os usuários podem interagir com a página sem esperar que ela seja recarregada. O Ajax é implementado usando o objeto XMLHttpRequest do JavaScript.
jQuery é uma biblioteca JavaScript que torna o desenvolvimento web front-end mais fácil e eficiente. Ela fornece uma série de funções e métodos para simplificar tarefas comuns como selecionar elementos DOM, manipular eventos, fazer animações e realizar requisições Ajax.
jQuery e Ajax ainda são extremamente utilizados no desenvolvimento web, superando em popularidade frameworks modernos como React e Next.js em diversos cenários.
Dados de pesquisas recentes comprovam essa afirmação:
- 2023 JavaScript Usage Survey:
- jQuery: 67.8% de uso entre desenvolvedores JavaScript.
- React: 57.7% de uso entre desenvolvedores JavaScript.
- Next.js: 16.2% de uso entre desenvolvedores JavaScript.
- 2024 State of JavaScript Survey:
- jQuery: 65.4% de uso entre desenvolvedores JavaScript.
- React: 60.1% de uso entre desenvolvedores JavaScript.
- Next.js: 18.5% de uso entre desenvolvedores JavaScript.
Fatores que contribuem para a popularidade do jQuery e Ajax:
- Facilidade de Uso: Sintaxe simples e intuitiva, ideal para iniciantes e projetos rápidos.
- Maturidade e Comunidade: Ampla comunidade e base de recursos consolidada ao longo dos anos.
- Compatibilidade Abrangente: Funcionam em diversos navegadores, inclusive versões mais antigas.
- Simplicidade: Ideal para projetos simples que não exigem a complexidade de frameworks modernos.
- Leveza: Impacto mínimo no desempenho, especialmente em dispositivos móveis ou com internet lenta.
Aplicações do jQuery e Ajax em 2024:
- Projetos Legados: Manutenção e atualização de websites já existentes que utilizam jQuery e Ajax.
- Desenvolvimento Rápido: Criação rápida de websites e landing pages com prazos apertados.
- Integrações com Sistemas Legados: Integração com APIs e sistemas legados que exigem soluções robustas e confiáveis.
- Habilidades da Equipe: Utilização quando a equipe possui expertise em jQuery e Ajax, otimizando o tempo de desenvolvimento.
- Projetos de Escopo Limitado: Ideal para websites com funcionalidades simples e foco em conteúdo.
Voltando ao projeto
Partes relevantes do JS
Documento Pronto
$(document).ready(function() { ... });
: Garante que o script seja executado apenas quando o DOM estiver completamente carregado.
Inicialização do Stripe
const stripeKeyElement = $('#stripe-key');
: Seleciona o elemento que contém a chave pública do Stripe.const publishKey = stripeKeyElement.data('publishKey');
: Extrai a chave pública do atributodata-publish-key
.const stripe = Stripe(publishKey);
: Inicializa o Stripe com a chave pública.
Requisição AJAX para Criar PaymentIntent
$.ajax({ ... })
: Faz uma requisição POST para o endpoint/create-payment-intent
para obter oclientSecret
necessário para confirmar o pagamento.- Em caso de sucesso, inicializa os elementos do Stripe e monta o campo de cartão no DOM.
Obtenção do Cookie CSRF
function getCookie(name) { ... }
: Função para obter o valor do cookie CSRF, necessário para proteger a requisição AJAX.
Captura da Submissão do Formulário
paymentForm.on("submit", function(event) { ... });
: Captura o evento de submissão do formulário, evita a submissão padrão e inicia o processo de confirmação do pagamento.
Confirmação do Pagamento
stripe.confirmCardPayment(clientSecret, { ... }).then(function(result) { ... });
: Confirma o pagamento com oclientSecret
e o método de pagamento coletado pelo Stripe.- Exibe mensagens de sucesso ou erro com base no resultado da confirmação.
Exibição de Mensagens
function showMessage(messageText) { ... }
: Exibe mensagens ao usuário. As mensagens desaparecem após 4 segundos por causa da parte do setTimeout.
Estado de Carregamento
function setLoading(isLoading) { ... }
: Gerencia o estado de carregamento do botão de submissão, mostrando ou ocultando o spinner e desabilitando ou habilitando o botão.
Observação do Elemento stripe-key
const observer = new MutationObserver(function(mutationsList, observer) { ... });
: Observa mudanças no DOM e inicializa o Stripe quando o elementostripe-key
é adicionado.
Agora crie na pasta static_local/js/ o arquivo stripe_payment.js e cole o conteúdo abaixo.
static_local/js/stripe_payment.js
$(document).ready(function() {
console.log("Documento pronto");
function initializeStripe() {
console.log("Verificando o elemento stripe-key...");
const stripeKeyElement = $('#stripe-key');
console.log("Elemento stripe-key encontrado:", stripeKeyElement); // Deve imprimir o elemento ou null
if (stripeKeyElement.length === 0) {
console.error("Elemento stripe-key não encontrado");
return;
}
const publishKey = stripeKeyElement.data('publishKey');
console.log("Chave pública do Stripe:", publishKey);
if (!publishKey) {
console.error("Chave pública do Stripe não encontrada");
return;
}
const stripe = Stripe(publishKey);
let elements;
let cardElement;
let clientSecret = null;
function initialize() {
console.log("Inicializando os Elementos do Stripe");
$.ajax({
url: "/create-payment-intent",
method: "POST",
contentType: "application/json",
headers: {
"X-CSRFToken": getCookie('csrftoken')
},
data: JSON.stringify({ items: [{ id: "xl-tshirt", price: 2000 }] }),
success: function(data) {
if (data.error) {
console.error('Falha ao criar o intent de pagamento:', data.error);
return;
}
console.log("ClientSecret recebido:", data.clientSecret);
clientSecret = data.clientSecret;
// Inicializar os elementos do Stripe e montar o elemento do cartão
elements = stripe.elements();
cardElement = elements.create("card");
cardElement.mount("#payment-element");
console.log("Elemento do cartão montado");
},
error: function(xhr, status, error) {
console.error("Erro ao inicializar os elementos do Stripe:", error);
}
});
}
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) { const cookie = cookies[i].trim(); if (cookie.substring(0, name.length + 1) === (name + '=')) { cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); break; } } } return cookieValue; } const paymentForm = $("#payment-form"); if (paymentForm.length > 0) {
paymentForm.on("submit", function(event) {
event.preventDefault();
console.log("Evento de submissão capturado");
if (!elements || !cardElement) {
console.error("Elementos não inicializados ou elemento do cartão não montado");
showMessage("Elemento do cartão não inicializado corretamente.");
return;
}
setLoading(true);
console.log("Confirmando o pagamento com clientSecret:", clientSecret);
stripe.confirmCardPayment(clientSecret, {
payment_method: {
card: cardElement,
billing_details: {
// Inclua os detalhes de faturamento se necessário
}
}
}).then(function(result) {
console.log("Resultado do pagamento:", result);
if (result.error) {
console.error("Erro ao confirmar o pagamento:", result.error);
showMessage(result.error.message);
} else {
if (result.paymentIntent && result.paymentIntent.status === 'succeeded') {
showMessage("Pagamento realizado com sucesso!");
console.log("Pagamento realizado com sucesso! Redirecionando...");
// Manter a mensagem visível por 3 segundos antes de redirecionar
setTimeout(function() {
const nextUrlElement = $('#next-url');
const nextUrl = nextUrlElement.data('url') || '/';
window.location.href = nextUrl;
}, 3000);
} else {
console.error("Status do pagamento inesperado:", result.paymentIntent.status);
showMessage("Pagamento não foi bem-sucedido. Tente novamente.");
}
}
setLoading(false);
}).catch(function(error) {
showMessage("Ocorreu um erro inesperado.");
console.error("Erro na promessa de confirmação do pagamento:", error);
setLoading(false);
});
});
} else {
console.error("Formulário de pagamento não encontrado");
}
function showMessage(messageText) {
const messageContainer = $("#payment-message");
messageContainer.text(messageText).removeClass("hidden");
// Pode comentar se quiser esse setTimeout, usado só para dar
// tempo de ver a mensagem
setTimeout(function() {
messageContainer.addClass("hidden").text('');
}, 4000);
}
function setLoading(isLoading) {
const submitButton = $("#submit");
submitButton.prop('disabled', isLoading);
const spinner = $("#spinner");
const buttonText = $("#button-text");
spinner.toggleClass("hidden", !isLoading);
buttonText.toggleClass("hidden", isLoading);
}
initialize();
}
function observeStripeKeyElement() {
const stripeKeyElement = $('#stripe-key');
if (stripeKeyElement.length > 0) {
initializeStripe();
observer.disconnect();
}
}
const observer = new MutationObserver(function(mutationsList, observer) {
for (let mutation of mutationsList) {
if (mutation.type === 'childList' || mutation.type === 'attributes') {
observeStripeKeyElement();
}
}
});
observer.observe(document.body, { childList: true, subtree: true, attributes: true });
observeStripeKeyElement();
});
CSS do Form do Stripe
static_local/css/stripe-custom-styles.css
/* Estilos aplicados apenas ao formulário do Stripe */
#payment-form {
width: 30vw;
min-width: 500px;
align-self: center;
box-shadow: 0px 0px 0px 0.5px rgba(50, 50, 93, 0.1),
0px 2px 5px 0px rgba(50, 50, 93, 0.1), 0px 1px 1.5px 0px rgba(0, 0, 0, 0.07);
border-radius: 7px;
padding: 40px;
}
.hidden {
display: none;
}
#payment-form input {
border-radius: 6px;
margin-bottom: 6px;
padding: 12px;
border: 1px solid rgba(50, 50, 93, 0.1);
height: 44px;
font-size: 16px;
width: 100%;
background: white;
}
.result-message {
line-height: 22px;
font-size: 16px;
}
.result-message a {
color: rgb(89, 111, 214);
font-weight: 600;
text-decoration: none;
}
.hidden {
display: none;
}
#card-error {
color: rgb(105, 115, 134);
text-align: left;
font-size: 13px;
line-height: 17px;
margin-top: 12px;
}
#card-element {
border-radius: 4px 4px 0 0;
padding: 12px;
border: 1px solid rgba(50, 50, 93, 0.1);
height: 44px;
width: 100%;
background: white;
}
#payment-request-button {
margin-bottom: 32px;
}
/* Buttons and links específicos do formulário Stripe */
#payment-form button {
background: #5469d4;
color: #ffffff;
font-family: Arial, sans-serif;
border-radius: 0 0 4px 4px;
border: 0;
padding: 12px 16px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
display: block;
transition: all 0.2s ease;
box-shadow: 0px 4px 5.5px 0px rgba(0, 0, 0, 0.07);
width: 100%;
}
#payment-form button:hover {
filter: contrast(115%);
}
#payment-form button:disabled {
opacity: 0.5;
cursor: default;
}
/* spinner/processing state, errors */
.spinner,
.spinner:before,
.spinner:after {
border-radius: 50%;
}
.spinner {
color: #ffffff;
font-size: 22px;
text-indent: -99999px;
margin: 0px auto;
position: relative;
width: 20px;
height: 20px;
box-shadow: inset 0 0 0 2px;
-webkit-transform: translateZ(0);
-ms-transform: translateZ(0);
transform: translateZ(0);
}
.spinner:before,
.spinner:after {
position: absolute;
content: "";
}
.spinner:before {
width: 10.4px;
height: 20.4px;
background: #5469d4;
border-radius: 20.4px 0 0 20.4px;
top: -0.2px;
left: -0.2px;
-webkit-transform-origin: 10.4px 10.2px;
transform-origin: 10.4px 10.2px;
-webkit-animation: loading 2s infinite ease 1.5s;
animation: loading 2s infinite ease 1.5s;
}
.spinner:after {
width: 10.4px;
height: 10.2px;
background: #5469d4;
border-radius: 0 10.2px 10.2px 0;
top: -0.1px;
left: 10.2px;
-webkit-transform-origin: 0px 10.2px;
transform-origin: 0px 10.2px;
-webkit-animation: loading 2s infinite ease;
animation: loading 2s infinite ease;
}
@-webkit-keyframes loading {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@keyframes loading {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@media only screen and (max-width: 600px) {
#payment-form {
width: 80vw;
}
}
JS Base
Agora coloque os scripts do Stripe no JS base.
django_ecommerce/e_commerce/templates/base/js.html
{% load static %}
<!-- Optional JavaScript -->
<!-- jQuery first, Popper.js, Bootstrap JS and jquery-confirm JS -->
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-confirm/3.3.2/jquery-confirm.min.js"></script>
<!-- Stripe JS -->
<script src="https://js.stripe.com/v3/"></script>
<!-- Custom Stripe payment JS -->
<script src='{% static "js/stripe-payment.js" %}' defer></script>
<!-- eCommerce Custom JS -->
<script src='{% static "js/ecommerce.js" %}'></script>
<!-- Django Secure AJAX JS -->
<script src='{% static "js/csrf.ajax.js" %}'></script>
Endpoints no Urls
Vamos colocar os endpoints no urls.py.
e_commerce/e_commerce/urls.py
from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin
from django.contrib.auth.views import LogoutView
from django.urls import path, include
from django.views.generic import TemplateView
from carts.views import cart_detail_api_view
from accounts.views import LoginView, RegisterView, LogoutView, guest_register_view
from addresses.views import checkout_address_create_view, checkout_address_reuse_view
from billing.views import create_payment_intent, payment_method_view
from .views import (home_page,
about_page,
contact_page
)
urlpatterns = [
path('', home_page, name='home'),
path('about/', about_page, name='about'),
path('contact/', contact_page, name='contact'),
path('cart/', include("carts.urls", namespace="cart")),
path('checkout/address/create/', checkout_address_create_view, name='checkout_address_create'),
path('checkout/address/reuse/', checkout_address_reuse_view, name='checkout_address_reuse'),
path('api/cart/', cart_detail_api_view, name='api-cart'),
path('login/', LoginView.as_view(), name='login'),
path('create-payment-intent', create_payment_intent, name='create-payment-intent'),
path('billing/payment-method/', payment_method_view, name='billing-payment-method'),
path('register/guest/', guest_register_view, name='guest_register'),
path('logout/', LogoutView.as_view(), name='logout'),
path('register/', RegisterView.as_view(), name='register'),
path('bootstrap/', TemplateView.as_view(template_name='bootstrap/example.html')),
path('search/', include("search.urls", namespace="search")),
path('products/', include("products.urls", namespace="products")),
path('admin/', admin.site.urls),
]
if settings.DEBUG:
urlpatterns = urlpatterns + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
urlpatterns = urlpatterns + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Collectstatic
Como criamos arquivos estáticos, o javascript e o CSS, podemos rodar o collectstatic.
python manage.py collectstatic
O comando collectstatic
do Django, coleta arquivos estáticos de várias origens, pastas e subpastas do projeto, incluindo a pasta static_local
, no caso desse projeto, e os copia para o diretório django_ecommerce\static_cdn\static_root
.
Isso é configurado no settings.py
do Django, onde STATIC_ROOT
é definido para apontar para django_ecommerce/static_cdn/static_root
, permitindo que o servidor web sirva esses arquivos de forma eficiente em produção.
Agora Suba o servidor
python manage.py runserver
Acesse: 127.0.0.1:8000/billing/payment-method
O aviso sobre “Third-party cookie will be blocked” se refere às políticas de cookies de terceiros que estão sendo apertadas em muitos navegadores modernos para melhorar a privacidade do usuário.
Quando você está desenvolvendo ou testando integrações com serviços como o Stripe, isso pode aparecer se os cookies necessários para a operação estão sendo bloqueados pelo navegador, especialmente quando não está usando HTTPS.