Kup online z odbiorem w sklepie: posiłek Bonjour – część 3 – integracja z firmą obsługującą płatności

1. Wprowadzenie

53003251caaf2be5.png 8826bd8cb0c0f1c7.png

Ostatnia aktualizacja: 13.09.2021

Zbieranie płatności!

Zbieranie płatności w Wiadomościach biznesowych daje Ci dostęp do zupełnie nowych możliwości biznesowych w ramach platformy konwersacyjnej. Wyobraź sobie, że potencjalny klient zadaje Ci pytanie o produkt, o którym chce dowiedzieć się więcej. Gdy użytkownik odpowie na swoje pytania, możesz zawrzeć z nim umowę, bezpośrednio w rozmowie.

fe0c6754fb69d708.png

Co sprawia, że korzystanie z płatności jest wygodne?

Dobra płatność to sposób, w jaki użytkownicy mogą płacić w taki sam sposób, w jaki są do tego przyzwyczajeni.

Użytkownicy mają preferencje dotyczące sposobu płacenia, a różne formy płatności są częściej używane niż w innych częściach świata. Korzystając z Business Messages, możesz przeprowadzić integrację z więcej niż jednym podmiotem przetwarzającym płatności, aby zapewnić użytkownikom jak największy komfort.

Po dokonaniu płatności użytkownik musi poinformować go, że płatność została zrealizowana. Większość firm obsługujących płatności obejmuje wywołania zakończone powodzeniem lub niepowodzeniem, które po zakończeniu procesu płatności wysyłają żądanie HTTP na wybrany adres URL.

Co utworzysz

W poprzedniej sekcji serii ćwiczeń z programowania udało Ci się rozbudować agenta Bonjour Meal, aby zaprezentować katalog produktów, utworzyć koszyk na zakupy, który pozwala użytkownikom dodawać i usuwać produkty, a także obliczać łączną cenę koszyka. W tej sekcji rozszerzysz działanie agenta, aby mógł on przetwarzać płatności na podstawie zawartości koszyka.

Podczas tego ćwiczenia Twoja aplikacja

  • przeprowadzić integrację z bramą płatności Stripe;
  • Pozwól użytkownikowi ukończyć proces płatności na podstawie ceny koszyka
  • Wyślij powiadomienie z powrotem do obszaru rozmowy, aby poinformować użytkownika o stanie płatności

ba08a4d2f8c09c0e.png

Co możesz zrobić

  • Zintegruj z firmą obsługującą płatności Stripe.
  • Wyślij żądanie do Stripe, aby rozpocząć sesję płatności.
  • radzić sobie z odpowiedziami na temat pomyślnych lub nieudanych płatności z Stripe;

Czego potrzebujesz

  • Projekt GCP, który jest zarejestrowany i zatwierdzony do użytku w Business Messages
  • Odpowiednie instrukcje znajdziesz w naszej witrynie dla deweloperów
  • Urządzenie z Androidem w wersji 5 lub nowszej LUB urządzenie z iOS z aplikacją Mapy Google
  • Doświadczenie w programowaniu aplikacji internetowych
  • Połączenie z internetem

2. Dodawanie zależności

Aktualizowanie wymagań w pliku robots.txt

W ramach integracji z firmą obsługującą płatności Stripe możemy użyć biblioteki klienta Stripe Python. Dodaj stripe do wymagań w pliku robots.txt bez wersji, aby uzyskać najnowszą wersję zależności.

Jest to konieczne, aby w środowisku wykonawczym Google Cloud App Engine w Pythonie uwzględnić moduł Paski w Pythonie.

requirements.txt

...
stripe
...

Przygotowywanie pliku balias/views.py

U góry pliku bheader/views.py zaimportuj render z: django.shortcuts i JsonResponse z django.http. Dodatkowo musisz zaimportować plik stripe, aby obsługiwać wywołania biblioteki klienta Stripe Python.

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

3. Praca z paskiem

Utwórz konto na Stripe.com

