Beli Online Ambil di Toko: Bonjour Meal - Bagian 2 - Membuat Keranjang Belanja

1. Pengantar

53003251caaf2be5.png 8826bd8cb0c0f1c7.png

Terakhir Diperbarui: 30-10-2020

Membuat Keranjang Belanja di Business Messages.

Ini adalah codelab kedua dalam seri yang bertujuan membangun perjalanan pengguna mengenai Beli Online Ambil di Toko. Berbicara mengenai e-commerce, keranjang belanja merupakan kunci keberhasilan dalam mengonversi pengguna menjadi pelanggan yang membayar. Keranjang belanja juga dapat digunakan untuk lebih memahami pelanggan Anda dan merupakan medium untuk menawarkan saran mengenai item lain yang mungkin menarik bagi mereka. Dalam codelab ini, kita akan fokus membahas pembuatan pengalaman keranjang belanja dan deployment aplikasi ke Google App Engine.

Apa yang membuat keranjang belanja yang bagus?

Keranjang belanja merupakan kunci pengalaman belanja online yang sukses. Pada faktanya, Business Messages tidak hanya bagus dalam memfasilitasi Tanya Jawab mengenai sebuah produk dengan calon pelanggan, tetapi juga dapat memfasilitasi seluruh pengalaman belanja hingga menyelesaikan pembayaran dalam percakapan.

9d17537b980d0e62.png

Selain keranjang belanja yang bagus, pengalaman berbelanja yang baik memungkinkan pengguna untuk menjelajahi item berdasarkan kategori dan memungkinkan bisnis merekomendasikan produk lain yang mungkin diminati oleh pembeli. Setelah menambahkan item lainnya ke keranjang belanja, pengguna dapat meninjau seluruh keranjang, dan dapat menghapus atau menambahkan item lainnya sebelum melakukan check out.

Yang akan Anda buat

Di bagian seri codelab ini, Anda akan memperluas agen digital yang dibuat di bagian 1 untuk perusahaan fiktif, yaitu Bonjour Meal. Dengan begitu, pengguna dapat menjelajahi katalog item dan menambahkan item ke keranjang belanja.

Dalam codelab ini, aplikasi Anda akan

  • Menampilkan katalog pertanyaan dalam Business Messages
  • Menyarankan item yang mungkin diminati pengguna
  • Meninjau konten keranjang belanja dan membuat ringkasan harga total

ab2fb6a4ed33a129.png

Yang akan Anda pelajari

  • Cara men-deploy aplikasi web di App Engine pada Google Cloud Platform
  • Cara menggunakan mekanisme penyimpanan persisten untuk menyimpan status keranjang belanja

Codelab ini berfokus untuk memperluas agen digital dari bagian 1 seri codelab ini.

Yang Anda butuhkan

  • Project GCP yang telah terdaftar dan disetujui untuk digunakan dengan Business Messages
  • Lihat situs developer kami untuk mendapatkan petunjuk terkait cara
  • File kredensial JSON akun layanan yang dibuat untuk Project GCP Anda
  • Perangkat Android 5+ ATAU perangkat iOS dengan aplikasi Google Maps
  • Pengalaman dengan pemrograman aplikasi web
  • Koneksi internet.

2. Mempersiapkan

Codelab ini mengasumsikan bahwa Anda telah membuat agen pertama dan menyelesaikan bagian 1 codelab. Oleh karena itu, kita tidak akan membahas dasar-dasar untuk mengaktifkan Business Messages dan Business Communications API, membuat kunci akun layanan, men-deploy aplikasi, atau menyiapkan webhook di Business Communications Console. Oleh karena itu, kami akan meng-clone aplikasi sampel untuk memastikan aplikasi Anda konsisten dengan apa yang sedang kami kembangkan, dan kami akan mengaktifkan API untuk Datastore di Google Cloud Platform agar dapat mempertahankan data yang berkaitan dengan keranjang belanja.

Meng-clone aplikasi dari GitHub

Di terminal, clone Django Echo Bot Sample ke direktori kerja project dengan perintah berikut:

$ git clone https://github.com/google-business-communications/bm-bonjour-meal-django-starter-code

Salin file kredensial JSON yang dibuat untuk akun layanan ke dalam folder referensi sampel, lalu ganti nama kredensial menjadi "bm-agent-service-account-credentials.json".

bm-bonjour-meal-django-starter-code/bonjourmeal-codelab/step-2/resources/bm-agent-service-account-credentials.json

Mengaktifkan Google Datastore API

Di Bagian 1 codelab ini, Anda telah mengaktifkan Business Messages API, BusinessCommunication API, dan Cloud Build API.

Untuk codelab ini, karena kita akan melakukan aktivitas dengan Google Datastore, maka API berikut juga perlu diaktifkan:

  1. Buka Google Datastore API di Google Cloud Console.
  2. Pastikan Anda mengerjakan project GCP yang benar.
  3. Klik Aktifkan.

Men-deploy aplikasi sampel

Di terminal, buka direktori langkah 2 sampel.

Jalankan perintah berikut di terminal untuk men-deploy sampel:

$ gcloud config set project PROJECT_ID*
$ gcloud app deploy
  • PROJECT_ID merupakan project ID untuk project yang Anda gunakan untuk mendaftar ke API.

