ซื้อออนไลน์เพื่อมารับที่ร้าน: อาหาร Bonjour - ตอนที่ 3 - การผสานรวมกับผู้ประมวลผลการชําระเงิน

1. บทนำ

53003251caaf2be5.png 8826bd8cb0c0f1c7.png

อัปเดตล่าสุด: 13-09-2021

การรับการชําระเงิน

การรวบรวมการชําระเงินใน Business Messages ช่วยให้คุณเข้าถึงโอกาสใหม่ๆ ทางธุรกิจได้จากภายในแพลตฟอร์มการสนทนา สมมติว่าผู้มีโอกาสเป็นลูกค้าส่งคําถามเกี่ยวกับผลิตภัณฑ์ที่พวกเขาต้องการดูข้อมูลเพิ่มเติมให้คุณ เมื่อนักเรียนตอบคําถามแล้ว คุณสามารถปิดดีลได้โดยระบุเกตเวย์การชําระเงินในการสนทนาโดยตรง

fe0c6754fb69d708.png

ประสบการณ์การชําระเงินที่ดีมีอะไรบ้าง

ประสบการณ์การชําระเงินที่ดีคือประสบการณ์ที่ผู้ใช้ชําระเงินได้ตามต้องการ

ผู้ใช้ให้ความสําคัญกับวิธีการชําระเงินของตน รวมทั้งวิธีการชําระเงินอื่นๆ ที่พบได้บ่อยมากกว่าในส่วนอื่นๆ ของโลก Business Messages ช่วยให้คุณทํางานร่วมกับผู้ประมวลผลการชําระเงินได้มากกว่า 1 รายเพื่อให้ความสะดวกสูงสุดแก่ผู้ใช้

เมื่อผู้ใช้ทําตามขั้นตอนการชําระเงินแล้ว คุณต้องการแจ้งให้ผู้ใช้ทราบว่าได้รับการชําระเงินเรียบร้อยแล้ว ตัวประมวลผลการชําระเงินส่วนใหญ่จะมีการเรียกกลับที่สําเร็จหรือล้มเหลว ซึ่งจะส่งคําขอ HTTP ไปยัง URL ที่คุณเลือกเมื่อการชําระเงินเสร็จสมบูรณ์

สิ่งที่คุณจะสร้าง

ในส่วนก่อนหน้าของชุดโปรแกรม Codelab เราได้ขยาย Agent ของ Bonjour Meal เพื่อแสดงแคตตาล็อกสินค้า สร้างรถเข็นช็อปปิ้งที่อนุญาตให้ผู้ใช้เพิ่มและนําสินค้าออก รวมถึงคํานวณราคารวมของรถเข็นช็อปปิ้ง ในส่วนนี้ คุณจะได้ขยายเวลาให้กับตัวแทนมากขึ้นไปอีกเพื่อให้ประมวลผลการชําระเงินตามเนื้อหาของรถเข็นช็อปปิ้งได้

ใน Codelab นี้ แอปของคุณจะ

  • ผสานรวมกับเกตเวย์การชําระเงิน Stripe
  • อนุญาตให้ผู้ใช้ทําตามขั้นตอนการชําระเงินตามราคารถเข็น
  • ส่งการแจ้งเตือนกลับไปยังแพลตฟอร์มการสนทนาเพื่อแจ้งผู้ใช้เกี่ยวกับสถานะการชําระเงิน

ba08a4d2f8c09c0e.png

สิ่งที่คุณจะทํา

  • ผสานรวมกับผู้ประมวลผลการชําระเงิน Stripe
  • ส่งคําขอไปยัง Stripe เพื่อเริ่มเซสชันการชําระเงิน
  • จัดการการตอบสนองการชําระเงินสําเร็จหรือล้มเหลวจาก Stripe

สิ่งที่ต้องมี#39

  • โปรเจ็กต์ GCP ที่ลงทะเบียนและได้รับอนุมัติให้ใช้กับ Business Messages แล้ว
  • ดูวิธีการได้ที่เว็บไซต์ของนักพัฒนาซอฟต์แวร์ของเรา
  • อุปกรณ์ Android เวอร์ชัน 5 ขึ้นไปหรืออุปกรณ์ iOS ที่มีแอป Google Maps
  • มีประสบการณ์ในการเขียนโปรแกรมเว็บแอปพลิเคชัน
  • การเชื่อมต่ออินเทอร์เน็ต