W trakcie tych ćwiczeń z programowania właśnie korzystaliśmy z Stripe, ale możesz zintegrować go z dowolnym procesorem obsługującym integrację internetową. Utwórz konto na stripe.com. Użyjemy tego profilu do celów edukacyjnych i edukacyjnych, aby dowiedzieć się, jak przeprowadzić bezpośrednią integrację z dowolną firmą obsługującą płatności.

6731d123c56feb67.png

Po utworzeniu konta i zalogowaniu się powinien wyświetlić się taki panel.

6d9d165d2d1fbb8c.png

Upewnij się, że działasz w trybie &testowym, a następnie kliknij przycisk Programiści w sposób opisany na zrzucie ekranu powyżej, aby wyszukać klucze interfejsu API. Powinny być widoczne 2 zestawy kluczy interfejsu API: Możliwy do opublikowania i Tajny klucz. Oba te klucze będą potrzebne do przeprowadzania transakcji płatności w Stripe.

Aktualizowanie pliku balias/views.py

Aplikacja wymaga obu zestawów kluczy, więc zaktualizuj je w pliku view.py.

Możesz ustawić tajny klucz bezpośrednio w usłudze bluee.api_key i przypisać mu wartość klucza tajnego znalezionego w panelu dewelopera Stripe. Następnie utwórz zmienną globalną o nazwie STRIPE_PUBLIC_KEY i ustaw ją na klucz publikacji.

Stripe musi też przekierowywać z powrotem na stronę internetową, którą zarządzasz. Dlatego utwórz kolejną zmienną globalną obejmującą Twoją domenę, do której aplikacja jest publicznie dostępna.

Po zakończeniu tych zmian otrzymasz coś takiego:

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

To wszystko, co trzeba zrobić na potrzeby konfiguracji usługi Stripe.

4. Funkcja płatności

Aktualizowanie funkcji łącznej ceny koszyka

Obecnie funkcja send_shopping_cart_total_price wysyła tylko wiadomość wskazującą cenę koszyka. Dodaj sugerowane działanie, aby otworzyć adres URL na stronie płatności.

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)

Gdy użytkownik kliknie tę sugerowaną czynność, otworzy się strona z łączną ceną i przyciskiem inicjowania płatności za pomocą systemu Stripe.

Utwórz prostą stronę internetową, która będzie obsługiwać ten proces.

W kodzie źródłowym projektu znajdź katalog o nazwie bopis. Utwórz nowy katalog o nazwie templates w katalogu administracyjnym, a w szablonach utwórz kolejny katalog o nazwie bopis. To jest wzorzec projektu Django, który określa nazwę aplikacji w katalogu szablonów. Pomaga to uniknąć nieporozumień między aplikacjami Django.

Powinien wyświetlić się katalog ze ścieżką w lokalizacji bopis/templates/bopis/. W tym katalogu możesz tworzyć pliki HTML służące do wyświetlania stron internetowych. Django to szablony renderowane w przeglądarce. Zacznijmy od checkout.html.

W tym katalogu utwórz checkout.html. Fragment kodu poniżej pokazuje przycisk płatności oraz cenę koszyka. Zawiera też kod JavaScript, który inicjuje płatność za pomocą paska 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>

Żądanie tej strony wymaga podania adresu URL. Działanie sugerowanego procesu płatności ma wartość parametru openUrlAction ustawioną na {YOUR_DOMAIN}/checkout/{conversation_id}. To przekłada się na np. https://<GCP-Project-ID>.appspot.com/checkout/abc123-cba321-abc123-cba321. Zanim utworzymy tę trasę, sprawdź JavaScript w szablonie 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>
...

Omówmy teraz powyższy fragment kodu.

  1. Najpierw tworzy element Stripe z kluczem publicznym, który jest przekazywany przez kontekst z funkcji widoku danych, czyli innego modelu Django.
  2. Następnie fragment kodu szuka na stronie elementu o identyfikatorze checkout-button.
  3. Do tego elementu zostanie dodany detektor zdarzeń.

Ten detektor zdarzeń zostanie aktywowany, gdy użytkownik kliknie ten przycisk, który inicjuje żądanie POST do serwera WWW określonego za pomocą adresu URL: {YOUR_DOMAIN}/create-checkout-session/{conversation_id}.

