การให้ข้อมูลการจัดส่งและข้อมูลติดต่อจากแอปการชำระเงินของ Android

วิธีอัปเดตแอปการชำระเงินบน Android เพื่อระบุที่อยู่สำหรับจัดส่งและข้อมูลติดต่อของผู้ชำระเงินกับ Web Payments API

Sahel Sharify
Sahel Sharify

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

นั่นคือเหตุผลที่ Payment Request API รองรับฟีเจอร์เพื่อขอที่อยู่สำหรับจัดส่งและข้อมูลติดต่อ ซึ่งมีประโยชน์หลายประการดังนี้

  • ผู้ใช้สามารถเลือกที่อยู่ที่ถูกต้องได้ด้วยการแตะเพียงไม่กี่ครั้ง
  • และจะแสดงผลที่อยู่ในรูปแบบมาตรฐานเสมอ
  • การส่งที่อยู่ที่ไม่ถูกต้องมีโอกาสน้อย

เบราว์เซอร์สามารถเลื่อนการเก็บข้อมูลที่อยู่สำหรับจัดส่งและข้อมูลติดต่อไปยังแอปการชำระเงิน เพื่อมอบประสบการณ์การชำระเงินที่เป็นหนึ่งเดียว ฟังก์ชันนี้เรียกว่าการมอบสิทธิ์

Chrome จะมอบสิทธิ์การเก็บรวบรวมที่อยู่สำหรับจัดส่งและข้อมูลติดต่อของลูกค้าไปยังแอปการชำระเงิน Android ที่เรียกใช้ทุกครั้งที่เป็นไปได้ โดยการมอบสิทธิ์นี้จะช่วยลดความยุ่งยากในระหว่างการชำระเงิน

เว็บไซต์ผู้ขายสามารถอัปเดตตัวเลือกการจัดส่งและราคารวมแบบไดนามิก โดยขึ้นอยู่กับตัวเลือกของลูกค้าสำหรับที่อยู่สำหรับจัดส่งและตัวเลือกการจัดส่ง

การเปลี่ยนแปลงตัวเลือกการจัดส่งและที่อยู่สำหรับจัดส่ง ดูว่าราคาส่งผลต่อตัวเลือกการจัดส่งและราคารวมแบบไดนามิกอย่างไร

หากต้องการเพิ่มการสนับสนุนการมอบสิทธิ์ในแอปการชำระเงิน Android ที่มีอยู่แล้ว ให้ทำตามขั้นตอนต่อไปนี้

  1. ประกาศการมอบสิทธิ์ที่รองรับ
  2. แยกวิเคราะห์ Intent เพิ่มเติม PAY รายการสำหรับตัวเลือกการชำระเงินที่จำเป็น
  3. ให้ข้อมูลที่จำเป็นในการตอบกลับการชำระเงิน
  4. [ไม่บังคับ] รองรับขั้นตอนแบบไดนามิก
    1. แจ้งให้ผู้ขายทราบเกี่ยวกับการเปลี่ยนแปลงวิธีการชำระเงิน ที่อยู่จัดส่ง หรือตัวเลือกการจัดส่งที่ผู้ใช้เลือก
    2. รับรายละเอียดการชำระเงินที่อัปเดตจากผู้ขาย (เช่น จำนวนเงินทั้งหมดที่ปรับตามค่าใช้จ่ายของตัวเลือกการจัดส่งที่เลือก)

ประกาศการมอบสิทธิ์ที่รองรับ

เบราว์เซอร์จำเป็นต้องทราบรายการข้อมูลเพิ่มเติมที่แอปการชำระเงินให้มาเพื่อให้มอบสิทธิ์การเก็บรวบรวมข้อมูลดังกล่าวให้กับแอปของคุณได้ ประกาศการมอบสิทธิ์ที่รองรับเป็น <meta-data> ใน AndroidManifest.xml ของแอป

<activity
  android:name=".PaymentActivity"
  …
  <meta-data
    android:name="org.chromium.payment_supported_delegations"
    android:resource="@array/supported_delegations" />
</activity>

<resource> ต้องเป็นรายการสตริงที่เลือกจากค่าที่ถูกต้องต่อไปนี้

[ "payerName", "payerEmail", "payerPhone", "shippingAddress" ]

