Online-Abholung im Geschäft: Bonjour-Mahlzeit – Teil 3 – Einbindung in einen Zahlungsabwickler

1. Einführung

53003251caaf2be5.png 8826bd8cb0c0f1c7.png

Zuletzt aktualisiert: 13.09.2021

Erheben von Zahlungen

Das Erfassen von Zahlungen in Business Messages eröffnet Ihnen ganz neue Geschäftsmöglichkeiten auf der Plattform für Unterhaltungen. Stellen Sie sich einen potenziellen Kunden vor, der Ihnen eine Anfrage zu einem Produkt sendet, über das er mehr erfahren möchte. Nachdem die Fragen beantwortet wurden, können Sie den Deal abschließen, indem Sie direkt in der Unterhaltung ein Zahlungsportal bereitstellen.

fe0c6754fb69d708.png

Was ist eine gute Zahlungserfahrung?

Eine gute Zahlungsabwicklung ist eine, bei der Nutzer wie gewohnt bezahlen.

Nutzer haben in Bezug auf Zahlungsmethoden Präferenzen und verschiedene Zahlungsmethoden sind in anderen Teilen der Welt üblicher als andere. Mit Business Messages können Sie mehr als einen Zahlungsabwickler einbinden, um Nutzern den größtmöglichen Komfort zu bieten.

Wenn ein Nutzer einen Zahlungsvorgang abschließt, möchten Sie ihn darüber informieren, dass Sie seine Zahlung erfolgreich erhalten haben. Die meisten Zahlungsabwickler haben einen Callback für Erfolg oder Fehler, der nach Abschluss des Zahlungsvorgangs eine HTTP-Anfrage an eine URL Ihrer Wahl sendet.

Inhalt

Im vorherigen Abschnitt der Codelab-Reihe haben Sie den Bonjour-Mahlzeit-Agent um einen Katalog von Artikeln erweitert, einen Warenkorb erstellt, in dem Nutzer Artikel hinzufügen und entfernen können, und den Gesamtpreis des Warenkorbs berechnet. In diesem Abschnitt erweitern Sie den Agent so, dass er Zahlungen basierend auf dem Inhalt des Einkaufswagens verarbeiten kann.

In diesem Codelab wird deine App

  • In das Stripe-Zahlungsgateway einbinden
  • Nutzern erlauben, den Zahlungsvorgang basierend auf dem Preis des Einkaufswagens abzuschließen
  • Eine Benachrichtigung an die Unterhaltungsoberfläche senden, um den Nutzer über den Zahlungsstatus zu informieren

ba08a4d2f8c09c0e.png

Aufgabe

  • Integrieren Sie den Stripe-Zahlungsabwickler.
  • Senden Sie eine Anfrage an Stripe, um eine Zahlungssitzung zu initiieren.
  • Umgang mit Zahlungserfolgen oder Fehlantworten von Stripe.

Voraussetzungen

  • Ein GCP-Projekt, das registriert und für die Verwendung mit Business Messages genehmigt wurde
  • Eine Anleitung dazu finden Sie auf unserer Entwicklerwebsite.
  • Ein Android-Gerät mit Version 5 oder höher ODER ein iOS-Gerät mit der Google Maps App
  • Erfahrung mit Programmen für Webanwendungen
  • Eine Internetverbindung

2. Abhängigkeiten hinzufügen

Die Datei „requirements.txt“ wird aktualisiert

Bei der Einbindung des Stripe-Zahlungsabwicklers können wir die Python-Clientbibliothek von Streifen verwenden. Fügen Sie stripe zu „requirements.txt“ ohne Version hinzu, um die neueste Version der Abhängigkeit zu erhalten.

Dies ist erforderlich, damit die Python-Laufzeit von Google Cloud App Engine das Stripe-Python-Modul enthält.

requirements.txt

...
stripe
...

bopis/views.py wird vorbereitet

Importieren Sie oben aus bopis/views.py render aus django.shortcuts und JsonResponse aus django.http. Außerdem müssen Sie stripe importieren, um Aufrufe an die Stripe Python-Clientbibliothek zu unterstützen.

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

3. Mit Stripe arbeiten

Konto bei Stripe.com erstellen

In diesem Codelab nutzen wir gerade Stripe. Du kannst aber beliebige Prozessoren verwenden, die Webintegration unterstützen. Erstellen Sie auf stripe.com ein Konto. Wir verwenden dieses Profil zu Testzwecken und zu Bildungszwecken, um zu erfahren, wie Sie es direkt in einen externen Zahlungsabwickler einbinden können.

