Aula 93 – Django – Ecommerce – Método de Pagamento

Aula 93 – Django – Ecommerce – Método de Pagamento

Loja Online - Django

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:

facebook

 

 


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:

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.

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

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 template payment-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 o clientSecret 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

from django.conf import settings
from django.shortcuts import render

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 atributo data-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 o clientSecret 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 o clientSecret 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 elemento stripe-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

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;
}

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 */
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%;
}

button:hover {
  filter: contrast(115%);
}

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) {
  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.

Por essa aula é só!

Voltar para página principal do blog

Todas as aulas desse curso

Aula 92                                   Aula 94

Nos vemos na próxima então, \o/  😉 Bons Estudos!

About The Author
-

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>