Acheter votre commande en ligne avec un repas en magasin – Partie 3 – Intégration à un outil de traitement des paiements

1. Présentation

53003251caaf2be5.png 8826bd8cb0c0f1c5.png

Dernière mise à jour:13/09/2021

Collecter des paiements

Collecter des paiements sur Business Messages vous donne accès à une multitude de nouvelles opportunités commerciales sur la plate-forme de conversation. Imaginons qu'un client potentiel vous envoie une demande au sujet d'un produit dont il souhaite en savoir plus. Une fois que vous avez obtenu la réponse à leurs questions, vous pouvez conclure l'accord avec eux en fournissant une passerelle de paiement dans la conversation.

F0C6754fb69d708.png

Qu'est-ce qu'une bonne expérience de paiement ?

Pour que les utilisateurs bénéficient d'une bonne expérience de paiement, ils doivent payer comme ils le font d'habitude.

Les utilisateurs ont des préférences en termes de modes de paiement, et les modes de paiement sont plus courants que dans d'autres pays. Business Messages vous permet d'intégrer plusieurs processeurs de paiement pour vous offrir un confort d'utilisation optimal.

Lorsqu'un utilisateur effectue un paiement, vous devez l'informer que vous avez reçu son paiement. La plupart des sociétés de traitement des paiements incluent un rappel de réussite ou d'échec qui envoie une requête HTTP à l'URL de votre choix une fois le processus de paiement terminé.

Objectifs de l'atelier

Dans la section précédente de la série d'ateliers de programmation, vous avez développé l'agent Bonjour Meal pour présenter un catalogue d'articles, créé un panier permettant aux utilisateurs d'ajouter et de supprimer des articles, et calculé le prix total de l'achat. Panier Dans cette section, vous allez enrichir l'agent afin qu'il puisse traiter les paiements en fonction du contenu du panier.

Dans cet atelier de programmation, votre application

  • Intégrer à la passerelle de paiement Stripe
  • Permettre à un utilisateur d'effectuer le paiement en fonction du prix du panier
  • Renvoyez une notification sur la conversation pour informer l'utilisateur de l'état du paiement

Ba08a4d2f8c09c0e.png

Objectifs de l'atelier

  • Intégrer à la société de traitement des paiements Stripe
  • Envoyez une demande à Stripe pour lancer une session de paiement.
  • Gérez les paiements et les échecs de réponse de Stripe.

Prérequis

  • Projet GCP enregistré et approuvé pour l'utilisation avec Business Messages
  • Consultez notre site pour les développeurs afin de découvrir comment
  • Un appareil Android 5 ou version ultérieure OU un appareil iOS disposant de l'application Google Maps.
  • Expérience dans la programmation d'applications Web
  • Une connexion Internet

2. Ajouter des dépendances

Mise à jour du fichier requirements.txt

Comme nous allons l'intégrer à la société de traitement des paiements Stripe, nous pouvons utiliser la bibliothèque cliente Python Stripe. Ajoutez stripe au fichier requirements.txt sans version pour obtenir la dernière version de la dépendance.

Cela est nécessaire pour que le module Python de Google Cloud App Engine intègre le module Python à rayures.

requirements.txt

...
stripe
...

Préparer bopis/views.py

En haut du fichier bopis/views.py, importez render à partir de django.shortcuts et JsonResponse à partir de django.http. De plus, nous devons importer stripe pour prendre en charge les appels vers la bibliothèque cliente Python de Stripe.

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

3. Travailler avec Stripe

Créer un compte sur Stripe.com

Dans cet atelier de programmation, nous venons d'utiliser Stripe, mais vous pouvez intégrer n'importe quel processeur compatible avec l'intégration Web. Créez-en un sur stripe.com. Il nous permettra de tester et d'intégrer directement les outils de traitement des paiements tiers.

6731d123c56feb67.png

Une fois que vous avez créé un compte et que vous vous êtes connecté, un tableau de bord de ce type doit s'afficher.

6d9d165d2d1fbb8c.png

Assurez-vous que vous êtes en mode test, puis cliquez sur le bouton Developers (Développeurs) comme indiqué dans la capture d'écran ci-dessus pour rechercher vos clés API. Vous devriez voir deux ensembles de clés API: une clé publié et une clé secrète. Vous avez besoin de ces deux clés pour faciliter vos transactions de paiement avec Stripe.

Mettre à jour bopis/views.py

Votre application a besoin des deux ensembles de clés. Vous devez donc les mettre à jour dans le fichier views.py.

Vous pouvez définir la clé secrète directement dans la propriété "bande.api_key" et lui attribuer la valeur de la clé secrète indiquée dans le tableau de bord du développeur Stripe. Créez ensuite une variable globale appelée STRIPE_PUBLIC_KEY et définissez-la sur la clé publiable.

En outre, Stripe doit rediriger vers une page Web que vous gérez. Nous allons donc créer une autre variable globale pour inclure le domaine accessible au public de votre application.

À l'issue de ces modifications, vous obtiendrez le résultat suivant:

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

Voilà comment réaliser la configuration de Stripe.

4. Fonctionnalité de règlement

Mettre à jour la fonction de prix total du panier

Actuellement, la fonction send_shopping_cart_total_price n'envoie qu'un message spécifiant le prix du panier. Ajoutons une action suggérée pour ouvrir une URL vers la page de paiement.

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)

Lorsque l'utilisateur appuie sur cette action suggérée, il est redirigé vers une page Web affichant son prix total et un bouton permettant d'effectuer un paiement avec Stripe.

