Предоставление информации о доставке и контактной информации из платежного приложения Android.

Как обновить платежное приложение Android, чтобы предоставить адрес доставки и контактную информацию плательщика с помощью API веб-платежей.

Сахель Шарифи
Sahel Sharify

Ввод адреса доставки и контактной информации через веб-форму может оказаться утомительным занятием для клиентов. Это может привести к ошибкам и снижению коэффициента конверсии.

Вот почему API запроса платежа поддерживает функцию запроса адреса доставки и контактной информации. Это дает множество преимуществ:

  • Пользователи могут выбрать правильный адрес всего несколькими нажатиями.
  • Адрес всегда возвращается в стандартизированном формате .
  • Отправка неправильного адреса менее вероятна.

Браузеры могут отложить сбор адреса доставки и контактной информации платежному приложению, чтобы обеспечить унифицированный процесс оплаты. Эта функция называется делегированием .

Когда это возможно, Chrome делегирует сбор адреса доставки и контактной информации клиента вызываемому платежному приложению Android. Делегирование уменьшает трудности во время оформления заказа.

Веб-сайт продавца может динамически обновлять варианты доставки и общую стоимость в зависимости от выбора покупателем адреса и варианта доставки.

Вариант доставки и адрес доставки меняются. Посмотрите, как это динамически влияет на варианты доставки и общую стоимость.

Чтобы добавить поддержку делегирования в уже существующее платежное приложение Android, выполните следующие действия:

  1. Заявить о поддержке делегаций .
  2. Анализ дополнительных возможностей 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>

Анализ дополнительных возможностей PAY для необходимых вариантов оплаты

Продавец может указать дополнительную необходимую информацию, используя словарь paymentOptions . Chrome предоставит список обязательных параметров, которые может предоставить ваше приложение, передав следующие параметры в действие PAY в качестве Intent extras .

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" . Ваше приложение может использовать эту подсказку в своем пользовательском интерфейсе, когда запрашивает адрес пользователя или выбирает варианты доставки.

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 равно true.
  • payerPhone — номер телефона плательщика. Это должна быть непустая строка, если для paymentOptions.requestPayerPhone установлено значение true.
  • payerEmail — адрес электронной почты плательщика. Это должна быть непустая строка, если для paymentOptions.requestPayerEmail установлено значение true.
  • shippingAddress — указанный пользователем адрес доставки. Это должен быть непустой пакет, если для paymentOptions.requestShipping установлено значение true. Пакет должен иметь следующие ключи, которые представляют разные части физического адреса .
    • city
    • countryCode
    • dependentLocality
    • organization
    • phone
    • postalCode
    • recipient
    • region
    • sortingCode
    • addressLine Все ключи, кроме addressLine имеют строковые значения. addressLine представляет собой массив строк.
  • shippingOptionId — идентификатор выбранного пользователем варианта доставки. Это должна быть непустая строка, если для paymentOptions.requestShipping установлено значение true.

Подтвердите ответ на платеж

Если результат активности платежного ответа, полученного от вызванного платежного приложения, установлен на 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")
    }
}

Дополнительно: Поддержка динамического потока

Иногда общая стоимость транзакции увеличивается, например, когда пользователь выбирает вариант экспресс-доставки или когда список доступных вариантов доставки или их цены меняются, когда пользователь выбирает международный адрес доставки. Когда ваше приложение предоставляет выбранный пользователем адрес или опцию доставки, оно должно иметь возможность уведомлять продавца о любых изменениях адреса доставки или опции и показывать пользователю обновленные платежные реквизиты (предоставленные продавцом).

АИДЛ

Чтобы уведомить продавца о новых изменениях, используйте службу PaymentDetailsUpdateService , объявленную в AndroidManifest.xml Chrome. Чтобы использовать эту службу, создайте два файла AIDL следующего содержания:

app/src/main/aidl/org/chromium/comкомпоненты/платежи/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/comкомпоненты/платежи/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"
Хром "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 — это пакет, эквивалентный словарю WebIDL PaymentRequestDetailsUpdate (после редактирования поля modifiers ) и содержит следующие дополнительные ключи:

  • total — пакет, содержащий ключи currency и value , оба ключа имеют строковые значения.
  • shippingOptions — набор вариантов доставки.
  • error — строка, содержащая общее сообщение об ошибке (например, когда changeShippingOption не предоставляет действительный идентификатор варианта доставки).
  • stringifiedPaymentMethodErrors — строка JSON, представляющая ошибки проверки метода оплаты.
  • addressErrors — пакет с дополнительными ключами, идентичными адресу доставки и строковым значениям. Каждый ключ представляет собой ошибку проверки, связанную с соответствующей частью адреса доставки.

Отсутствие ключа означает, что его значение не изменилось.