2. การเพิ่มทรัพยากร Dependency

กําลังอัปเดตข้อกําหนด.txt

เนื่องจากเราจะผสานรวมกับผู้ประมวลผลการชําระเงินของ Stripe จึงใช้ไลบรารีของไคลเอ็นต์ Stripe Python ได้ เพิ่ม stripe ไปยังข้อกําหนดใน ads.txt โดยไม่มีเวอร์ชันที่ใช้ทรัพยากร Dependency ล่าสุด

การดําเนินการนี้จําเป็นสําหรับรันไทม์ของ 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

สร้างบัญชีที่ Stripe.com

เราเพิ่งนํา Stripe มาใช้ใน Codelab นี้ แต่คุณสามารถผสานรวมกับโปรเซสเซอร์ใดก็ได้ที่รองรับการผสานรวมเว็บ สร้างบัญชีใน stripe.com เราจะใช้โปรไฟล์นี้ในการทดสอบและวัตถุประสงค์เพื่อการศึกษาเพื่อดูวิธีผสานรวมกับผู้ประมวลผลการชําระเงินบุคคลที่สามโดยตรง

6731d123c56feb67.png

เมื่อคุณสร้างบัญชีและลงชื่อเข้าสู่ระบบแล้ว คุณจะเห็นแดชบอร์ดที่มีลักษณะดังนี้

6d9d165d2d1fbb8c.png

ตรวจสอบว่าคุณกําลังทํางานใน "โหมดทดสอบ&quot แล้วคลิกปุ่มนักพัฒนาซอฟต์แวร์ตามที่ระบุไว้ในภาพหน้าจอด้านบนเพื่อค้นหาคีย์ API คุณจะเห็นคีย์ API 2 ชุด ได้แก่ คีย์ที่เผยแพร่ได้และคีย์ลับ คุณจะต้องใช้ทั้ง 2 คีย์นี้เพื่ออํานวยความสะดวกในการทําธุรกรรมการชําระเงินด้วย Stripe

อัปเดต bopis/views.py

แอปพลิเคชันต้องใช้ชุดคีย์ทั้ง 2 ชุด ดังนั้นโปรดอัปเดตคีย์ใน views.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'

และทั้งหมดที่ต้องทําสําหรับการตั้งค่า 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 ในซอร์สโค้ดของโปรเจ็กต์ สร้างไดเรกทอรีใหม่ภายใน bopi ที่ชื่อว่า 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. ระบบจะเพิ่ม Listener เหตุการณ์ลงในองค์ประกอบนั้น

ระบบจะทริกเกอร์ Listener เหตุการณ์นี้เมื่อผู้ใช้คลิกหรือแตะปุ่มนี้ ซึ่งจะเริ่มต้นคําขอ POST ไปยังเว็บเซิร์ฟเวอร์ที่คุณระบุผ่าน URL: {YOUR_DOMAIN}/create-checkout-session/{conversation_id}.

คุณสามารถดูตรรกะของเว็บเซิร์ฟเวอร์ได้ในข้อมูลโค้ดด้านล่าง เมื่อผู้ใช้แตะปุ่มที่มีรหัส "checkout-button" เราจะสามารถคาดหวังได้ว่าจะแสดงรหัสเซสชันของ Stripe ซึ่งสร้างขึ้นโดยใช้ Stripe API ที่ระบุราคาสินค้าในรถเข็น

หากเซิร์ฟเวอร์สร้างรหัสเซสชันที่ถูกต้องได้ ตรรกะแอปพลิเคชันจะเปลี่ยนเส้นทางผู้ใช้ไปยังหน้า Stripe Checkout มิเช่นนั้น จะแจ้งเตือนผู้ใช้ด้วยข้อความ JavaScript มาตรฐานว่าเกิดข้อผิดพลาด

