การให้ข้อมูลการจัดส่งและข้อมูลติดต่อจากแอปการชำระเงินของ 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 ที่ใช้สำหรับความตั้งใจเริ่มต้นของบริการอาจมีค่าใดค่าหนึ่งต่อไปนี้ โดยขึ้นอยู่กับเบราว์เซอร์ที่เริ่มคำขอการชำระเงิน

ช่อง Chrome ชื่อแพ็กเกจ
คงที่ "com.android.chrome"
เบต้า "com.chrome.beta"
กำลังพัฒนา "com.chrome.dev"
คะแนรี "com.chrome.canary"
Chromium "org.chromium.chrome"
ช่องค้นหาด่วนของ Google (เครื่องมือฝัง 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 - อาร์เรย์ shipping options ที่แบ่งออกเป็นส่วนๆ ได้
  • error - สตริงที่มีข้อความแสดงข้อผิดพลาดทั่วไป (เช่น เมื่อ changeShippingOption ไม่ได้ระบุตัวระบุตัวเลือกการจัดส่งที่ถูกต้อง)
  • stringifiedPaymentMethodErrors - สตริง JSON ที่แสดงข้อผิดพลาดในการตรวจสอบสำหรับวิธีการชำระเงิน
  • addressErrors - กลุ่มที่มีคีย์ที่ไม่บังคับซึ่งเหมือนกับที่อยู่สำหรับจัดส่งและค่าสตริง คีย์แต่ละรายการแสดงข้อผิดพลาดในการตรวจสอบที่เกี่ยวข้องกับส่วนที่เกี่ยวข้องของที่อยู่สำหรับจัดส่ง

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