ตัวอย่างต่อไปนี้ระบุได้เฉพาะที่อยู่สำหรับจัดส่งและอีเมลของผู้ชำระเงิน

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <string-array name="supported_delegations">
    <item>payerEmail</item>
    <item>shippingAddress</item>
  </string-array>
</resources>

แยกวิเคราะห์ Intent เพิ่มเติมของ PAY สำหรับตัวเลือกการชำระเงินที่จำเป็น

ผู้ขายสามารถระบุข้อมูลที่จำเป็นเพิ่มเติมโดยใช้พจนานุกรม paymentOptions Chrome จะแสดงรายการตัวเลือกที่จำเป็นซึ่งแอปมีให้โดยการส่งพารามิเตอร์ต่อไปนี้ไปยังกิจกรรม PAY เป็น Intent เพิ่มเติม

paymentOptions

paymentOptions เป็นชุดย่อยของตัวเลือกการชำระเงินที่ผู้ขายระบุ ซึ่งแอปของคุณได้ประกาศว่ามีการสนับสนุนการมอบสิทธิ์

val paymentOptions: Bundle? = extras.getBundle("paymentOptions")
val requestPayerName: Boolean? = paymentOptions?.getBoolean("requestPayerName")
val requestPayerPhone: Boolean? = paymentOptions?.getBoolean("requestPayerPhone")
val requestPayerEmail: Boolean? = paymentOptions?.getBoolean("requestPayerEmail")
val requestShipping: Boolean? = paymentOptions?.getBoolean("requestShipping")
val shippingType: String? = paymentOptions?.getString("shippingType")

โดยอาจมีพารามิเตอร์ต่อไปนี้

  • requestPayerName - บูลีนที่ระบุว่าจำเป็นต้องระบุชื่อผู้ชำระเงินหรือไม่
  • requestPayerPhone - บูลีนที่ระบุว่าต้องใช้โทรศัพท์ของผู้ชำระเงินหรือไม่
  • requestPayerEmail - บูลีนที่ระบุว่าต้องใช้อีเมลของผู้ชำระเงินหรือไม่
  • requestShipping - บูลีนที่ระบุว่าจำเป็นต้องมีข้อมูลการจัดส่งหรือไม่
  • shippingType - สตริงที่แสดงประเภทการจัดส่ง ประเภทการจัดส่งอาจเป็น "shipping", "delivery" หรือ "pickup" แอปของคุณสามารถใช้คำแนะนำนี้ใน UI เมื่อขอที่อยู่ของผู้ใช้หรือตัวเลือกการจัดส่ง

shippingOptions

shippingOptions คืออาร์เรย์แบบพัสดุที่ผู้ขายระบุ พารามิเตอร์นี้จะมีอยู่ก็ต่อเมื่อ paymentOptions.requestShipping == true เท่านั้น

val shippingOptions: List<ShippingOption>? =
    extras.getParcelableArray("shippingOptions")?.mapNotNull {
        p -> from(p as Bundle)
    }

ตัวเลือกการจัดส่งแต่ละรายการจะเป็น Bundle ซึ่งมีคีย์ดังต่อไปนี้

  • id - ตัวระบุตัวเลือกการจัดส่ง
  • label - ป้ายกำกับตัวเลือกการจัดส่งที่แสดงต่อผู้ใช้
  • amount - กลุ่มค่าจัดส่งที่มีคีย์ currency และ value พร้อมค่าสตริง
  • selected - ควรเลือกตัวเลือกการจัดส่งหรือไม่เมื่อแอปการชำระเงินแสดงตัวเลือกการจัดส่ง

คีย์ทั้งหมดที่ไม่ใช่ selected มีค่าสตริง selected มีค่าบูลีน

val id: String = bundle.getString("id")
val label: String = bundle.getString("label")
val amount: Bundle = bundle.getBundle("amount")
val selected: Boolean = bundle.getBoolean("selected", false)

ระบุข้อมูลที่จำเป็นในการตอบกลับการชำระเงิน

แอปของคุณควรมีข้อมูลเพิ่มเติมที่จําเป็นในการตอบสนองต่อกิจกรรม PAY

