Saltar al contenido principal




Aprenda a adaptar su aplicación de pago de Android para que funcione con Pagos web y brinde una mejor experiencia de usuario a los clientes.

los API de solicitud de pago trae a la web una interfaz integrada basada en navegador que permite a los usuarios ingresar la información de pago requerida más fácil que nunca. La API también puede invocar aplicaciones de pago específicas de la plataforma.

Flujo de pago con la aplicación Google Pay específica de la plataforma que utiliza Pagos web.

En comparación con el uso de solo Android Intents, los pagos web permiten una mejor integración con el navegador, la seguridad y la experiencia del usuario:

  • La aplicación de pago se lanza como modal, en el contexto del sitio web del comerciante.
  • La implementación es complementaria a su aplicación de pago existente, lo que le permite aprovechar su base de usuarios.
  • Se comprueba la firma de la aplicación de pago para evitar
    carga lateral.
  • Las aplicaciones de pago pueden admitir varios métodos de pago.
  • Se puede integrar cualquier método de pago, como criptomonedas, transferencias bancarias y más. Las aplicaciones de pago en dispositivos Android pueden incluso integrar métodos que requieren acceso al chip de hardware en el dispositivo.

Se necesitan cuatro pasos para implementar los pagos web en una aplicación de pago de Android:

  1. Deje que los comerciantes descubran su aplicación de pago.
  2. Informe a un comerciante si un cliente tiene un instrumento registrado (como una tarjeta de crédito) que está listo para pagar.
  3. Deje que un cliente realice el pago.
  4. Verifique el certificado de firma de la persona que llama.

Para ver los pagos web en acción, consulte el
pago-web-android
manifestación.

Paso 1: permita que los comerciantes descubran su aplicación de pago

Para que un comerciante utilice su aplicación de pago, debe utilizar la API de solicitud de pago y especifique el método de pago que admite utilizando el identificador de método de pago.

Si tiene un identificador de método de pago exclusivo para su aplicación de pago, puede configurar el suyo manifiesto de método de pago para que los navegadores puedan descubrir su aplicación.

Paso 2: Informe a un comerciante si un cliente tiene un instrumento registrado que está listo para pagar

El comerciante puede llamar hasEnrolledInstrument() para consultar si el cliente puede realizar un pago. Puedes implementar IS_READY_TO_PAY como un servicio de Android para responder a esta consulta.

AndroidManifest.xml

Declare su servicio con un filtro de intención con la acción
org.chromium.intent.action.IS_READY_TO_PAY.

<service
android:name=".SampleIsReadyToPayService"
android:exported="true">

<intent-filter>
<action android:name="org.chromium.intent.action.IS_READY_TO_PAY" />
</intent-filter>
</service>

los IS_READY_TO_PAY el servicio es opcional. Si no hay tal controlador de intenciones en la aplicación de pago, entonces el navegador web asume que la aplicación siempre puede realizar pagos.

AIDL

La API para IS_READY_TO_PAY el servicio está definido en AIDL. Cree dos archivos AIDL con el siguiente contenido:

app / src / main / aidl / org / chromium / IsReadyToPayServiceCallback.aidl

package org.chromium;
interface IsReadyToPayServiceCallback {
oneway void handleIsReadyToPay(boolean isReadyToPay);
}

app / src / main / aidl / org / chromium / IsReadyToPayService.aidl

package org.chromium;
import org.chromium.IsReadyToPayServiceCallback;

interface IsReadyToPayService {
oneway void isReadyToPay(IsReadyToPayServiceCallback callback);
}

Implementar IsReadyToPayService

La implementación más simple de IsReadyToPayService se muestra en el siguiente ejemplo:

class SampleIsReadyToPayService : Service() {
private val binder = object : IsReadyToPayService.Stub() {
override fun isReadyToPay(callback: IsReadyToPayServiceCallback?) {
callback?.handleIsReadyToPay(true)
}
}

override fun onBind(intent: Intent?): IBinder? {
return binder
}
}

Parámetros

Pase los siguientes parámetros a onBind como extras de Intent:

  • methodNames
  • methodData
  • topLevelOrigin
  • topLevelCertificateChain
  • topLevelCertificateChain
  • paymentRequestOrigin

override fun onBind(intent: Intent?): IBinder? {
val extras: Bundle? = intent?.extras
}

methodNames

Los nombres de los métodos que se consultan. Los elementos son las claves en el
methodData diccionario e indique los métodos que admite la aplicación de pago.

val methodNames: List<String>? = extras.getStringArrayList("methodNames")

methodData

Un mapeo de cada entrada de methodNames al
methodData.

val methodData: Bundle? = extras.getBundle("methodData")

topLevelOrigin

