קנייה באיסוף עצמי בחנות: ארוחת בונז' – חלק 3 – שילוב עם מעבד תשלומים

1. מבוא

53003251caaf2be5.png 8826bd8cb0c0f1c7.png

תאריך עדכון אחרון: 13 בספטמבר 2021

אוספים תשלומים!

גביית התשלומים מ-Business Messages פותחת לכם עולם חדש של הזדמנויות עסקיות במסגרת פלטפורמת השיחות. נניח שלקוח פוטנציאלי שולח לכם שאלה לגבי המוצר שעליו הוא מעוניין לקבל מידע נוסף. אחרי שהתשובות לשאלות של הלקוחות נענו, תוכלו לסגור את העסקה איתם על ידי ציון שער תשלום ישירות בשיחה.

fe0c6754fb69d708.png

איך יוצרים חוויית תשלום טובה?

חוויית תשלום טובה היא חוויה שבה המשתמשים יכולים לשלם כרגיל.

למשתמשים יש העדפות שונות לגבי אופן התשלום שלהם, ואמצעי תשלום נפוצים יותר מאחרים באזורים שונים בעולם. שירות Business Messages מאפשר לשלב את השימוש ביותר ממעבד תשלומים אחד כדי לקבל את רמת הנוחות הגבוהה ביותר למשתמשים.

אחרי שהמשתמש משלים את תהליך התשלום, אתם רוצים ליידע אותו שקיבלתם את התשלום בהצלחה. רוב עיבודי התשלומים כוללים התקשרות חזרה בהצלחה או נכשלה, ששולחת בקשת HTTP לכתובת URL לבחירתך, לאחר סיום תהליך התשלום.

מה תפתחו

בקטע הקודם של סדרת ה-Codelab, הרחבתם את נציג הארוחה של בונז'ור כדי להציג קטלוג פריטים, יצרתם עגלת קניות שמאפשרת למשתמשים להוסיף ולהסיר פריטים ולחשב את המחיר הכולל של עגלת הקניות. בקטע הזה, תרחיבו עוד יותר את הנציג כדי שהוא יוכל לעבד תשלומים על סמך התוכן שבעגלת הקניות.

ב-Codelab הזה, האפליקציה:

  • שילוב עם שער התשלום Stripe
  • המשתמשים יכולים להשלים את תהליך התשלום על סמך מחיר עגלת הקניות
  • שליחת התראה בחזרה לאזור השיחה כדי לעדכן את המשתמש במצב התשלום

ba08a4d2f8c09c0e.png

מה צריך לעשות

  • שילוב עם מעבד התשלומים Stripe.
  • יש לשלוח בקשה ל-Stripe כדי להתחיל סשן תשלום.
  • טיפול בניסיונות הצלחה או כשלים ב-Stripe.

מה צריך

  • פרויקט ב-GCP שרשום ומאושר לשימוש ב-Business Messages
  • אפשר לעיין באתר למפתחים כדי לקבל הוראות לביצוע
  • מכשיר Android בגרסה 5 ואילך או מכשיר iOS עם אפליקציית מפות Google
  • ניסיון בתכנות של אפליקציות אינטרנט
  • חיבור לאינטרנט!

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

מוודאים שאתם פועלים ב-"מצב בדיקה&quot, ולוחצים על הלחצן מפתחים כפי שמתואר בצילום המסך שלמעלה כדי לחפש את מפתחות ה-API. אמורות להיות מוצגות שתי קבוצות של מפתחות API: מפתח שניתן לפרסם ומפתח סודי. שני המפתחות נדרשים כדי לאפשר עסקאות תשלום באמצעות Stripe.

עדכון bopis/views.py

האפליקציה שלך צריכה את שתי קבוצות המפתחות, אז צריך לעדכן אותן ב-view.py.

אפשר להגדיר את המפתח הסודי ישירות בנכסtripe.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'

