Acquista online Ritira in negozio: Bonjour Meal - Parte 3 - Integrazione con un elaboratore dei pagamenti

1. Introduzione

53003251caaf2be5.png 8826bd8cb0c0f1c7.png

Ultimo aggiornamento: 13-09-2021

Raccolta pagamenti

La raccolta dei pagamenti tramite Business Messages ti consente di accedere a un mondo completamente nuovo di opportunità di business all'interno della piattaforma di conversazione. Immagina un potenziale cliente che ti invia una richiesta di informazioni su un prodotto di cui vuole avere ulteriori informazioni. Dopo aver risposto alle domande, puoi concludere la trattativa fornendo un gateway per i pagamenti direttamente all'interno della conversazione.

fe0c6754fb69d708.png

Cosa offre una buona esperienza di pagamento?

Per garantire un'esperienza di pagamento positiva, utilizza il metodo di pagamento abituale degli utenti.

Gli utenti hanno preferenze relative alle modalità di pagamento e i diversi metodi di pagamento sono più comuni di altri in diverse parti del mondo. Con Business Messages, puoi eseguire l'integrazione con più responsabili dell'elaborazione dei pagamenti per garantire agli utenti la massima praticità.

Quando un utente completa un flusso di pagamento, vuoi informarlo che hai ricevuto il pagamento. La maggior parte degli elaboratori dei pagamenti include un callback di operazione riuscita o non riuscita che invia una richiesta HTTP a un URL a tua scelta una volta completato il flusso di pagamento.

Cosa imparerai a creare

Nella sezione precedente della serie di codelab, hai esteso l'agente Bonjour Meal a presentare un catalogo di articoli, creato un carrello degli acquisti che consente agli utenti di aggiungere e rimuovere articoli e calcolato il prezzo totale del carrello. In questa sezione estenderai ulteriormente l'agente in modo che possa elaborare i pagamenti in base ai contenuti del carrello degli acquisti.

In questo codelab, la tua app

  • Integrazione con il gateway di pagamento Stripe
  • Consenti a un utente di completare il flusso di pagamento in base al prezzo del carrello.
  • Invia una notifica alla piattaforma di conversazione per informare l'utente dello stato del pagamento

ba08a4d2f8c09c0e.png

Attività previste

  • Integrazione con il processore di pagamento Stripe.
  • Invia una richiesta a Stripe per avviare una sessione di pagamento.
  • Gestisci le risposte riuscite o non riuscite al pagamento da Stripe.

Che cosa ti serve

  • Un progetto GCP registrato e approvato per l'utilizzo con Business Messages
  • Visita il nostro sito per sviluppatori per istruzioni su come fare.
  • Un dispositivo Android con versione 5 o successiva O un dispositivo iOS con l'app Google Maps
  • Esperienza con la programmazione di applicazioni web
  • Una connessione a Internet.

2. Aggiungere dipendenze

Aggiornamento dei requisiti.txt

Poiché stiamo integrando il processore di pagamento Stripe, possiamo utilizzare la libreria client di Python. Aggiungi stripe al filerequirements.txt senza una versione per ottenere la versione più recente della dipendenza.

Questo è necessario affinché il runtime Python di Google Cloud App Engine includa il modulo Python a strisce.

requirements.txt

...
stripe
...

Preparazione bopis/views.py

Nella parte superiore di bopis/views.py, importa render da django.shortcuts e JsonResponse da django.http. Inoltre, dovremo importare stripe per supportare le chiamate alla libreria client Python di Stripe.

...
from django.shortcuts import render
from django.http import JsonResponse
import stripe
...

3. Lavorare con Stripe

Creare un account su Stripe.com

In questo codelab abbiamo appena usato Stripe, ma puoi integrarlo con qualsiasi processore che supporti l'integrazione web. Crea un account su stripe.com. Utilizzeremo questo profilo a scopo di test e verifica per scoprire come puoi integrarlo direttamente con un elaboratore dei pagamenti di terze parti.

6731d123c56feb67.png

Una volta creato un account e una volta effettuato l'accesso, dovresti vedere una dashboard simile a questa.

6d9d165d2d1fbb8c.png

Assicurati di gestire la "Modalità test" e fai clic sul pulsante Sviluppatori come descritto nello screenshot sopra per cercare le tue chiavi API. Dovresti vedere due set di chiavi API: una chiave pubblicabile e una chiave segreta. Dovrai utilizzare entrambe le chiavi per semplificare le transazioni di pagamento con Stripe.

Aggiornare bopis/views.py

La tua applicazione ha bisogno di entrambi i set di chiavi, quindi aggiornali in views.py.

Puoi impostare il codice segreto direttamente nella proprietà Stripe.api_key e assegnargli il valore della Secret Key trovato nella dashboard per sviluppatori Stripe. Quindi, crea una variabile globale denominata STRIPE_PUBLIC_KEY e impostala sulla chiave pubblicabile.