Créons une page Web simple qui permettra de gérer ce flux.

Dans le code source du projet, recherchez le répertoire nommé bopis. Créez un répertoire appelé templates, puis, dans les modèles, créez un répertoire nommé bopis. Il s'agit d'un modèle de conception Django pour spécifier le nom de l'application dans le répertoire des modèles. Il permet de moins bien confondre les modèles entre les applications Django.

Vous devez maintenant avoir un répertoire avec un chemin d'accès à bopis/templates/bopis/. Vous pouvez créer des fichiers HTML dans ce répertoire pour diffuser des pages Web. C'est le cas des modèles affichés dans le navigateur. Commençons par checkout.html.

Dans ce répertoire, créez checkout.html. L'extrait de code suivant affiche un bouton de règlement et le prix du panier. Il inclut également JavaScript pour lancer le règlement 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>

Nous avons besoin d'un itinéraire vers cette page Web lorsque l'URL est demandée. La valeur OpenUrlAction est définie sur {YOUR_DOMAIN}/checkout/{conversation_id} pour le processus de règlement. Le résultat est le suivant : https://<GCP-Project-ID>.appspot.com/checkout/abc123-cba321-abc123-cba321. Avant de créer cet itinéraire, examinons le code JavaScript qui figure dans le modèle 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>
...

Examinons ensemble l'extrait de code ci-dessus.

  1. Tout d'abord, une entité Stripe est créée avec la clé publique, qui est transmise via le contexte de la fonction d'affichage, un autre paradigme Django.
  2. L'extrait doit ensuite rechercher un élément sur la page ayant l'ID checkout-button.
  3. Un écouteur d'événements est ajouté à cet élément.

Cet écouteur d'événements se déclenche lorsqu'un utilisateur clique ou appuie sur ce bouton, qui envoie une requête POST au serveur Web que vous spécifiez via l'URL: {YOUR_DOMAIN}/create-checkout-session/{conversation_id}.

La logique du serveur Web est indiquée dans les extraits ci-dessous. Lorsque l'utilisateur appuie sur le bouton avec l'ID "checkout-button", on s'attend à ce qu'un ID de session Stripe soit créé à l'aide de l'API Stripe spécifiant le prix du panier.

Si votre serveur parvient à produire un ID de session valide, la logique d'application redirige l'utilisateur vers une page Stripe Checkout. Dans le cas contraire, un message JavaScript standard est renvoyé pour signaler à l'utilisateur un problème.

Commençons par ajouter de nouveaux chemins d'accès au tableau de formats d'URL pour prendre en charge la page de paiement et générer l'ID de session. Ajoutez les éléments suivants au tableau d'URL dans url.py.

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

Nous allons ensuite créer les fonctions de vue dans views.py pour renvoyer le modèle checkout.html et générer la session Stripe Checkout.

...

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)

...

Ces deux fonctions utilisent l'ID de conversation pour associer le panier à l'utilisateur, puis pour déterminer le prix que Stripe doit appliquer à l'utilisateur.

Ces deux méthodes constituent la première moitié du parcours de paiement. Si vous déployez cette solution et testez l'expérience, un formulaire de paiement Stripe s'affiche. Vous pouvez l'utiliser pour effectuer un paiement à l'aide d'une carte de crédit test, comme indiqué dans la documentation destinée aux développeurs Stripe pour tester le règlement Visa.

La seconde moitié consiste à faire revenir l'utilisateur dans la conversation après réception de la réponse de Stripe concernant le paiement de l'utilisateur.

5. Réponses par bandes

Lorsqu'un utilisateur s'engage dans votre processus de paiement, il a réussi ou échoué. Dans la fonction create_checkout_session, nous avons défini success_url et cancel_url. Stripe redirigera vers l'une de ces deux URL en fonction de l'état du paiement. Nous allons définir ces deux routes dans urls.py, puis ajouter deux fonctions d'affichage à bopis/views.py afin de prendre en charge ces deux flux possibles.

Ajoutez ces lignes au fichier urls.py.

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

Les vues correspondantes se présenteront comme suit:

...

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

...

La bande est redirigée vers le domaine comme vous l'avez spécifié dans la constante DOMAIN, ce qui signifie que vous devez afficher une réponse HTML à l'aide d'un modèle, sinon le site Web semblera très simple. Nous allons créer deux fichiers HTML simples dans le répertoire bopis/templates/bopis/ avec 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>

Avec ces deux modèles, un utilisateur qui finalise le processus de paiement avec votre intégration Stripe est redirigé vers les URL appropriées et reçoit les modèles respectifs. Ils recevront également un message via Business Messages leur permettant de revenir à la conversation.

6. Recevez des paiements !

Félicitations ! Vous avez intégré une société de traitement des paiements dans votre agent Business Messages.

Dans cette série, vous avez déployé une application Web dans Google Cloud App Engine, défini votre webhook dans la Business Communications Developer Console, l'avez étendu pour permettre la recherche de l'inventaire via une base de données statique et créé un panier à l'aide de Google Datastore. Dans la dernière partie de la série, vous avez intégré à Stripe, une société de traitement des paiements compatible avec les intégrations Web et cette expérience. Vous pouvez désormais interagir avec d'autres sociétés de traitement des paiements et plus encore.

D6d80cf9c9fc621.png 44db8d6441dce4c5.png

Et ensuite ?

Lorsque vous êtes prêt, passez en revue certaines des rubriques suivantes pour en savoir plus sur les interactions plus complexes que vous pouvez accomplir avec Business Messages:

Documents de référence