El origen del comerciante sin el esquema (el origen sin esquema del contexto de navegación de nivel superior). Por ejemplo, https://mystore.com/checkout se pasará como mystore.com.

val topLevelOrigin: String? = extras.getString("topLevelOrigin")

topLevelCertificateChain

La cadena de certificados del comerciante (la cadena de certificados del contexto de navegación de nivel superior). Nulo para localhost y archivo en disco, que son contextos seguros sin certificados SSL. La cadena de certificados es necesaria porque una aplicación de pago puede tener diferentes requisitos de confianza para los sitios web.

val topLevelCertificateChain: Array<Parcelable>? =
extras.getParcelableArray("topLevelCertificateChain")

Cada Parcelable es un Bundle con un "certificate" clave y un valor de matriz de bytes.

val list: List<ByteArray>? = topLevelCertificateChain?.mapNotNull { p ->
(p as Bundle).getByteArray("certificate")
}

paymentRequestOrigin

El origen sin esquema del contexto de navegación de iframe que invocaba new PaymentRequest(methodData, details, options) constructor en JavaScript. Si el constructor fue invocado desde el contexto de nivel superior, entonces el valor de este parámetro es igual al valor de topLevelOrigin parámetro.

val paymentRequestOrigin: String? = extras.getString("paymentRequestOrigin")

Respuesta

El servicio puede enviar su respuesta a través de handleIsReadyToPay(Boolean) método.

callback?.handleIsReadyToPay(true)

Permiso

Puedes usar Binder.getCallingUid() para comprobar quién es la persona que llama. Tenga en cuenta que tiene que hacer esto en el isReadyToPay método, no en el onBind método.