Inoltre, Stripe deve reindirizzare a una pagina web che gestisci, quindi creiamo un'ulteriore variabile globale per includere il dominio raggiungibile pubblicamente della tua applicazione.

Al termine di queste modifiche, noterai una dicitura simile alla seguente:

stripe.api_key = 'sk_test_abcde-12345'
STRIPE_PUBLIC_KEY = 'pk_test_edcba-54321'
YOUR_DOMAIN = 'https://<GCP_PROJECT_ID>.appspot.com'

E questo è tutto ciò che devi fare per configurare Stripe.

4. Funzionalità di pagamento

Aggiornare la funzione di prezzo totale del carrello degli acquisti

Attualmente, la funzione send_shopping_cart_total_price invia solo un messaggio con il prezzo del carrello degli acquisti. Aggiungiamo un'azione suggerita per aprire un URL alla pagina di pagamento.

def send_shopping_cart_total_price(conversation_id):
  """Sends shopping cart price to the user through Business Messages.

  Args:
    conversation_id (str): The unique id for this user and agent.
  """
  cart_price = get_cart_price(conversation_id)

  message_obj = BusinessMessagesMessage(
      messageId=str(uuid.uuid4().int),
      representative=BOT_REPRESENTATIVE,
      text=f'Your cart\'s total price is ${cart_price}.',
      suggestions=[
          BusinessMessagesSuggestion(
              action=BusinessMessagesSuggestedAction(
                  text='Checkout',
                  postbackData='checkout',
                  openUrlAction=BusinessMessagesOpenUrlAction(
                      url=f'{YOUR_DOMAIN}/checkout/{conversation_id}'))),
      ]
    )

  send_message(message_obj, conversation_id)

Quando l'utente tocca questa azione suggerita, viene indirizzato a una pagina web che mostra il prezzo totale e un pulsante per avviare il pagamento con Stripe.

Costruiamo una semplice pagina web che supporterà questo flusso.

Nel codice sorgente del progetto, individua la directory bopis. Crea una nuova directory all'interno del bopis denominata templates e, all'interno dei modelli, crea un'altra directory denominata bopis. Questo è un pattern di progettazione Django per specificare il nome dell'app all'interno della directory dei modelli. Contribuisce a ridurre la confusione tra i modelli delle app Django.

A questo punto dovresti avere una directory con un percorso all'indirizzo bopis/templates/bopis/. Puoi creare file HTML in questa directory per pubblicare pagine web. Questi modelli vengono definiti Django come visualizzati nel browser. Iniziamo con checkout.html.

In questa directory, crea checkout.html. Il seguente snippet di codice mostra un pulsante di pagamento e il prezzo del carrello. Include anche JavaScript per avviare il pagamento della Stripe.

{% load static %}

<!DOCTYPE html>
<html>
  <head>
    <title>Purchase from Bonjour Meal</title>

    <script src="https://js.stripe.com/v3/"></script>
    <style>
      .description{
        font-size: 4em;
      }
      button {
        color: red;
        padding: 40px;
        font-size: 4em;
      }
    </style>
  </head>
  <body>
    <section>
      <img
        src="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png"
        alt="Bonjour Meal Restaurant"
      />
      <div class="product">
        <div class="description">
          <h3>Your Bonjour Meal Total</h3>
          <h5>${{cart_price}}</h5>
        </div>
      </div>
      <button type="button" id="checkout-button">Checkout</button>
    </section>
  </body>
  <script type="text/javascript">
    // Create an instance of the Stripe object with your publishable API key
    var stripe = Stripe("{{stripe_public_key}}");
    var checkoutButton = document.getElementById("checkout-button");

    checkoutButton.addEventListener("click", function () {
      fetch("/create-checkout-session/{{conversation_id}}", {
        method: "POST",
      })
        .then(function (response) {
          return response.json();
        })
        .then(function (session) {
          return stripe.redirectToCheckout({ sessionId: session.id });
        })
        .then(function (result) {
          // If redirectToCheckout fails due to a browser or network
          // error, you should display the localized error message to your
          // customer using error.message.
          if (result.error) {
            alert(result.error.message);
          }
        })
        .catch(function (error) {
          console.error("Error:", error);
        });
    });
  </script>
</html>

Quando l'URL viene richiesto, serve un percorso a questa pagina web. Il valore suggerito openUrlAction per il checkout è impostato su {YOUR_DOMAIN}/checkout/{conversation_id}. Ciò si traduce in un formato simile a https://<GCP-Project-ID>.appspot.com/checkout/abc123-cba321-abc123-cba321. Prima di creare questo percorso, esaminiamo il codice JavaScript trovato nel modello HTML.