Perhatikan URL aplikasi yang di-deploy dalam output perintah terakhir:

Deployed service [default] to [https://PROJECT_ID.appspot.com]

Kode yang baru saja Anda deploy berisi aplikasi web dengan webhook untuk menerima pesan dari Business Messages. Ini berisi semua hal yang telah kita lakukan dari bagian 1 codelab. Jika Anda belum melakukannya, harap konfigurasikan webhook Anda.

Aplikasi ini akan merespons beberapa pertanyaan sederhana seperti pengguna yang menanyakan jam buka Bonjour Meal. Anda harus mengujinya pada perangkat seluler melalui URL pengujian yang dapat Anda ambil dari Informasi Agen dalam Business Communications Console. URL pengujian akan meluncurkan pengalaman Business Messages di perangkat seluler dan Anda dapat mulai berinteraksi dengan agen di sana.

3. Katalog produk

Sistem inventaris

Pada umumnya, Anda dapat melakukan integrasi langsung dengan inventaris merek melalui API internal. Pada contoh lain, Anda dapat menyalin halaman web atau membuat sistem pelacakan inventaris sendiri. Fokus kami bukan untuk membangun sistem inventaris, melainkan menggunakan file statis sederhana yang berisi gambar dan informasi produk untuk agen. Di bagian ini, kita akan menarik informasi dari file statis ini, menampilkan informasi tersebut ke dalam percakapan, dan mengizinkan pengguna menjelajahi item yang tersedia untuk ditambahkan ke keranjang belanja.

File inventaris statis akan terlihat seperti ini:

bonjourmeal-codelab/step-2/resources/inventory.json

{

    "food": [
        {
            "id":0,
            "name": "Ham and cheese sandwich",
            "price": "6.99",
            "image_url": "https://storage.googleapis.com/bonjour-rail.appspot.com/ham-and-cheese.png",
            "remaining": 8
        },
        {
            "id":1,
            "name": "Chicken veggie wrap",
            "price": "9.99",
            "image_url": "https://storage.googleapis.com/bonjour-rail.appspot.com/chicken-veggie-wrap.png",
            "remaining": 2
        },
        {
            "id":2,
            "name": "Assorted cheese plate",
            "price": "7.99",
            "image_url": "https://storage.googleapis.com/bonjour-rail.appspot.com/assorted-cheese-plate.png",
            "remaining": 6
        },
        {
            "id":3,
            "name": "Apple walnut salad",
            "price": "12.99",
            "image_url": "https://storage.googleapis.com/bonjour-rail.appspot.com/apple-walnut-salad.png",
            "remaining": 1
        }
    ]
}

Mari kita buat aplikasi Python untuk membaca file ini.

Membaca dari inventaris kami

Inventaris merupakan file statis yang disebut "inventory.json" yang dapat ditemukan di direktori ./resources. Kita perlu menambahkan beberapa logika Python ke views.py untuk membaca isi file JSON, lalu menampilkannya ke percakapan. Mari kita buat fungsi yang membaca data dari file JSON dan menampilkan daftar produk yang tersedia.

Definisi fungsi ini dapat ditempatkan di mana pun di views.py.

bonjourmeal-codelab/step-2/bopis/views.py

...
def get_inventory_data():
        f = open(INVENTORY_FILE)
        inventory = json.load(f)
        return inventory
...

Tindakan ini akan memberi kita hal yang diperlukan untuk membaca data dari inventaris. Sekarang kita memerlukan cara untuk memunculkan informasi produk ini ke dalam percakapan.

Memunculkan katalog produk

Untuk memudahkan codelab ini, kami memiliki katalog produk umum untuk menampilkan semua item inventaris ke percakapan Business Messages melalui satu carousel kartu informasi.

Untuk melihat katalog produk, kami akan membuat balasan yang disarankan dengan teks "Tampilkan Menu" dan "show-product-catalog" postbackData. Ketika pengguna mengetuk balasan yang disarankan dan aplikasi web Anda menerima data postback, kami akan mengirimkan carousel kartu informasi. Mari kita tambahkan konstanta baru untuk balasan yang disarankan ini di bagian atas views.py.

bonjourmeal-codelab/step-2/bopis/views.py

...
CMD_SHOW_PRODUCT_CATALOG = 'show-product-catalog'
...

Dari sini, kita mengurai pesan dan mengarahkannya ke fungsi baru yang mengirimkan carousel kartu informasi yang berisi katalog produk. Pertama, perluas fungsi route_message untuk memanggil fungsi "send_product_catalog" saat balasan yang disarankan diketuk, lalu tetapkan fungsi tersebut.

Dalam cuplikan berikut, tambahkan kondisi tambahan ke pernyataan if di fungsi route_message untuk memeriksa apakah normalized_message sama dengan konstanta yang ditentukan sebelumnya, yaitu CMD_SHOW_PRODUCT_CATALOG.

bonjourmeal-codelab/step-2/bopis/views.py

...
def route_message(message, conversation_id):
    '''
    Routes the message received from the user to create a response.

    Args:
        message (str): The message text received from the user.
        conversation_id (str): The unique id for this user and agent.
    '''
    normalized_message = message.lower()

    if normalized_message == CMD_RICH_CARD:
        send_rich_card(conversation_id)
    elif normalized_message == CMD_CAROUSEL_CARD:
        send_carousel(conversation_id)
    elif normalized_message == CMD_SUGGESTIONS:
        send_message_with_suggestions(conversation_id)
    elif normalized_message == CMD_BUSINESS_HOURS_INQUIRY:
        send_message_with_business_hours(conversation_id)
    elif normalized_message == CMD_ONLINE_SHOPPING_INQUIRY:
        send_online_shopping_info_message(conversation_id)
    elif normalized_message == CMD_SHOW_PRODUCT_CATALOG:
        send_product_catalog(conversation_id)
    else:
        echo_message(message, conversation_id)
...

Kemudian, pastikan alurnya selesai dan tentukan send_product_catalog. send_product_catalog memanggil get_menu_carousel, yang menghasilkan carousel kartu informasi dari file inventaris yang kita baca sebelumnya.

Definisi fungsi dapat ditempatkan di mana pun di views.py. Perlu diketahui bahwa cuplikan berikut menggunakan dua konstanta baru yang harus ditambahkan ke bagian atas file.

bonjourmeal-codelab/step-2/bopis/views.py

...

CMD_ADD_ITEM = 'add-item'
CMD_SHOW_CART = 'show-cart'

...

def get_menu_carousel():
    """Creates a sample carousel rich card.

    Returns:
       A :obj: A BusinessMessagesCarouselCard object with three cards.
    """

    inventory = get_inventory_data()

    card_content = []

    for item in inventory['food']:
        card_content.append(BusinessMessagesCardContent(
            title=item['name'],
            description=item['price'],
            suggestions=[
                BusinessMessagesSuggestion(
                    reply=BusinessMessagesSuggestedReply(
                        text='Add item',
                        postbackData='{'+f'"action":"{CMD_ADD_ITEM}","item_name":"{item["id"]}"'+'}'))

                ],
            media=BusinessMessagesMedia(
                height=BusinessMessagesMedia.HeightValueValuesEnum.MEDIUM,
                contentInfo=BusinessMessagesContentInfo(
                    fileUrl=item['image_url'],
                    forceRefresh=False))))

    return BusinessMessagesCarouselCard(
        cardContents=card_content,
        cardWidth=BusinessMessagesCarouselCard.CardWidthValueValuesEnum.MEDIUM)

def send_product_catalog(conversation_id):
    """Sends the product catalog to the conversation_id.

    Args:
        conversation_id (str): The unique id for this user and agent.
    """
    rich_card = BusinessMessagesRichCard(carouselCard=get_menu_carousel())

    fallback_text = ''

    # Construct a fallback text for devices that do not support carousels
    for card_content in rich_card.carouselCard.cardContents:
        fallback_text += (card_content.title + '\n\n' + card_content.description
                          + '\n\n' + card_content.media.contentInfo.fileUrl
                          + '\n---------------------------------------------\n\n')

    message_obj = BusinessMessagesMessage(
        messageId=str(uuid.uuid4().int),
        representative=BOT_REPRESENTATIVE,
        richCard=rich_card,
        fallback=fallback_text,
        suggestions=[
        BusinessMessagesSuggestion(
            reply=BusinessMessagesSuggestedReply(
                text='See my cart',
                postbackData=CMD_SHOW_CART)
            ),
        BusinessMessagesSuggestion(
            reply=BusinessMessagesSuggestedReply(
                text='See the menu',
                postbackData=CMD_SHOW_PRODUCT_CATALOG)
            ),
        ]
        )

    send_message(message_obj, conversation_id)
...

Jika Anda memeriksa pembuatan item carousel, kami juga membuat instance class BusinessMessagesSuggestion. Setiap saran mewakili pilihan pengguna untuk sebuah produk di carousel. Saat pengguna mengetuk balasan yang disarankan, Business Messages akan mengirimkan postbackData yang berisi JSON, yang mendeskripsikan item dan tindakan yang ingin dilakukan pengguna (menambahkan atau menghapus dari keranjang) ke webhook Anda. Di bagian berikut, kita akan mengurai pesan yang terlihat seperti ini agar dapat menambahkan item ke keranjang.

Setelah melakukan perubahan ini, mari kita deploy aplikasi web ke Google App Engine dan cobalah pengalaman tersebut.

$ gcloud app deploy

Saat Anda memiliki platform percakapan yang dimuat di perangkat seluler, kirim pesan "show-product-catalog" dan Anda akan melihat carousel produk yang terlihat seperti ini.

4639da46bcc5230c.png

Jika Anda mengetuk Tambahkan item, agen akan menggemakan data postback dari balasan yang disarankan. Di bagian berikutnya, kita akan menggunakan katalog produk dan menggunakannya untuk membuat keranjang belanja tempat item akan ditambahkan.

Katalog produk yang baru saja Anda buat dapat diperluas dengan berbagai cara. Anda mungkin memiliki opsi menu minuman yang berbeda atau opsi vegetarian. Menggunakan carousel atau chip saran adalah cara yang bagus agar pengguna dapat melihat perincian opsi menu untuk mendapatkan serangkaian produk yang mereka cari. Sebagai ekstensi untuk codelab ini, coba perluas sistem katalog produk agar pengguna dapat melihat minuman secara terpisah dari makanan di menu, atau bahkan dapat menentukan opsi vegetarian.

4. Keranjang belanja

Di bagian codelab ini, kita akan membuat fungsi keranjang belanja dari bagian sebelumnya yang memungkinkan kita untuk menjelajahi produk yang tersedia.

Di antara banyak hal, pengalaman keranjang belanja yang utama memungkinkan pengguna menambahkan item ke keranjang, menghapus item dari keranjang, melacak jumlah setiap item dalam keranjang, dan meninjau item di keranjang.

Dengan melacak status keranjang belanja, artinya kita harus menyimpan data di aplikasi web. Untuk memudahkan eksperimen dan deployment, kami akan menggunakan Google Datastore di Google Cloud Platform untuk mempertahankan data. ID Percakapan tetap konstan antara pengguna dan bisnis, sehingga kita dapat menggunakannya untuk mengaitkan pengguna dengan item keranjang belanja.

Mari kita mulai dengan terhubung dengan Google Datastore dan mempertahankan ID percakapan saat kita melihatnya.

Menghubungkan dengan Datastore

Kami akan menghubungkan dengan Google Datastore setiap kali ada interaksi yang dijalankan di keranjang belanja, misalnya, saat pengguna menambahkan atau menghapus item. Anda dapat mempelajari lebih lanjut cara menggunakan library klien ini untuk berinteraksi dengan Google Datastore di dokumentasi resmi.

Cuplikan berikut menentukan fungsi untuk memperbarui keranjang belanja. Fungsi ini mengambil input berikut: conversation_id dan message. message berisi JSON yang menjelaskan tindakan yang ingin dilakukan pengguna, yang sudah disertakan dalam carousel Anda yang menampilkan katalog produk. Fungsi ini membuat klien Google Datastore dan segera mengambil entity ShoppingCart, dengan kuncinya adalah ID percakapan.

Salin fungsi berikut ke file views.py Anda. Kami akan terus memperluasnya di bagian berikutnya.

bonjourmeal-codelab/step-2/bopis/views.py

from google.oauth2 import service_account
from google.cloud import datastore

def update_shopping_cart(conversation_id, message):
        credentials = service_account.Credentials.from_service_account_file(
        SERVICE_ACCOUNT_LOCATION)

        client = datastore.Client(credentials=credentials)
        key = client.key('ShoppingCart', conversation_id)
        entity = datastore.Entity(key=key)
        result = client.get(key)
        
        # TODO: Add logic to add and remove items from cart
        
        entity.update(result)
        client.put(entity)

Mari kita perluas fungsi ini untuk menambahkan item ke keranjang.

Menambahkan item ke keranjang

Saat pengguna mengetuk tindakan yang disarankan Tambahkan item dari carousel produk, postbackData akan menampilkan JSON yang menjelaskan tindakan yang ingin dilakukan pengguna. Kamus JSON memiliki dua kunci, "action" dan "item_name", dan kamus JSON ini dikirimkan ke webhook Anda. Kolom "item_name" adalah ID unik yang terkait dengan item di inventory.json.

Setelah perintah keranjang dan item keranjang diuraikan dari pesan, selanjutnya kita dapat menulis pernyataan bersyarat untuk menambahkan item. Beberapa kasus yang perlu diperhatikan adalah jika Datastore belum pernah mendeteksi ID percakapan atau jika keranjang belanja menerima item ini untuk pertama kalinya. Berikut adalah ekstensi fungsi update_shopping_cart yang ditentukan di atas. Perubahan ini akan menambahkan item ke keranjang belanja yang dipertahankan oleh Google Datastore.

Cuplikan berikut adalah ekstensi fungsi sebelumnya yang ditambahkan ke views.py. Jangan ragu untuk menambahkan perbedaan, atau menyalin cuplikan dan mengganti versi fungsi update_shopping_cart yang ada.

bonjourmeal-codelab/step-2bopis/views.py

def update_shopping_cart(conversation_id, message):
    credentials = service_account.Credentials.from_service_account_file(
      SERVICE_ACCOUNT_LOCATION)
    inventory = get_inventory_data()

    cart_request = json.loads(message)
    cart_cmd = cart_request["action"]
    cart_item = cart_request["item_name"]

    item_name = inventory['food'][int(cart_item)]['name']

    client = datastore.Client(credentials=credentials)
    key = client.key('ShoppingCart', conversation_id)
    entity = datastore.Entity(key=key)
    result = client.get(key)

    if result is None:
        if cart_cmd == CMD_ADD_ITEM:
            entity.update({
                item_name: 1
            })

    else:
        if cart_cmd == CMD_ADD_ITEM:
            if result.get(item_name) is None:
                result[item_name] = 1
            else:
                result[item_name] = result[item_name] + 1

        entity.update(result)
    client.put(entity)

Fungsi ini akan diperluas nanti untuk mendukung skenario ketika cart_cmd berisi string 'del-item' yang ditentukan di CMD_DEL_ITEM.

Menggabungkan semuanya

Pastikan Anda menambahkan pipeline dalam fungsi route_message, sehingga jika Anda menerima pesan untuk menambahkan item ke keranjang, fungsi update_shopping_cart dipanggil. Anda juga harus menentukan konstanta untuk menambahkan item menggunakan konvensi yang kita gunakan di seluruh codelab.

bonjourmeal-codelab/step-2bopis/views.py

...

CMD_DEL_ITEM = 'del-item'

...

def route_message(message, conversation_id):
    '''
    Routes the message received from the user to create a response.

    Args:
        message (str): The message text received from the user.
        conversation_id (str): The unique id for this user and agent.
    '''
    normalized_message = message.lower()

    if normalized_message == CMD_RICH_CARD:
        send_rich_card(conversation_id)
    elif normalized_message == CMD_CAROUSEL_CARD:
        send_carousel(conversation_id)
    elif normalized_message == CMD_SUGGESTIONS:
        send_message_with_suggestions(conversation_id)
    elif normalized_message == CMD_BUSINESS_HOURS_INQUIRY:
        send_message_with_business_hours(conversation_id)
    elif normalized_message == CMD_ONLINE_SHOPPING_INQUIRY:
        send_online_shopping_info_message(conversation_id)
    elif normalized_message == CMD_SHOW_PRODUCT_CATEGORY:
        send_product_catalog(conversation_id)
    elif CMD_ADD_ITEM in normalized_message or CMD_DEL_ITEM in normalized_message:
       update_shopping_cart(conversation_id, message)
    else:
        echo_message(message, conversation_id)

...

Untuk saat ini, kami memiliki kemampuan untuk menambahkan item ke keranjang belanja. Jika men-deploy perubahan ke Google App Engine, Anda akan dapat melihat perubahan keranjang belanja yang tercermin di dasbor Google Datastore yang ditemukan di GCP console. Lihat screenshot di bawah konsol Google Datastore, ada satu entitas yang diberi nama berdasarkan ID Percakapan yang diikuti dengan beberapa hubungan dengan item inventaris dan jumlah item yang ada di keranjang belanja.

619dc18a8136ea69.png

Di bagian berikutnya, kita akan membuat cara untuk membuat daftar item di keranjang belanja. Mekanisme peninjauan keranjang belanja akan menampilkan semua item di keranjang, jumlah item tersebut, dan opsi untuk menghapus item dari keranjang.

Meninjau item di keranjang

Mencantumkan item di keranjang belanja adalah satu-satunya cara agar kami dapat memahami status keranjang belanja dan mengetahui item mana yang dapat kami hapus.

Pertama, mari kita kirim pesan yang ramah seperti "Ini keranjang belanja Anda:", diikuti pesan lain yang berisi carousel kartu informasi dengan balasan yang disarankan terkait, yaitu "Hapus satu" atau "Tambahkan satu". Carousel kartu informasi juga harus mencantumkan jumlah item yang disimpan di keranjang.

Hal yang perlu diketahui sebelum benar-benar masuk dan menulis fungsi: jika hanya ada satu jenis item di keranjang belanja, kita tidak dapat merendernya sebagai carousel. Carousel kartu informasi harus berisi minimal dua kartu. Di sisi lain, jika tidak ada item di keranjang, kami ingin menampilkan pesan sederhana yang menyatakan bahwa keranjang kosong.

Dengan mempertimbangkan hal tersebut, mari kita tentukan fungsi yang disebut send_shopping_cart. Fungsi ini terhubung dengan Google Datastore dan meminta entitas ShoppingCart berdasarkan ID Percakapan. Setelah itu, kita akan memanggil fungsi get_inventory_data dan menggunakan carousel kartu informasi untuk melaporkan status keranjang belanja. Kita juga perlu mendapatkan ID produk berdasarkan nama dan dapat mendeklarasikan fungsi untuk melihat ke Google Datastore guna menentukan nilai tersebut. Saat carousel dibuat, kita dapat mengaitkan balasan yang disarankan untuk menghapus item atau menambahkan item berdasarkan ID produk. Cuplikan di bawah ini menjalankan semua operasi ini. Salin kode di mana saja ke views.py.

bonjourmeal-codelab/step-2/bopis/views.py

...
def get_id_by_product_name(product_name):
  inventory = get_inventory_data()
  for item in inventory['food']:
    if item['name'] == product_name:
      return int(item['id'])
  return False


def send_shopping_cart(conversation_id):
  credentials = service_account.Credentials.from_service_account_file(
      SERVICE_ACCOUNT_LOCATION)

  # Retrieve the inventory data
  inventory = get_inventory_data()

  # Pull the data from Google Datastore
  client = datastore.Client(credentials=credentials)
  key = client.key('ShoppingCart', conversation_id)
  result = client.get(key)

  shopping_cart_suggestions = [
      BusinessMessagesSuggestion(
          reply=BusinessMessagesSuggestedReply(
              text='See total price', postbackData='show-cart-price')),
      BusinessMessagesSuggestion(
          reply=BusinessMessagesSuggestedReply(
              text='Empty the cart', postbackData='empty-cart')),
      BusinessMessagesSuggestion(
          reply=BusinessMessagesSuggestedReply(
              text='See the menu', postbackData=CMD_SHOW_PRODUCT_CATALOG)),
  ]

  if result is None or len(result.items()) == 0:
    message_obj = BusinessMessagesMessage(
        messageId=str(uuid.uuid4().int),
        representative=BOT_REPRESENTATIVE,
        text='There are no items in your shopping cart.',
        suggestions=shopping_cart_suggestions)

    send_message(message_obj, conversation_id)
  elif len(result.items()) == 1:

    for product_name, quantity in result.items():
      product_id = get_id_by_product_name(product_name)

      fallback_text = ('You have one type of item in the shopping cart')

      rich_card = BusinessMessagesRichCard(
          standaloneCard=BusinessMessagesStandaloneCard(
              cardContent=BusinessMessagesCardContent(
                  title=product_name,
                  description=f'{quantity} in cart.',
                  suggestions=[
                      BusinessMessagesSuggestion(
                          reply=BusinessMessagesSuggestedReply(
                              text='Remove one',
                              postbackData='{'+f'"action":"{CMD_DEL_ITEM}","item_name":"{product_id}"'+'}'))
                  ],
                  media=BusinessMessagesMedia(
                      height=BusinessMessagesMedia.HeightValueValuesEnum.MEDIUM,
                      contentInfo=BusinessMessagesContentInfo(
                          fileUrl=inventory['food'][product_id]
                          ['image_url'],
                          forceRefresh=False)))))

      message_obj = BusinessMessagesMessage(
          messageId=str(uuid.uuid4().int),
          representative=BOT_REPRESENTATIVE,
          richCard=rich_card,
          suggestions=shopping_cart_suggestions,
          fallback=fallback_text)

      send_message(message_obj, conversation_id)
  else:
    cart_carousel_items = []

    # Iterate through the cart and generate a carousel of items
    for product_name, quantity in result.items():
      product_id = get_id_by_product_name(product_name)

      cart_carousel_items.append(
          BusinessMessagesCardContent(
              title=product_name,
              description=f'{quantity} in cart.',
              suggestions=[
                  BusinessMessagesSuggestion(
                      reply=BusinessMessagesSuggestedReply(
                          text='Remove one',
                          postbackData='{'+f'"action":"{CMD_DEL_ITEM}","item_name":"{product_id}"'+'}'))
              ],
              media=BusinessMessagesMedia(
                  height=BusinessMessagesMedia.HeightValueValuesEnum.MEDIUM,
                  contentInfo=BusinessMessagesContentInfo(
                      fileUrl=inventory['food'][product_id]
                      ['image_url'],
                      forceRefresh=False))))

    rich_card = BusinessMessagesRichCard(
        carouselCard=BusinessMessagesCarouselCard(
            cardContents=cart_carousel_items,
            cardWidth=BusinessMessagesCarouselCard.CardWidthValueValuesEnum
            .MEDIUM))

    fallback_text = ''

    # Construct a fallback text for devices that do not support carousels
    for card_content in rich_card.carouselCard.cardContents:
      fallback_text += (
          card_content.title + '\n\n' + card_content.description + '\n\n' +
          card_content.media.contentInfo.fileUrl +
          '\n---------------------------------------------\n\n')

    message_obj = BusinessMessagesMessage(
        messageId=str(uuid.uuid4().int),
        representative=BOT_REPRESENTATIVE,
        richCard=rich_card,
        suggestions=shopping_cart_suggestions,
        fallback=fallback_text,
    )

    send_message(message_obj, conversation_id)

...

Pastikan Anda telah menentukan CMD_SHOW_CART di bagian atas views.py dan memanggil send_shopping_cart jika pengguna mengirim pesan yang berisi 'show-cart'.

bonjourmeal-codelab/step-2/bopis/views.py

...
def route_message(message, conversation_id):
    '''
    Routes the message received from the user to create a response.

    Args:
        message (str): The message text received from the user.
        conversation_id (str): The unique id for this user and agent.
    '''
    normalized_message = message.lower()

    if normalized_message == CMD_RICH_CARD:
        send_rich_card(conversation_id)
    elif normalized_message == CMD_CAROUSEL_CARD:
        send_carousel(conversation_id)
    elif normalized_message == CMD_SUGGESTIONS:
        send_message_with_suggestions(conversation_id)
    elif normalized_message == CMD_BUSINESS_HOURS_INQUIRY:
        send_message_with_business_hours(conversation_id)
    elif normalized_message == CMD_ONLINE_SHOPPING_INQUIRY:
        send_online_shopping_info_message(conversation_id)
    elif normalized_message == CMD_SHOW_PRODUCT_CATEGORY:
        send_product_catalog(conversation_id)
    elif CMD_ADD_ITEM in normalized_message or CMD_DEL_ITEM in normalized_message:
        update_shopping_cart(conversation_id, message)
    elif normalized_message == CMD_SHOW_CART:
        send_shopping_cart(conversation_id)
    else:
        echo_message(message, conversation_id)
...

34801776a97056ac.png

Berdasarkan logika yang kami masukkan dalam fungsi send_shopping_cart, saat Anda mengetik 'show-cart', kami akan menerima pesan yang menyatakan bahwa tidak ada item apa pun di keranjang, kartu informasi yang menampilkan satu item di keranjang, atau carousel kartu yang menampilkan beberapa item. Selain itu, kami memiliki tiga saran balasan: "Lihat harga total", "Kosongkan keranjang", dan "Lihat menu".

Coba terapkan perubahan kode di atas untuk menguji apakah keranjang belanja Anda melacak item yang Anda tambahkan. Anda juga dapat meninjau keranjang dari platform percakapan seperti yang ditunjukkan pada screenshot di atas. Anda dapat men-deploy perubahan dengan menjalankan perintah ini dari direktori langkah 2 tempat Anda menambahkan perubahan.

$ gcloud app deploy

Kita akan membuat fitur "Lihat harga total" di bagian berikutnya setelah membuat fungsi untuk menghapus item dari keranjang. Fungsi get_cart_price akan berperilaku mirip dengan fitur "Lihat keranjang belanja". Artinya, fungsi tersebut akan membandingkan data di Datastore dengan file inventory.json untuk menghasilkan harga total untuk keranjang belanja. Tindakan ini akan berguna untuk bagian codelab berikutnya yang terintegrasi dengan pembayaran.

Menghapus item dari keranjang

Terakhir, kita dapat menyelesaikan perilaku keranjang belanja dengan memperkenalkan fungsi untuk menghapus keranjang. Ganti fungsi update_shopping_cart yang ada dengan cuplikan berikut.

bonjourmeal-codelab/step-2/ bopis/views.py

def update_shopping_cart(conversation_id, message):
    credentials = service_account.Credentials.from_service_account_file(
      SERVICE_ACCOUNT_LOCATION)
    inventory = get_inventory_data()

    cart_request = json.loads(message)
    cart_cmd = cart_request["action"]
    cart_item = cart_request["item_name"]

    item_name = inventory['food'][int(cart_item)]['name']


    client = datastore.Client(credentials=credentials)
    key = client.key('ShoppingCart', conversation_id)
    entity = datastore.Entity(key=key)
    result = client.get(key)

    if result is None:
        if cart_cmd == CMD_ADD_ITEM:
            entity.update({
                item_name: 1
            })
        elif cart_cmd == CMD_DEL_ITEM:
            # The user is trying to delete an item from an empty cart. Pass and skip
            pass

    else:
        if cart_cmd == CMD_ADD_ITEM:
            if result.get(item_name) is None:
                result[item_name] = 1
            else:
                result[item_name] = result[item_name] + 1

        elif cart_cmd == CMD_DEL_ITEM:
            if result.get(item_name) is None:
                # The user is trying to remove an item that's no in the shopping cart. Pass and skip
                pass
            elif result[item_name] - 1 > 0:
                result[item_name] = result[item_name] - 1
            else:
                del result[item_name]

        entity.update(result)
    client.put(entity)

Mengirim pesan konfirmasi

Saat pengguna menambahkan item ke keranjang, Anda harus mengirim pesan konfirmasi yang berisi informasi bahwa Anda telah memproses permintaan mereka. Tindakan ini tidak hanya membantu menetapkan ekspektasi, tetapi juga menjaga interaksi tetap berlangsung.

Mari kita perluas fungsi update_shopping_cart, sehingga fungsi tersebut mengirim pesan ke ID percakapan yang menyatakan bahwa item telah ditambahkan atau dihapus, serta memberikan saran untuk meninjau keranjang belanja atau melihat menu lagi.

bonjourmeal-codelab/step-2/bopis/views.py

def update_shopping_cart(conversation_id, message):

     # No changes to the function, except appending the following logic
     ...
   
    if cart_cmd == CMD_ADD_ITEM:
        message = 'Great! You\'ve added an item to the cart.'
    else:
        message = 'You\'ve removed an item from the cart.'

    message_obj = BusinessMessagesMessage(
        messageId=str(uuid.uuid4().int),
        representative=BOT_REPRESENTATIVE,
        text=message,
        suggestions=[
            BusinessMessagesSuggestion(
            reply=BusinessMessagesSuggestedReply(
                text='Review shopping cart',
                postbackData=CMD_SHOW_CART)
            ),
            BusinessMessagesSuggestion(
            reply=BusinessMessagesSuggestedReply(
                text='See menu again',
                postbackData=CMD_SHOW_PRODUCT_CATALOG)
            ),
            ])
    send_message(message_obj, conversation_id)

905a1f3d89893ba0.png

Itu seharusnya sudah cukup. Pengalaman keranjang belanja berfitur lengkap yang memungkinkan pengguna menambahkan item, menghapus item, dan meninjau item di keranjang.

Pada tahap ini, jika Anda ingin melihat fungsi keranjang belanja di percakapan Business Messages, deploy aplikasi untuk berinteraksi dengan agen Anda. Anda dapat melakukannya dengan menjalankan perintah ini di direktori langkah 2.

$ gcloud app deploy

5. Menyiapkan pembayaran

Untuk berintegrasi dengan pemroses pembayaran di bagian selanjutnya dari rangkaian ini, kita memerlukan cara untuk mendapatkan harga keranjang belanja. Mari kita buat fungsi yang mengambil harga untuk kita dengan melakukan referensi silang terhadap data keranjang belanja di Google Datastore, mengambil harga setiap item dari inventaris, dan mengalikan harga dengan kuantitas setiap item di keranjang.

bonjourmeal-codelab/step-2/bopis/views.py

...
def get_cart_price(conversation_id):
    # Pull the data from Google Datastore
    credentials = service_account.Credentials.from_service_account_file(
    SERVICE_ACCOUNT_LOCATION)
    client = datastore.Client(credentials=credentials)
    key = client.key('ShoppingCart', conversation_id)
    entity = datastore.Entity(key=key)
    result = client.get(key)

    # Retrieve the inventory data
    inventory = get_inventory_data()
   
    # Start off with a total of 0 before adding up the total
    total_price = 0

    if len(result.items()) != 0:
      for product_name, quantity in result.items():
        total_price = total_price + float(
            inventory['food'][get_id_by_product_name(product_name)]['price']) * int(quantity)

    return total_price

...

Terakhir, kita dapat menggunakan fungsi tersebut dan mengirim pesan kepada pengguna.

bonjourmeal-codelab/step-2/bopis/views.py

...

def send_shopping_cart_total_price(conversation_id):
    cart_price = get_cart_price(conversation_id)

    message_obj = BusinessMessagesMessage(
        messageId=str(uuid.uuid4().int),
        representative=BOT_REPRESENTATIVE,
        suggestions=[],
        text=f'Your cart\'s total price is ${cart_price}.')

    send_message(message_obj, conversation_id)
...

Untuk menggabungkan semuanya, mari kita update fungsi route_message dan konstanta untuk memicu logika di atas.

bonjourmeal-codelab/step-2/bopis/views.py

...
CMD_GET_CART_PRICE = 'show-cart-price'
...
def route_message(message, conversation_id):
    '''
    Routes the message received from the user to create a response.

    Args:
        message (str): The message text received from the user.
        conversation_id (str): The unique id for this user and agent.
    '''
    normalized_message = message.lower()

    if normalized_message == CMD_RICH_CARD:
        send_rich_card(conversation_id)
    elif normalized_message == CMD_CAROUSEL_CARD:
        send_carousel(conversation_id)
    elif normalized_message == CMD_SUGGESTIONS:
        send_message_with_suggestions(conversation_id)
    elif normalized_message == CMD_BUSINESS_HOURS_INQUIRY:
        send_message_with_business_hours(conversation_id)
    elif normalized_message == CMD_ONLINE_SHOPPING_INQUIRY:
        send_online_shopping_info_message(conversation_id)
    elif normalized_message == CMD_SHOW_PRODUCT_CATEGORY:
        send_product_catalog(conversation_id)
    elif CMD_ADD_ITEM in normalized_message or CMD_DEL_ITEM in normalized_message:
        update_shopping_cart(conversation_id, message)
    elif normalized_message == CMD_SHOW_CART:
        send_shopping_cart(conversation_id)
    elif normalized_message == CMD_GET_CART_PRICE:
        send_shopping_cart_total_price(conversation_id)
    else:
        echo_message(message, conversation_id)
...

Berikut adalah beberapa screenshot yang menunjukkan pencapaian logika di atas:

8feacf94ed0ac6c4.png

Jika sudah siap untuk berintegrasi dengan pemroses pembayaran di bagian codelab berikutnya, kita akan memanggil fungsi get_cart_price untuk meneruskan data ke pemroses pembayaran dan memulai alur pembayaran.

Sekali lagi, Anda dapat mencoba fungsi keranjang belanja ini di percakapan Business Messages dengan men-deploy aplikasi dan berinteraksi dengan agen Anda.

$ gcloud app deploy

6. Selamat

Selamat, Anda berhasil membuat pengalaman keranjang belanja dalam Business Messages.

Hal yang tidak kita bahas di codelab ini adalah fitur untuk mengosongkan seluruh keranjang belanja. Jika Anda mau, coba perluas aplikasi untuk memenuhi fitur "Kosongkan keranjang". Solusi ini tersedia di langkah ke-3 dari kode sumber yang Anda clone.

Di bagian berikutnya, kami akan melakukan integrasi dengan pemroses pembayaran eksternal agar pengguna dapat menyelesaikan transaksi pembayaran dengan brand Anda.

Apa yang membuat keranjang belanja yang bagus?

Pengalaman keranjang belanja yang baik dalam sebuah percakapan tidak berbeda dengan aplikasi seluler atau di toko fisik. Dapat menambahkan item, menghapus item, dan menghitung harga keranjang hanyalah beberapa fitur yang kita pelajari di codelab ini. Hal yang berbeda dari keranjang belanja di dunia nyata adalah kemampuan untuk melihat harga semua item di keranjang pada waktu tertentu, saat Anda menambahkan item atau menghapus item. Jenis fitur bernilai tinggi ini akan membuat pengalaman percakapan e-commerce Anda menarik.

Apa selanjutnya?

Jika Anda sudah siap, lihat beberapa topik berikut untuk mempelajari interaksi lebih kompleks yang dapat Anda capai di Business Messages:

Dokumen referensi