6731d123c56feb67

Sobald Sie ein Konto erstellt und sich angemeldet haben, sollte ein Dashboard wie dieses angezeigt werden.

6d9d165d2d1fbb8c.png

Achten Sie darauf, dass Sie im Modus „Testen“ arbeiten und klicken Sie auf die Schaltfläche Entwickler, wie im Screenshot oben beschrieben, um nach Ihren API-Schlüsseln zu suchen. Sie sollten zwei verschiedene API-Schlüsselsätze sehen: einen veröffentlichbaren Schlüssel und einen geheimen Schlüssel. Sie benötigen beide Schlüssel, um Zahlungstransaktionen mit Stripe zu ermöglichen.

bopis/views.py aktualisieren

Ihre Anwendung benötigt beide Schlüsselsätze. Aktualisieren Sie sie daher unter views.py.

Sie können den geheimen Schlüssel direkt für die Eigenschaft Stripe.api_key festlegen und ihm den Wert des geheimen Schlüssels aus dem Stripe-Entwickler-Dashboard zuweisen. Erstellen Sie dann eine globale Variable mit dem Namen STRIPE_PUBLIC_KEY und legen Sie sie auf den veröffentlichten Schlüssel fest.

Außerdem muss Stripe den Nutzer zu einer von Ihnen verwalteten Webseite weiterleiten. Erstellen Sie daher eine zusätzliche globale Variable, die die öffentlich erreichbare Domain Ihrer Anwendung enthält.

Am Ende dieser Änderungen sehen Sie in etwa Folgendes:

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

Das wars für die Einrichtung von Stripe.

4. Bezahlvorgang

„Gesamtpreis des Einkaufswagens“ aktualisieren

Derzeit sendet die Funktion send_shopping_cart_total_price nur eine Nachricht, die den Preis des Einkaufswagens angibt. Fügen Sie eine vorgeschlagene Aktion hinzu, um eine URL zur Zahlungsseite zu öffnen.

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)

Wenn der Nutzer auf diese vorgeschlagene Aktion tippt, wird er zu einer Webseite weitergeleitet, auf der sein Gesamtpreis sowie eine Schaltfläche zum Bezahlen mit Stripe angezeigt werden.

Lassen Sie uns eine einfache Webseite erstellen, die diesen Vorgang unterstützt.

Suchen Sie im Quellcode des Projekts nach dem Verzeichnis bopis. Erstellen Sie in bopis ein neues Verzeichnis namens templates und in Vorlagen ein weiteres Verzeichnis namens bopis. Dies ist ein GCR-Designmuster, das den Anwendungsnamen im Vorlagenverzeichnis angibt. Sie verringern die Verwechslung von Vorlagen zwischen GCR-Apps.

Sie sollten jetzt ein Verzeichnis mit einem Pfad unter bopis/templates/bopis/ haben. Sie können in diesem Verzeichnis HTML-Dateien erstellen, um Webseiten bereitzustellen. In GCR werden diese Vorlagen als Vorlagen bezeichnet, die im Browser gerendert werden. Fangen wir mit checkout.html an.

Erstellen Sie in diesem Verzeichnis checkout.html. Im folgenden Code-Snippet werden eine Zahlungsschaltfläche und der Preis des Einkaufswagens angezeigt. Sie umfasst auch JavaScript, um den Stripe-Kauf zu starten.

{% 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>

Wir benötigen eine Route zu dieser Webseite, wenn die URL angefordert wird. Der Wert „suggestedAction“ an der Kasse hat den Wert „openUrlAction“ auf {YOUR_DOMAIN}/checkout/{conversation_id} festgelegt. Dies entspricht in etwa https://<GCP-Project-ID>.appspot.com/checkout/abc123-cba321-abc123-cba321. Bevor wir diese Route erstellen, sollten Sie sich den JavaScript-Code aus der HTML-Vorlage ansehen.

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

Gehen wir das Code-Snippet oben gemeinsam durch.

  1. Zuerst wird eine Stripe-Entität mit dem öffentlichen Schlüssel erstellt, der durch den Kontext aus der Ansichtsfunktion übergeben wird, einem weiteren GCR-Modell.
  2. Dann wird nach einem Element auf der Seite mit der ID checkout-button gesucht.
  3. Diesem Element wird ein Ereignis-Listener hinzugefügt.

Dieser Ereignis-Listener wird ausgelöst, wenn ein Nutzer auf diese Schaltfläche klickt oder tippt. Dadurch wird eine POST-Anfrage an den Webserver initiiert, den Sie über die URL {YOUR_DOMAIN}/create-checkout-session/{conversation_id}. angeben.

Die Webserverlogik wird in den Snippets unten angezeigt. Wenn der Nutzer auf die Schaltfläche mit der ID checkout-button tippt, sollte eine Stripe-Sitzungs-ID zurückgegeben werden, die mit der Stripe API erstellt wurde und den Preis des Einkaufswagens angibt.

Wenn Ihr Server eine gültige Sitzungs-ID erzeugen konnte, leitet die Anwendungslogik den Nutzer zu einer Stripe Checkout-Seite weiter. Andernfalls wird der Nutzer mit einer standardmäßigen JavaScript-Nachricht darauf hingewiesen, dass ein Fehler aufgetreten ist.

Fügen Sie dem URL-Muster neue Pfade hinzu, um die Zahlungsseite zu unterstützen und die Sitzungs-ID zu generieren. Fügen Sie dem urlpatterns-Array in urls.py Folgendes hinzu:

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

Anschließend erstellen Sie die Ansichtsfunktionen in views.py, um die Vorlage checkout.html zurückzugeben und den Stripe-Kaufvorgang zu generieren.

... 

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)