override fun isReadyToPay(callback: IsReadyToPayServiceCallback?) {
try {
val callingPackage = packageManager.getNameForUid(Binder.getCallingUid())

Consulte Verificar el certificado de firma de la persona que llama para saber cómo verificar que el paquete de llamada tenga la firma correcta.

Paso 3: deje que un cliente realice el pago

El comerciante llama show() para iniciar la aplicación de pago para que el cliente pueda realizar un pago. La aplicación de pago se invoca a través de una intención de Android PAY con información de la transacción en los parámetros de intención.

La aplicación de pago responde con methodName y details, que son específicas de la aplicación de pago y son opacas para el navegador. El navegador convierte el details
cadena en un objeto JavaScript para el comerciante a través de la deserialización JSON, pero no aplica ninguna validez más allá de eso. El navegador no modifica el
details; el valor de ese parámetro se pasa directamente al comerciante.

AndroidManifest.xml

La actividad con el PAY El filtro de intención debe tener un <meta-data> etiqueta que identifica el identificador de método de pago predeterminado para la aplicación.

Para admitir varios métodos de pago, agregue un <meta-data> etiqueta con un
<string-array> recurso.

<activity
android:name=".PaymentActivity"
android:theme="@style/Theme.SamplePay.Dialog">

<intent-filter>
<action android:name="org.chromium.intent.action.PAY" />
</intent-filter>

<meta-data
android:name="org.chromium.default_payment_method_name"
android:value="https://bobpay.xyz/pay" />

<meta-data
android:name="org.chromium.payment_method_names"
android:resource="@array/method_names" />

</activity>

los resource debe ser una lista de cadenas, cada una de las cuales debe ser una URL absoluta válida con un esquema HTTPS como se muestra aquí.

<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="method_names">
<item>https://alicepay.com/put/optional/path/here</item>
<item>https://charliepay.com/put/optional/path/here</item>
</string-array>
</resources>

Parámetros

Los siguientes parámetros se pasan a la actividad como extras de intención:

  • methodNames
  • methodData
  • topLevelOrigin
  • topLevelCertificateChain
  • paymentRequestOrigin
  • total
  • modifiers
  • paymentRequestId

val extras: Bundle? = intent?.extras

methodNames

Los nombres de los métodos que se utilizan. Los elementos son las claves en el
methodData diccionario. Estos son los métodos que admite la aplicación de pago.

val methodNames: List<String>? = extras.getStringArrayList("methodNames")

methodData

Un mapeo de cada uno de los methodNames al
methodData.

val methodData: Bundle? = extras.getBundle("methodData")

Nombre del comerciante

El contenido del <title> Etiqueta HTML de la página de pago del comerciante (el contexto de navegación de nivel superior del navegador).

val merchantName: String? = extras.getString("merchantName")

topLevelOrigin

El origen del comerciante sin el esquema (El origen sin esquema del contexto de navegación de nivel superior). Por ejemplo, https://mystore.com/checkout se pasa como mystore.com.

val topLevelOrigin: String? = extras.getString("topLevelOrigin")

topLevelCertificateChain

La cadena de certificados del comerciante (la cadena de certificados del contexto de navegación de nivel superior). Nulo para localhost y archivo en disco, que son contextos seguros sin certificados SSL. Cada Parcelable es un paquete con un
certificate clave y un valor de matriz de bytes.

val topLevelCertificateChain: Array<Parcelable>? =
extras.getParcelableArray("topLevelCertificateChain")
val list: List<ByteArray>? = topLevelCertificateChain?.mapNotNull { p ->
(p as Bundle).getByteArray("certificate")
}

paymentRequestOrigin

El origen sin esquema del contexto de navegación de iframe que invocaba new PaymentRequest(methodData, details, options) constructor en JavaScript. Si el constructor fue invocado desde el contexto de nivel superior, entonces el valor de este parámetro es igual al valor de topLevelOrigin parámetro.

val paymentRequestOrigin: String? = extras.getString("paymentRequestOrigin")

total

La cadena JSON que representa el monto total de la transacción.

val total: String? = extras.getString("total")

Aquí hay un ejemplo de contenido de la cadena:

{"currency":"USD","value":"25.00"}

modifiers

La salida de JSON.stringify(details.modifiers), dónde details.modifiers
contener solo supportedMethods y total.

paymentRequestId

los PaymentRequest.id campo que las aplicaciones de «pago automático» deben asociar con el estado de la transacción. Los sitios web de comerciantes utilizarán este campo para consultar las aplicaciones de «pago automático» para conocer el estado de la transacción fuera de banda.

val paymentRequestId: String? = extras.getString("paymentRequestId")

Respuesta

La actividad puede enviar su respuesta a través de setResult con RESULT_OK.

setResult(Activity.RESULT_OK, Intent().apply {
putExtra("methodName", "https://bobpay.xyz/pay")
putExtra("details", "{"token": "put-some-data-here"}")
})
finish()

Debe especificar dos parámetros como extras de intención:

  • methodName: El nombre del método que se está utilizando.
  • details: Cadena JSON que contiene la información necesaria para que el comerciante complete la transacción. Si el éxito es true, entonces details debe estar construido de tal manera que JSON.parse(details) podría suceder.

Puedes pasar RESULT_CANCELED si la transacción no se completó en la aplicación de pago, por ejemplo, si el usuario no ingresó el código PIN correcto para su cuenta en la aplicación de pago. El navegador puede permitir al usuario elegir una aplicación de pago diferente.

setResult(RESULT_CANCELED)
finish()

Si el resultado de la actividad de una respuesta de pago recibida de la aplicación de pago invocada se establece en RESULT_OK, luego Chrome comprobará si no está vacío methodName y
details en sus extras. Si la validación falla, Chrome devolverá una promesa rechazada de request.show() con uno de los siguientes mensajes de error que enfrentan los desarrolladores:

'Payment app returned invalid response. Missing field "details".'
'Payment app returned invalid response. Missing field "methodName".'

Permiso

La actividad puede comprobar la persona que llama con su getCallingPackage() método.

val caller: String? = callingPackage

El último paso es verificar el certificado de firma de la persona que llama para confirmar que el paquete de llamada tiene la firma correcta.

Paso 4: Verifique el certificado de firma de la persona que llama

Puede comprobar el nombre del paquete de la persona que llama con Binder.getCallingUid() en
IS_READY_TO_PAY, y con Activity.getCallingPackage() en PAY. Para verificar realmente que la persona que llama es el navegador que tiene en mente, debe verificar su certificado de firma y asegurarse de que coincida con el valor correcto.

Si tiene como objetivo el nivel de API 28 y superior y se está integrando con un navegador que tiene un solo certificado de firma, puede usar
PackageManager.hasSigningCertificate().

val packageName: String =
val certificate: ByteArray =
val verified = packageManager.hasSigningCertificate(
callingPackage,
certificate,
PackageManager.CERT_INPUT_SHA256
)

PackageManager.hasSigningCertificate() se prefiere para navegadores de certificados únicos, ya que maneja correctamente la rotación de certificados. (Chrome tiene un solo certificado de firma). Las aplicaciones que tienen varios certificados de firma no pueden rotarlos.

Si necesita admitir niveles de API anteriores 27 e inferiores, o si necesita manejar navegadores con varios certificados de firma, puede usar
PackageManager.GET_SIGNATURES.

val packageName: String =
val certificates: Set<ByteArray> =

val packageInfo = getPackageInfo(packageName, PackageManager.GET_SIGNATURES)
val sha256 = MessageDigest.getInstance("SHA-256")
val signatures = packageInfo.signatures.map { sha256.digest(it.toByteArray()) }
val verified = signatures.size == certificates.size &&
signatures.all { s -> certificates.any { it.contentEquals(s) } }

R Marketing Digital