ในการดำเนินการดังกล่าว คุณต้องระบุพารามิเตอร์ต่อไปนี้เป็นส่วนเสริมของ Intent

  • payerName - ชื่อและนามสกุลของผู้ชำระเงิน สตริงนี้ควรเป็นสตริงที่ไม่ว่างเมื่อ paymentOptions.requestPayerName เป็นจริง
  • payerPhone - หมายเลขโทรศัพท์ของผู้ชำระเงิน สตริงนี้ควรเป็นสตริงที่ไม่ว่างเมื่อ paymentOptions.requestPayerPhone เป็นจริง
  • payerEmail - อีเมลของผู้ชำระเงิน ควรเป็นสตริงที่ไม่ว่างเมื่อ paymentOptions.requestPayerEmail เป็นจริง
  • shippingAddress - ที่อยู่สำหรับจัดส่งที่ได้จากผู้ใช้ ซึ่งควรเป็นแพ็กเกจที่ไม่ว่างเปล่าเมื่อ paymentOptions.requestShipping เป็นจริง แพ็กเกจควรมีคีย์ต่อไปนี้ซึ่งแสดงถึงส่วนต่างๆ ในที่อยู่จริง
    • city
    • countryCode
    • dependentLocality
    • organization
    • phone
    • postalCode
    • recipient
    • region
    • sortingCode
    • addressLine คีย์ทั้งหมดที่ไม่ใช่ addressLine มีค่าสตริง addressLine คืออาร์เรย์ของสตริง
  • shippingOptionId - ตัวระบุของตัวเลือกการจัดส่งที่ผู้ใช้เลือก ซึ่งควรเป็นสตริงที่ไม่ว่างเมื่อ paymentOptions.requestShipping เป็นจริง

ตรวจสอบการตอบกลับการชำระเงิน

หากผลของกิจกรรมการตอบสนองการชำระเงินที่ได้รับจากแอปการชำระเงินที่เรียกใช้มีการตั้งค่าเป็น RESULT_OK Chrome จะตรวจสอบข้อมูลเพิ่มเติมที่จำเป็นในส่วนเพิ่มเติม หากตรวจสอบไม่สำเร็จ Chrome จะส่งคืนคำสัญญาที่ถูกปฏิเสธจาก request.show() พร้อมข้อความแสดงข้อผิดพลาดเกี่ยวกับนักพัฒนาซอฟต์แวร์ต่อไปนี้

'Payment app returned invalid response. Missing field "payerEmail".'
'Payment app returned invalid response. Missing field "payerName".'
'Payment app returned invalid response. Missing field "payerPhone".'
'Payment app returned invalid shipping address in response.'
'... is not a valid CLDR country code, should be 2 upper case letters [A-Z]'
'Payment app returned invalid response. Missing field "shipping option".'

ตัวอย่างโค้ดต่อไปนี้เป็นตัวอย่างของการตอบกลับที่ถูกต้อง

fun Intent.populateRequestedPaymentOptions() {
    if (requestPayerName) {
        putExtra("payerName", "John Smith")
    }
    if (requestPayerPhone) {
        putExtra("payerPhone", "4169158200")
    }
    if (requestPayerEmail) {
        putExtra("payerEmail", "john.smith@gmail.com")
    }
    if(requestShipping) {
        val address: Bundle = Bundle()
        address.putString("countryCode", "CA")
        val addressLines: Array<String> =
                arrayOf<String>("111 Richmond st. West")
        address.putStringArray("addressLines", addressLines)
        address.putString("region", "Ontario")
        address.putString("city", "Toronto")
        address.putString("postalCode", "M5H2G4")
        address.putString("recipient", "John Smith")
        address.putString("phone", "4169158200")
        putExtra("shippingAddress", address)
        putExtra("shippingOptionId", "standard")
    }
}

ไม่บังคับ: รองรับขั้นตอนแบบไดนามิก

บางครั้งค่าใช้จ่ายรวมของธุรกรรมอาจเพิ่มขึ้น เช่น เมื่อผู้ใช้เลือกตัวเลือกการจัดส่งด่วน หรือเมื่อรายการตัวเลือกการจัดส่งที่ใช้ได้หรือราคามีการเปลี่ยนแปลงเมื่อผู้ใช้เลือกที่อยู่สำหรับจัดส่งระหว่างประเทศ เมื่อแอปให้ที่อยู่สำหรับจัดส่งหรือตัวเลือกที่ผู้ใช้เลือก แอปควรแจ้งผู้ขายเกี่ยวกับการเปลี่ยนแปลงที่อยู่สำหรับจัดส่งหรือตัวเลือก และแสดงรายละเอียดการชำระเงินที่อัปเดตแก่ผู้ใช้ (ผู้ขายให้ไว้) ได้