Informacje na temat logiki serwera WWW znajdziesz w poniższych opisach. Gdy użytkownik kliknie przycisk z identyfikatorem „&&tt”, oczekujemy, że spowoduje to zwrócenie identyfikatora sesji Stripe utworzonego przy użyciu interfejsu Stripe API, który określa cenę koszyka.

Jeśli serwer udało się wygenerować prawidłowy identyfikator sesji, logika aplikacji przekieruje użytkownika na stronę Stripe Checkout. W przeciwnym razie powiadomi użytkownika o standardowym komunikacie JavaScript, że coś poszło nie tak.

Zacznij od dodania do tablicy adresów URL nowych ścieżek, które będą obsługiwać stronę płatności i generować identyfikator sesji. Dodaj poniższy ciąg do urlpatterns z adresu urls.py.

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

Następnie utwórz funkcje widoku w view.py, aby zwrócić szablon checkout.html i wygenerować sesję płatności.

... 

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)

...

Obie te funkcje korzystają z identyfikatora konw_ nam, aby powiązać koszyk na zakupy z użytkownikiem, a następnie określić cenę, którą Stripe obciąży użytkownika.

Na obie te metody składają się pierwsza połowa procesu płatności. Po wdrożeniu i przetestowaniu funkcji zobaczysz formularz płatności Stripe, na którym możesz zrealizować płatność testową kartą kredytową, zgodnie z dokumentacją dla deweloperów Stripe dotyczącą testowania procesu płatności kartą Visa.

Druga połowa procesu to przywrócenie użytkownika do rozmowy, gdy otrzymamy od firmy Stripe odpowiedź na temat płatności tego użytkownika.

5. Paski

Gdy użytkownik uczestniczy w procesie płatności, udało mu się to zrobić lub nie udało mu się go zrealizować. W funkcji create_checkout_session zdefiniowaliśmy success_url i cancel_url. Stripe przekieruje do jednego z tych dwóch adresów URL w zależności od stanu płatności. Zdefiniuj te 2 trasy w adresie urls.py, a następnie dodaj 2 funkcje widoku do pliku balias/views.py, aby obsługiwać te 2 możliwe przepływy.

Dodaj te wiersze do pliku urls.py.

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

Odpowiednie widoki będą wyglądać tak:

... 

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')

...

Pasek jest przekierowywany z powrotem do domeny określonej w stałej DOMAIN, co oznacza, że musisz wygenerować odpowiedź HTML za pomocą szablonu, a witryna będzie wyglądała bardzo błędnie. Utwórz jeden z 2 prostych plików HTML w katalogu b="#/templates/b="#/ i przejdź do witryny checkout.html.

bm-django-echo-bot/bapis/ templates/bapis/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/bapis/ templates/bapis/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>

Dzięki tym 2 szablonom użytkownik, który dokona płatności w ramach integracji z Stripe, zostanie przekierowany na odpowiednie adresy URL i będzie wyświetlany razem z odpowiednimi szablonami. Otrzymają też wiadomość od Business Messages, która umożliwi im powrót do rozmowy.

6. Otrzymywanie płatności!

Gratulacje! Udało Ci się zintegrować firmę obsługującą płatności z agentem Business Messages.

W tej serii wdrożysz aplikację internetową w Google Cloud App Engine, ustawisz webhooka w konsoli programisty Business Communications, rozszerzysz aplikację o wyszukiwanie zasobów reklamowych za pomocą statycznej bazy danych i utworzysz koszyk na zakupy w Google Datastore. W ostatniej części serii zintegrowaliśmy się z Stripe, firmą obsługującą płatności, która obsługuje integrację sieciową i ten interfejs. Możesz teraz korzystać z integracji z innymi firmami obsługującymi płatności.

D6D80cf9c9fc621.png 44db8d6441dce4c5.png

Co dalej?

Aby dowiedzieć się więcej o bardziej złożonych interakcjach, które możesz wykonać w Business Messages, zapoznaj się z artykułami poniżej:

Dokumentacja referencyjna