Купить онлайн Самовывоз в магазине: Bonjour Meal - Часть 3 - Интеграция с обработчиком платежей

1. Введение

53003251caaf2be5.png8826bd8cb0c0f1c7.png

Последнее обновление: 13 сентября 2021 г.

Сбор платежей!

Сбор платежей в Business Messages открывает перед вами целый мир новых возможностей для бизнеса на платформе для разговоров. Представьте себе, что потенциальный клиент отправляет вам запрос о продукте, о котором он хочет узнать больше. Как только они получат ответы на свои вопросы, вы сможете закрыть с ними сделку, предоставив платежный шлюз прямо во время разговора.

fe0c6754fb69d708.png

Что делает процесс оплаты удобным?

Хороший опыт оплаты — это когда пользователи могут платить так, как они привыкли.

У пользователей есть предпочтения, когда дело доходит до того, как они платят, и разные способы оплаты более распространены, чем другие, в разных частях мира. С Business Messages вы можете интегрироваться с более чем одним платежным процессором для максимального удобства для пользователей.

Когда пользователь завершает процесс оплаты, вы хотите, чтобы он знал, что вы успешно получили его платеж. Большинство обработчиков платежей включают обратный вызов об успешном или неудачном завершении, который отправляет HTTP-запрос на выбранный вами URL-адрес после завершения потока платежа.

Что вы будете строить

В предыдущем разделе серии статей вы расширили агент Bonjour Meal, чтобы представить каталог товаров, создали корзину, позволяющую пользователям добавлять и удалять товары, и рассчитали общую стоимость корзины. В этом разделе вы собираетесь дополнительно расширить агент, чтобы он мог обрабатывать платежи на основе содержимого корзины.

В этой лаборатории кода ваше приложение будет

  • Интеграция с платежным шлюзом Stripe
  • Разрешить пользователю завершить процесс оплаты на основе цены корзины
  • Отправьте уведомление обратно на диалоговую поверхность, чтобы проинформировать пользователя о состоянии платежа.

ba08a4d2f8c09c0e.png

Что ты будешь делать

  • Интеграция с платежным процессором Stripe.
  • Отправьте запрос в Stripe, чтобы инициировать сеанс оплаты.
  • Обрабатывайте ответы об успешном или неудачном платеже от Stripe.

Что вам понадобится

  • Проект GCP, зарегистрированный и одобренный для использования с Business Messages.
  • Посетите наш сайт для разработчиков, чтобы узнать, как
  • Устройство Android с версией 5 или выше ИЛИ устройство iOS с приложением Google Maps.
  • Опыт программирования веб-приложений
  • Интернет-соединение!

2. Добавление зависимостей

Обновление требований.txt

Поскольку мы будем интегрироваться с платежным процессором Stripe, мы можем использовать клиентскую библиотеку Stripe Python . Добавьте stripe в файл requirements.txt без указания версии, чтобы получить последнюю версию зависимости.

Это необходимо для того, чтобы среда выполнения Google Cloud App Engine Python включала модуль чередования Python.

требования.txt

...
stripe
...

Подготовка bopis/views.py

В верхней части bopis/views.py импортируйте render из django.shortcuts и JsonResponse из django.http . Кроме того, нам потребуется импортировать stripe для поддержки вызовов клиентской библиотеки Stripe Python.

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

3. Работа с полосой

Создайте учетную запись на Stripe.com

В этой кодовой лаборатории мы просто использовали Stripe, но вы можете интегрироваться с любым процессором, который поддерживает веб-интеграцию. Создайте учетную запись на stripe.com . Мы будем использовать этот профиль для тестирования и обучения, чтобы узнать, как вы можете напрямую интегрироваться с любым сторонним платежным процессором.

6731d123c56feb67.png

После того, как вы создали учетную запись и вошли в систему, вы должны увидеть панель инструментов, которая выглядит следующим образом.

6d9d165d2d1fbb8c.png

Убедитесь, что вы работаете в «Тестовом режиме», и нажмите кнопку « Разработчики », как показано на снимке экрана выше, чтобы найти ключи API. Вы должны увидеть два набора ключей API: публикуемый ключ и секретный ключ . Вам понадобятся оба этих ключа для облегчения платежных транзакций с Stripe.

Обновите bopis/views.py

Вашему приложению нужны оба набора ключей, поэтому обновите их в views.py.

Вы можете установить секретный ключ непосредственно в свойстве stripe.api_key и присвоить ему значение секретного ключа, найденного на панели инструментов разработчика Stripe. Затем создайте глобальную переменную STRIPE_PUBLIC_KEY и установите для нее публикуемый ключ.

Кроме того, Stripe необходимо перенаправить обратно на веб-страницу, которой вы управляете, поэтому давайте создадим дополнительную глобальную переменную, чтобы включить общедоступный домен вашего приложения.

В конце этих модификаций у вас будет что-то вроде этого:

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

И это все, что вам нужно сделать для настройки Stripe.

4. Функциональность оформления заказа

Обновите функцию общей цены корзины покупок

В настоящее время функция send_shopping_cart_total_price отправляет только сообщение с указанием цены корзины. Давайте добавим предлагаемое действие , чтобы открыть URL-адрес страницы оформления заказа.

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)