AIDL

หากต้องการแจ้งผู้ขายเกี่ยวกับการเปลี่ยนแปลงใหม่ ให้ใช้บริการ PaymentDetailsUpdateService ที่ประกาศใน AndroidManifest.xml ของ Chrome หากต้องการใช้บริการนี้ ให้สร้างไฟล์ AIDL 2 ไฟล์ที่มีเนื้อหาต่อไปนี้

app/src/main/aidl/org/chromium/components/payments/IPaymentDetailsUpdateService

package org.chromium.components.payments;
import android.os.Bundle;

interface IPaymentDetailsUpdateServiceCallback {
    oneway void updateWith(in Bundle updatedPaymentDetails);

    oneway void paymentDetailsNotUpdated();
}

app/src/main/aidl/org/chromium/components/payments/IPaymentDetailsUpdateServiceCallback

package org.chromium.components.payments;
import android.os.Bundle;
import org.chromium.components.payments.IPaymentDetailsUpdateServiceCallback;

interface IPaymentDetailsUpdateService {
    oneway void changePaymentMethod(in Bundle paymentHandlerMethodData,
            IPaymentDetailsUpdateServiceCallback callback);

    oneway void changeShippingOption(in String shippingOptionId,
            IPaymentDetailsUpdateServiceCallback callback);

    oneway void changeShippingAddress(in Bundle shippingAddress,
            IPaymentDetailsUpdateServiceCallback callback);
}

แจ้งผู้ขายเกี่ยวกับการเปลี่ยนแปลงวิธีการชำระเงิน ที่อยู่สำหรับจัดส่ง หรือตัวเลือกการจัดส่งที่ผู้ใช้เลือก

private fun bind() {
    // The action is introduced in Chrome version 92, which supports the service in Chrome
    // and other browsers (e.g., WebLayer).
    val newIntent = Intent("org.chromium.intent.action.UPDATE_PAYMENT_DETAILS")
        .setPackage(callingBrowserPackage)
    if (packageManager.resolveService(newIntent, PackageManager.GET_RESOLVED_FILTER) == null) {
        // Fallback to Chrome-only approach.
        newIntent.setClassName(
            callingBrowserPackage,
            "org.chromium.components.payments.PaymentDetailsUpdateService")
        newIntent.action = IPaymentDetailsUpdateService::class.java.name
    }
    isBound = bindService(newIntent, connection, Context.BIND_AUTO_CREATE)
}

private val connection = object : ServiceConnection {
    override fun onServiceConnected(className: ComponentName, service: IBinder) {
        val service = IPaymentDetailsUpdateService.Stub.asInterface(service)
        try {
            if (isOptionChange) {
                service?.changeShippingOption(selectedOptionId, callback)
            } else (isAddressChange) {
                service?.changeShippingAddress(selectedAddress, callback)
            } else {
                service?.changePaymentMethod(methodData, callback)
            }
        } catch (e: RemoteException) {
            // Handle the remote exception
        }
    }
}

callingPackageName ที่ใช้สำหรับ Intent เริ่มต้นของบริการอาจมีค่าใดค่าหนึ่งต่อไปนี้ ทั้งนี้ขึ้นอยู่กับเบราว์เซอร์ที่ส่งคำขอการชำระเงิน

ช่องของ Chrome ชื่อแพ็กเกจ
คงที่ "com.android.chrome"
เบต้า "com.chrome.beta"
กำลังพัฒนา "com.chrome.dev"
คะแนรี "com.chrome.canary"
Chromium "org.chromium.chrome"
ช่อง Google Quick Search (การฝัง WebLayer) "com.google.android.googlequicksearchbox"

changePaymentMethod

แจ้งผู้ขายเกี่ยวกับการเปลี่ยนแปลงในวิธีการชำระเงินที่ผู้ใช้เลือก กลุ่ม paymentHandlerMethodData มี methodName และคีย์ details ที่ไม่บังคับทั้งคู่ที่มีค่าสตริง Chrome จะตรวจหาแพ็กเกจที่ไม่ว่างเปล่าซึ่งมี methodName ที่ไม่ว่างเปล่า และส่ง updatePaymentDetails ที่มีข้อความแสดงข้อผิดพลาดอย่างใดอย่างหนึ่งต่อไปนี้ผ่านทาง callback.updateWith หากตรวจสอบไม่สำเร็จ