...
  <script type="text/javascript">
    // Create an instance of the Stripe object with your publishable API key
    var stripe = Stripe("{{stripe_public_key}}");
    var checkoutButton = document.getElementById("checkout-button");

    checkoutButton.addEventListener("click", function () {
      fetch("/create-checkout-session/{{conversation_id}}", {
        method: "POST",
      })
        .then(function (response) {
          return response.json();
        })
        .then(function (session) {
          return stripe.redirectToCheckout({ sessionId: session.id });
        })
        .then(function (result) {
          // If redirectToCheckout fails due to a browser or network
          // error, you should display the localized error message to your
          // customer using error.message.
          if (result.error) {
            alert(result.error.message);
          }
        })
        .catch(function (error) {
          console.error("Error:", error);
        });
    });
  </script>
...

Esaminiamo insieme lo snippet di codice riportato sopra.

  1. Innanzitutto, crea un'entità Stripe con la chiave pubblica trasferita nel contesto dalla funzione di visualizzazione, un altro paradigma di Django.
  2. Successivamente, lo snippet cerca un elemento della pagina con l'ID checkout-button.
  3. A questo elemento viene aggiunto un listener di eventi.

Questo listener di eventi viene attivato quando un utente fa clic o tocca questo pulsante, che avvia una richiesta POST al server web specificato tramite l'URL: {YOUR_DOMAIN}/create-checkout-session/{conversation_id}.

Puoi vedere la logica del server web negli snippet di seguito. Quando l'utente tocca il pulsante con l'ID"checkout-button"può aspettarsi di restituire un ID sessione Stripe creato usando l'API Stripe che specifica il prezzo del carrello.

Se il server è riuscito a produrre un ID sessione valido, la logica dell'applicazione reindirizza l'utente a una pagina di Stripe Checkout. In caso contrario, avvisa l'utente con un messaggio JavaScript standard che indica che si è verificato un problema.

Iniziamo con l'aggiunta di nuovi percorsi all'array urlpatterns per supportare la pagina di pagamento e generare l'ID sessione. Aggiungi quanto segue alla matrice urlpatterns in url.py.

... 
path('checkout/<str:conversation_id>', bopis_views.payment_checkout),
path('create-checkout-session/<str:conversation_id>', bopis_views.create_checkout_session),
...

Quindi creiamo le funzioni di visualizzazione in views.py per restituire il modello checkout.html e generare la sessione di pagamento di Stripe.

... 

def payment_checkout(request, conversation_id):
  """Sends the user to a payment confirmation page before the payment portal.

  Args:
    request (HttpRequest): Incoming Django request object
    conversation_id (str): The unique id for this user and agent.

  Returns:
    Obj (HttpResponse): Returns an HTTPResponse to the browser
  """

  cart_price = get_cart_price(conversation_id)
  context = {'conversation_id': conversation_id,
             'stripe_public_key': STRIPE_PUBLIC_KEY,
             'cart_price': cart_price
            }
  return render(request, 'bopis/checkout.html', context)


@csrf_exempt
def create_checkout_session(request, conversation_id):
  """Creates a Stripe session to start a payment from the conversation.

  Args:
    request (HttpRequest): Incoming Django request object
    conversation_id (str): The unique id for this user and agent.

  Returns:
    Obj (HttpResponse): Returns an HTTPResponse to the browser
  """
  cart_price = get_cart_price(conversation_id)
  try:
    checkout_session = stripe.checkout.Session.create(
        payment_method_types=['card'],
        line_items=[
            {
                'price_data': {
                    'currency': 'usd',
                    'unit_amount': int(cart_price*100),
                    'product_data': {
                        'name': 'Bonjour Meal Checkout',
                        'images': ['https://storage.googleapis.com/bonjour-rail.appspot.com/apple-walnut-salad.png'],
                    },
                },
                'quantity': 1,
            },
        ],
        mode='payment',
        success_url=YOUR_DOMAIN + '/success/' + conversation_id,
        cancel_url=YOUR_DOMAIN + '/cancel/' + conversation_id,
    )

    return JsonResponse({
        'id': checkout_session.id
    })

  except Exception as e:
    # Handle exceptions according to your payment processor's documentation
    # https://stripe.com/docs/api/errors/handling?lang=python
    return HttpResponse(e)

...

Entrambe queste funzioni utilizzano la conversazione_id per associare il carrello degli acquisti all'utente e quindi per stabilire il prezzo che Stripe deve addebitare all'utente.

Questi due metodi costituiscono la prima metà del flusso di pagamento. Se esegui il deployment di questa ed esegui il test dell'esperienza, visualizzerai un modulo di pagamento Stripe in cui potrai completare il pagamento con una carta di credito di prova, come indicato nella documentazione per gli sviluppatori di Stripe per il test del pagamento tramite Visa.

La seconda metà del flusso consiste nel riportare l'utente nella conversazione una volta ricevuta la risposta da Stripe in merito al pagamento dell'utente.