Когда пользователь нажимает на это предлагаемое действие, он перенаправляется на веб-страницу, на которой указана его общая стоимость и кнопка для инициирования оплаты с помощью Stripe.

Давайте создадим простую веб-страницу, которая будет поддерживать этот процесс.

В исходном коде проекта найдите каталог с именем bopis . Создайте новый каталог внутри bopis с именем templates , а внутри templates создайте другой каталог с именем bopis . Это шаблон проектирования Django для указания имени приложения в каталоге шаблонов. Это помогает уменьшить путаницу шаблонов между приложениями Django.

Теперь у вас должен быть каталог с путем bopis/templates/bopis/ . Вы можете создавать файлы HTML в этом каталоге для обслуживания веб-страниц. Django называет их шаблонами, которые отображаются в браузере. Начнем с checkout.html .

В этом каталоге создайте checkout.html . В следующем фрагменте кода отображается кнопка оформления заказа и цена корзины. Он также включает JavaScript для инициации проверки 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>

Нам нужен маршрут к этой веб-странице, когда запрашивается URL-адрес. Для предложенного действия оформления заказа значение openUrlAction равно {YOUR_DOMAIN}/checkout/{conversation_id} . Это означает что-то вроде https://<GCP-Project-ID>.appspot.com/checkout/abc123-cba321-abc123-cba321 . Прежде чем мы создадим этот маршрут, давайте рассмотрим JavaScript, найденный в шаблоне 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>
...

Давайте вместе рассмотрим приведенный выше фрагмент кода.

  1. Сначала он создает объект Stripe с открытым ключом, который передается через контекст из функции просмотра, еще одна парадигма Django.
  2. Затем сниппет ищет на странице элемент с идентификатором checkout-button .
  3. К этому элементу добавляется прослушиватель событий.

Этот прослушиватель событий будет запущен, когда пользователь нажмет или коснется этой кнопки, которая инициирует запрос POST к веб-серверу, указанному вами через URL-адрес: {YOUR_DOMAIN}/create-checkout-session/{conversation_id}.

Вы можете увидеть логику веб-сервера в фрагментах ниже. Когда пользователь нажимает на кнопку с идентификатором « checkout-button », мы можем ожидать, что он вернет идентификатор сеанса Stripe, созданный с использованием Stripe API, с указанием цены корзины.

Если ваш сервер смог создать действительный идентификатор сеанса, логика приложения перенаправит пользователя на страницу Stripe Checkout, в противном случае он предупредит пользователя стандартным сообщением JavaScript о том, что что-то пошло не так.

Начнем с добавления новых путей в массив urlpatterns для поддержки страницы оформления заказа и создания идентификатора сеанса. Добавьте следующее в массив urlpatterns в urls.py.

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

Затем давайте создадим функции просмотра в views.py, чтобы вернуть шаблон checkout.html и сгенерировать сеанс проверки 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)

...

Обе эти функции используют chat_id, чтобы связать корзину с пользователем, а затем определить цену, которую Stripe должна взимать с пользователя.

Эти два метода составляют первую половину платежного потока. Если вы развернете это и протестируете работу, вы увидите форму проверки Stripe, где вы можете совершить оплату с помощью тестовой кредитной карты, как указано в документации разработчика Stripe для тестирования оплаты Visa .

Вторая половина потока — это то, как мы возвращаем пользователя в беседу после получения ответа от Stripe относительно платежа пользователя.

5. Полоса ответов

Когда пользователь участвует в вашем потоке платежей, он либо успешно, либо не может завершить платеж. В функции create_checkout_session мы определили success_url и cancel_url . Stripe будет перенаправлять на один из этих двух URL-адресов в зависимости от состояния платежа. Давайте определим эти два маршрута в urls.py, а затем добавим две функции представления в bopis/views.py для поддержки этих двух возможных потоков.

Добавьте эти строки в файл urls.py.

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

И соответствующие представления будут выглядеть так:

... 

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 перенаправляет обратно на домен, как вы указали в константе DOMAIN , что означает, что вам нужно отображать HTML-ответ через шаблон, иначе веб-сайт будет выглядеть очень голым. Давайте создадим два простых HTML-файла в каталоге bopis/templates/bopis/ вместе с 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>

С помощью этих двух шаблонов пользователь, который завершает процесс оплаты с вашей интеграцией Stripe, перенаправляется на соответствующие URL-адреса и получает соответствующие шаблоны. Они также получат сообщение через Деловые сообщения, позволяющее им вернуться к разговору.

6. Получайте платежи!

Поздравляем, вы успешно интегрировали платежный процессор в свой агент Business Messages!

В этой серии вы развернули веб-приложение в Google Cloud App Engine, установили веб-перехватчик в консоли разработчика Business Communications, расширили приложение для поддержки поиска запасов в статической базе данных и создали корзину для покупок с помощью Google Datastore. В заключительной части серии вы интегрировались с Stripe, платежным процессором, который поддерживает веб-интеграцию и с этим опытом. Теперь вы можете участвовать в интеграции с другими платежными системами и многое другое!

d6d80cf9c9fc621.png44db8d6441dce4c5.png

Что дальше?

Когда вы будете готовы, ознакомьтесь с некоторыми из следующих тем, чтобы узнать о более сложных взаимодействиях, которые вы можете реализовать в Business Messages:

Справочные документы