'Method data required.'
'Method name required.'

changeShippingOption

แจ้งผู้ขายเกี่ยวกับการเปลี่ยนแปลงในตัวเลือกการจัดส่งที่ผู้ใช้เลือก shippingOptionId ควรเป็นตัวระบุของตัวเลือกการจัดส่งที่ผู้ขายระบุ Chrome จะตรวจหา shippingOptionId ที่ไม่ว่างเปล่า และส่ง updatePaymentDetails ที่มีข้อความแสดงข้อผิดพลาดต่อไปนี้ผ่านทาง callback.updateWith หากตรวจสอบไม่สำเร็จ

'Shipping option identifier required.'

changeShippingAddress

แจ้งผู้ขายเกี่ยวกับการเปลี่ยนแปลงในที่อยู่สำหรับจัดส่งที่ได้จากผู้ใช้ Chrome จะตรวจหาแพ็กเกจ shippingAddress ที่ไม่ว่างเปล่าซึ่งมี countryCode ที่ถูกต้อง และส่ง updatePaymentDetails ที่มีข้อความแสดงข้อผิดพลาดต่อไปนี้ผ่าน callback.updateWith หากตรวจสอบไม่สำเร็จ

'Payment app returned invalid shipping address in response.'

ข้อความแสดงข้อผิดพลาดเกี่ยวกับสถานะไม่ถูกต้อง

หาก Chrome พบสถานะที่ไม่ถูกต้องเมื่อได้รับคำขอเปลี่ยนแปลงใดๆ ระบบจะเรียกใช้ callback.updateWith พร้อมแพ็กเกจ updatePaymentDetails ที่ปกปิด แพ็กเกจจะมีเฉพาะคีย์ error ที่มี "Invalid state" เท่านั้น ตัวอย่างของสถานะที่ไม่ถูกต้องมีดังนี้

  • ในกรณีที่ Chrome ยังคงรอการตอบกลับจากผู้ขายต่อการเปลี่ยนแปลงก่อนหน้า (เช่น เหตุการณ์การเปลี่ยนแปลงที่ดำเนินอยู่)
  • ตัวระบุตัวเลือกการจัดส่งที่แอปชำระเงินไม่ได้อยู่ในตัวเลือกการจัดส่งที่ผู้ขายกำหนด

รับรายละเอียดการชำระเงินที่อัปเดตจากผู้ขาย

private fun unbind() {
    if (isBound) {
        unbindService(connection)
        isBound = false
    }
}

private val callback: IPaymentDetailsUpdateServiceCallback =
    object : IPaymentDetailsUpdateServiceCallback.Stub() {
        override fun paymentDetailsNotUpdated() {
            // Payment request details have not changed.
            unbind()
        }

        override fun updateWith(updatedPaymentDetails: Bundle) {
            newPaymentDetails = updatedPaymentDetails
            unbind()
        }
    }

updatePaymentDetails เป็นแพ็กเกจที่เทียบเท่ากับพจนานุกรม PaymentRequestDetailsUpdate WebIDL (หลังจากปกปิดช่อง modifiers) และมีคีย์ที่ไม่บังคับต่อไปนี้

  • total - แพ็กเกจที่มีคีย์ currency และ value โดยทั้ง 2 คีย์มีค่าสตริง
  • shippingOptions - อาร์เรย์แบบพาร์เซลของตัวเลือกการจัดส่ง
  • error - สตริงที่มีข้อความแสดงข้อผิดพลาดทั่วไป (เช่น เมื่อ changeShippingOption ไม่มีตัวระบุตัวเลือกการจัดส่งที่ถูกต้อง)
  • stringifiedPaymentMethodErrors - สตริง JSON ที่แสดง ข้อผิดพลาดในการตรวจสอบความถูกต้องสำหรับวิธีการชำระเงิน
  • addressErrors - แพ็กเกจที่มีคีย์ที่ไม่บังคับซึ่งเหมือนกับที่อยู่การจัดส่งและค่าของสตริง แต่ละคีย์แสดงถึงข้อผิดพลาดในการตรวจสอบความถูกต้องในส่วนที่เกี่ยวข้องของที่อยู่สำหรับจัดส่ง

คีย์ที่ไม่มีการเปลี่ยนแปลงหมายความว่าค่าของคีย์ดังกล่าวไม่มีการเปลี่ยนแปลง