5. Strisce di risposte

Quando un utente interagisce con il flusso di pagamento, è riuscito o meno a completare il pagamento. Nella funzione create_checkout_session, abbiamo definito un success_url e un cancel_url. Stripe reindirizza uno dei due URL a seconda dello stato del pagamento. Cerchiamo di definire queste due route in url.py e di aggiungere due funzioni di visualizzazione a bopis/views.py per supportare queste due possibili modalità.

Aggiungi queste righe al file url.py.

... 
    path('success/<str:conversation_id>', bopis_views.payment_success),
    path('cancel/<str:conversation_id>', bopis_views.payment_cancel),
...

Le visualizzazioni corrispondenti avranno il seguente aspetto:

... 

def payment_success(request, conversation_id):
  """Sends a notification to the user prompting them back into the conversation.

  Args:
    request (HttpRequest): Incoming Django request object
    conversation_id (str): The unique id for this user and agent.

  Returns:
    Obj (HttpResponse): Returns an HTTPResponse to the browser
  """
  message_obj = BusinessMessagesMessage(
      messageId=str(uuid.uuid4().int),
      representative=BOT_REPRESENTATIVE,
      suggestions=[
          BusinessMessagesSuggestion(
              reply=BusinessMessagesSuggestedReply(
                  text='Check on order', postbackData='check-order')),
      ],
      text='Awesome it looks like we\'ve received your payment.')

  send_message(message_obj, conversation_id)

  return render(request, 'bopis/success.html')


def payment_cancel(request, conversation_id):
  """Sends a notification to the user prompting them back into the conversation.

  Args:
    request (HttpRequest): Incoming Django request object
    conversation_id (str): The unique id for this user and agent.

  Returns:
    Obj (HttpResponse): Returns an HTTPResponse to the browser
  """
  message_obj = BusinessMessagesMessage(
      messageId=str(uuid.uuid4().int),
      representative=BOT_REPRESENTATIVE,
      suggestions=[
          BusinessMessagesSuggestion(
              action=BusinessMessagesSuggestedAction(
                  text='Checkout',
                  postbackData='checkout',
                  openUrlAction=BusinessMessagesOpenUrlAction(
                      url=f'{YOUR_DOMAIN}/checkout/{conversation_id}'))),
      ],
      text='It looks like there was a problem with checkout. Try again?')

  send_message(message_obj, conversation_id)

  return render(request, 'bopis/cancel.html')

...

Stripe reindirizza al dominio come specificato nella costante DOMAIN, il che significa che devi eseguire il rendering di una risposta HTML tramite un modello o se il sito web sarà molto semplice. Creiamo due semplici file HTML nella directory bopis/templates/bopis/ insieme a checkout.html.

bm-django-echo-bot/bopis/ templates/bopis/success.html

{% load static %}

<html>
<head>
  <title>Business Messages Payment Integration Sample!</title>
  <style>
    p{
      font-size: 4em;
    }
  </style>
</head>
<body>
  <section>

    <p>
      Checkout succeeded - We appreciate your business!
      <br/><br/>
      For support related questions, please email
      <a href="mailto:bm-support@google.com">bm-support@google.com</a>.

    </p>
  </section>
</body>
</html>

bm-django-echo-bot/bopis/ templates/bopis/cancel.html

{% load static %}

<html>
<head>
  <title>Checkout canceled</title>
  <style>
    p{
      font-size: 4em;
    }
    </style>
</head>
<body>
  <section>
    <p>Checkout canceled - Forgot to add something to your cart? Shop around then come back to pay!</p>
  </section>
</body>
</html>

Con questi due modelli, un utente che completa un flusso di pagamento con l'integrazione con Stripe viene reindirizzato agli URL appropriati e viene presentato con i rispettivi modelli. Inoltre, riceveranno un messaggio tramite Business Messages con cui potranno tornare alla conversazione.

6. Ricevi pagamenti

Complimenti, hai integrato correttamente un elaboratore dei pagamenti nel tuo agente Business Messages.

In questa serie hai eseguito il deployment di un'applicazione web in Google Cloud App Engine, hai impostato il webhook in Business Communications Developer Console, hai esteso l'applicazione per supportare la ricerca dell'inventario tramite un database statico e hai creato un carrello degli acquisti utilizzando Google Datastore. Nella parte finale della serie hai eseguito l'integrazione con Stripe, un processore di pagamento che supporta le integrazioni web e con questa esperienza. Ora puoi interagire con altre aziende di elaborazione dei pagamenti e altro ancora.

d6d80cf9c9fc621.png 44db8d6441dce4c5.png

E dopo?

Quando è tutto pronto, controlla alcuni dei seguenti argomenti per scoprire quali interazioni più complesse puoi ottenere in Business Messages:

Documenti di riferimento