וזה כל מה שצריך לעשות כדי להגדיר את הפס.

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. ניתן ליצור ספרייה חדשה בתוך קובצי bopi בשם templates, ובתוך תבניות ליצור ספרייה נוספת בשם bopis. זהו תבנית עיצוב של Django כדי לציין את שם האפליקציה בספריית התבניות. הוא עוזר להפחית בלבול של תבניות בין אפליקציות של Django.

אמורה להיות לך ספרייה עם נתיב ב-bopis/templates/bopis/. אפשר ליצור קובצי HTML בספרייה הזו כדי להציג דפי אינטרנט. Dג'נגו מתייחס לתבניות שמעובדות בדפדפן. בואו נתחיל ב-checkout.html.

בספרייה הזו יוצרים: checkout.html. קטע הקוד הבא מציג לחצן תשלום ואת מחיר עגלת הקניות. הוא כולל גם JavaScript להפעלת קופה עם פס.

{% 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. תחילה היא יוצרת ישות פסים עם המפתח הציבורי שמועבר בהקשר של פונקציית התצוגה, פרדיגמה אחרת של ג'אנגו.
  2. לאחר מכן, קטע הקוד מחפש רכיב בדף עם המזהה checkout-button.
  3. לרכיב ההאזנה הזה מתווסף אירוע.

הכלי להאזנה על אירועים יופעל כשמשתמש לוחץ או מקיש על הלחצן הזה, שמפעיל בקשת POST בשרת האינטרנט שציינת דרך כתובת ה-URL: {YOUR_DOMAIN}/create-checkout-session/{conversation_id}.

ניתן לראות את הלוגיקה של שרת האינטרנט בקטעים שבהמשך. כשהמשתמש מקיש על הלחצן עם המזהה "checkout-button" אנחנו מצפים שהוא יחזיר מזהה סשן Stripe שנוצר באמצעות Stripe API, המציין את מחיר העגלה.

אם השרת שלך הצליח ליצור מזהה הפעלה חוקי, לוגיקת האפליקציה תפנה את המשתמש לדף Stripe Checkout, אחרת הוא ישלח למשתמש הודעת JavaScript רגילה על כך שמשהו השתבש.

נתחיל בהוספת נתיב חדש למערך ה-URLs כדי לתמוך בדף התשלום וליצור את מזהה הסשן. צריך להוסיף את הפרטים הבאים למערך urlpatterns ב-urls.py.

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

לאחר מכן, יש ליצור את פונקציות התצוגה ב-view.py כדי להחזיר את תבנית checkout.html וליצור את סשן התשלום בפסים.

... 

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)

...

שתי הפונקציות עושות שימוש ב-call_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')

...

פסים מפנות בחזרה לדומיין כפי שציינת בקבוע של 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 המתאימות ויוצגו בו התבניות המתאימות. בנוסף, הם יקבלו הודעה דרך Business Messages שתאפשר להם לחזור לשיחה.

6. לקבל תשלומים!

מזל טוב, שילבת בהצלחה מעבד תשלומים בסוכן Business Messages!

בסדרה הזו, הטמעתם אפליקציית אינטרנט ב-Google Cloud App Engine, הגדרתם את התגובה לפעולה מאתר אחר (webhook) ב-Business Communications Developer Console, הרחיבו את האפליקציה לתמיכה בחיפוש מלאי דרך מסד נתונים סטטי ויצרתם עגלת קניות באמצעות Google Datastore. בחלק האחרון של הסדרה, ביצעתם שילוב עם Stripe, חברה לעיבוד תשלומים שתומכת בשילובים באינטרנט ובחוויה הזו. עכשיו תוכלו לבצע שילובים עם חברות נוספות לעיבוד תשלומים ועוד!

d6d80cf9c9fc621.png 44db8d6441dce4c5.png

מה עושים עכשיו?

כשתהיו מוכנים, תוכלו לבדוק בכמה מהנושאים הבאים לגבי אינטראקציות מורכבות יותר שניתן להשיג ב-Business Messages:

מסמכי עזר