มาเพิ่มเส้นทางใหม่ไปยังอาร์เรย์รูปแบบ URL เพื่อรองรับหน้าชําระเงินและสร้างรหัสเซสชันกัน เพิ่มสิ่งต่อไปนี้ลงในอาร์เรย์ 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)

...

ฟังก์ชันทั้งสองนี้ใช้Conversation_id เพื่อเชื่อมโยงรถเข็นช็อปปิ้งกับผู้ใช้ จากนั้นกําหนดราคา Stripe ควรเรียกเก็บเงินจากผู้ใช้

ทั้ง 2 วิธีนี้รวมกันเป็นครึ่งแรกของขั้นตอนการชําระเงิน หากคุณนําการทดสอบนี้ไปใช้และทดสอบประสบการณ์ คุณจะเห็นแบบฟอร์มชําระเงิน Stripe ซึ่งคุณทําการชําระเงินด้วยบัตรเครดิตทดสอบได้ตามที่แนะนําในเอกสารประกอบสําหรับนักพัฒนาซอฟต์แวร์ Stripe สําหรับการทดสอบการชําระเงินผ่าน Visa

ครึ่งหนึ่งของขั้นตอนคือวิธีที่เรานําผู้ใช้กลับมาที่การสนทนาเมื่อเราได้รับการตอบกลับจาก Stripe เกี่ยวกับการชําระเงินของผู้ใช้

5. คําตอบเป็นแถบ

เมื่อผู้ใช้มีส่วนร่วมในขั้นตอนการชําระเงิน อาจชําระเงินสําเร็จหรือไม่สําเร็จ เรากําหนด success_url และ cancel_url ในฟังก์ชัน create_checkout_session แถบจะเปลี่ยนเส้นทางไปยัง URL 1 ใน 2 รายการนี้ โดยขึ้นอยู่กับสถานะการชําระเงิน มากําหนดเส้นทางทั้งสองนี้ใน urls.py แล้วเพิ่มฟังก์ชันการดู 2 รายการลงใน 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 แบบง่าย 2 ไฟล์ในไดเรกทอรี bopis/templates/bopis/ พร้อมกับ checkout.html

เทมเพลต mm-django-echo-bot/bopis/ template/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/ เทมเพลต/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>

เมื่อใช้เทมเพลต 2 รายการนี้ ระบบจะเปลี่ยนเส้นทางผู้ใช้ที่ดําเนินการชําระเงินให้เสร็จสมบูรณ์ด้วยการผสานรวม Stripe ไปยัง URL ที่เหมาะสมและแสดงเทมเพลตที่เกี่ยวข้อง และจะได้รับข้อความผ่าน Business Messages ด้วย ซึ่งช่วยให้กลับมาที่การสนทนาได้

6. รับการชําระเงิน

ยินดีด้วย คุณผสานรวมผู้ประมวลผลการชําระเงินกับตัวแทน Business Messages เรียบร้อยแล้ว

ในชุดนี้ คุณทําให้เว็บแอปพลิเคชันใช้งานได้ใน Google Cloud App Engine, ตั้งค่าเว็บฮุคบนคอนโซลของนักพัฒนาซอฟต์แวร์ Business Communications, ขยายแอปพลิเคชันเพื่อสนับสนุนการค้นหาพื้นที่โฆษณาผ่านฐานข้อมูลแบบคงที่ และสร้างรถเข็นช็อปปิ้งโดยใช้ Google Datastore ในส่วนสุดท้ายของชุด คุณได้ผสานรวมกับ Stripe ซึ่งเป็นผู้ประมวลผลการชําระเงินที่รองรับการผสานรวมเว็บและประสบการณ์การใช้งานนี้ ตอนนี้คุณมีส่วนร่วมในการทํางานร่วมกับผู้ประมวลผลการชําระเงินรายอื่นและอีกมากมายได้แล้ว

d6d80cf9c9fc621.png 44db8d6441dce4c5.png

ขั้นต่อไปคืออะไร

เมื่อคุณพร้อมแล้ว ให้ชําระเงินเกี่ยวกับหัวข้อต่อไปนี้เพื่อเรียนรู้เกี่ยวกับการโต้ตอบที่ซับซ้อนขึ้นซึ่งคุณได้รับจาก Business Messages

เอกสารอ้างอิง