...

Bei beiden Funktionen wird die „conversation_id“ verwendet, um dem Nutzer den Einkaufswagen zuzuordnen und anschließend den Preis zu bestimmen, dem Stripe den Nutzer in Rechnung stellen soll.

Diese beiden Methoden bilden die erste Hälfte des Zahlungsvorgangs. Wenn Sie die Bereitstellung durchführen und die Websitevariante testen, sehen Sie ein Stripe-Bezahlformular. Dort können Sie die Zahlung mit einer Testkreditkarte abschließen, wie in der Stripe-Entwicklerdokumentation für den Test von Visa-Bezahl empfohlen.

Die zweite Hälfte des Ablaufs besteht darin, wie wir den Nutzer wieder in die Unterhaltung einbringen, nachdem wir die Antwort von Stripe zur Zahlung des Nutzers erhalten haben.

5. Stripe-Antworten

Wenn ein Nutzer Ihren Zahlungsvorgang abwickelt, konnte er die Zahlung entweder erfolgreich oder nicht abschließen. In der Funktion create_checkout_session haben wir success_url und cancel_url definiert. Stripe leitet den Nutzer je nach Status der Zahlung an eine dieser beiden URLs weiter. Definieren Sie diese beiden Routen in urls.py und fügen Sie dann bopis/views.py zwei Ansichtsfunktionen hinzu, um diese beiden möglichen Abläufe zu unterstützen.

Fügen Sie diese Zeilen der Datei urls.py hinzu.

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

Die entsprechenden Ansichten sehen dann so aus:

... 

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 leitet die Nutzer wieder zur Domain zurück, wie Sie in der Konstante DOMAIN angegeben haben. Das bedeutet, dass Sie eine HTML-Antwort über eine Vorlage rendern müssen oder die Website sehr minimalistisch aussieht. Erstellen Sie im Verzeichnis „bopis/template/bopis/“ zusammen mit „checkout.html“ zwei einfache HTML-Dateien.

bm-django-echo-bot/bopis/ Vorlagen/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/ vorlagen/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>

Mit diesen beiden Vorlagen wird ein Nutzer, der einen Zahlungsvorgang mit der Stripe-Integration durchführt, an die entsprechenden URLs weitergeleitet und mit den entsprechenden Vorlagen dargestellt. Sie erhalten auch eine Nachricht über Business Messages, sodass sie zur Unterhaltung zurückkehren können.

6. Zahlungen erhalten

Glückwunsch, Sie haben einen Zahlungsabwickler erfolgreich in Ihren Business Messages-Agent eingebunden.

In dieser Reihe haben Sie eine Webanwendung in Google Cloud App Engine bereitgestellt, Ihren Webhook in der Business Communications Developer Console eingerichtet, die Anwendung zur Unterstützung der Inventarsuche über eine statische Datenbank erweitert und einen Einkaufswagen mit Google Datastore erstellt. Im letzten Teil der Reihe haben Sie Stripe integriert, einen Zahlungsabwickler, der Webintegrationen und diese Erfahrung unterstützt. Sie können jetzt mit anderen Zahlungsabwicklern zusammenarbeiten.

d6d80cf9c9fc621.png 44db8d6441dce4c5.png

Was liegt als Nächstes an?

Wenn Sie so weit sind, sehen Sie sich die folgenden Themen an, um mehr über komplexere Interaktionen mit Business Messages zu erfahren:

Referenzdokumente