Aprende a adaptar tu app de pagos para Android de modo que funcione con los Pagos web y brinde una mejor experiencia del usuario a los clientes.
La Payment Request API ofrece web una interfaz integrada basada en el navegador que permite a los usuarios ingresar el pago requerido la información sea más fácil que nunca. La API también puede invocar pagos específicos de la plataforma de Google Chat.
En comparación con el uso exclusivo de intents de Android, los pagos web permiten una mejor integración. con el navegador, la seguridad y la experiencia del usuario:
- La app de pagos se inicia como modal, en el contexto del sitio web del comercio.
- La implementación es complementaria a tu app de pagos existente, lo que te permite hacer lo siguiente: aprovechar tu base de usuarios.
- Se marcó la firma de la app de pago para evitar transferencia.
- Las apps de pagos admiten varias formas de pago.
- Cualquier forma de pago, como criptomonedas, transferencias bancarias y más, integrado. En los dispositivos Android, las apps de pago incluso pueden integrar métodos que requieren acceso al chip de hardware del dispositivo.
Para implementar los pagos web en una app de pagos para Android, debes seguir cuatro pasos:
- Permite que los comercios descubran tu app de pagos.
- Informa a un comercio si un cliente tiene un instrumento inscrito (como crédito tarjeta) que está listo para pagar.
- Permite que un cliente realice un pago.
- Verifica el certificado de firma del emisor.
Para ver los Pagos web en acción, consulta la android-web-payment demostración.
Paso 1: Permite que los comercios descubran tu app de pagos
Para que un comerciante pueda utilizar tu aplicación de pagos, debe utilizar la página Pagos API de solicitud y Especifica la forma de pago que admites con la forma de pago identificador.
Si tienes un identificador de forma de pago único para tu app de pagos, puedes puedes configurar tu propia forma de pago de Terraform para que los navegadores puedan descubrir tu app.
Paso 2: Informa al comercio si un cliente tiene un instrumento inscrito que esté listo para pagar
El comercio puede llamar a hasEnrolledInstrument()
para consultar si el cliente
puede realizar un pago. Puedes
Implementa IS_READY_TO_PAY
como servicio de Android para responder esta consulta.
AndroidManifest.xml
Declara tu servicio con un filtro de intents 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>
El servicio IS_READY_TO_PAY
es opcional. Si no existe tal controlador de intents en
pago, el navegador web da por sentado que la app siempre puede realizar
pagos.
AIDL
La API para el servicio IS_READY_TO_PAY
se define en AIDL. Crea dos 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);
}
Cómo implementar IsReadyToPayService
La implementación más simple de IsReadyToPayService
se muestra a continuación
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
}
}
Respuesta
El servicio puede enviar su respuesta a través del método handleIsReadyToPay(Boolean)
.
callback?.handleIsReadyToPay(true)
Permiso
Puedes usar Binder.getCallingUid()
para verificar quién es el emisor. Ten en cuenta que
debes hacerlo en el método isReadyToPay
, no en el método onBind
.
override fun isReadyToPay(callback: IsReadyToPayServiceCallback?) {
try {
val callingPackage = packageManager.getNameForUid(Binder.getCallingUid())
// …
Consulta Cómo verificar el certificado de firma del emisor para obtener información sobre cómo hacerlo. para verificar que el paquete de llamada tenga la firma correcta.
Paso 3: Permite que un cliente realice un pago
El comerciante llama a show()
para iniciar el pago.
app
para que el cliente pueda realizar un pago. La app de pago se invoca mediante un código
El intent PAY
con información de transacción en los parámetros del intent
La app de pagos responde con methodName
y details
, que son apps de pagos.
específicas y son opacas para el navegador. El navegador convierte el details
en un objeto de 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 la
details
; el valor de ese parámetro se pasa directamente al comercio.
AndroidManifest.xml
La actividad con el filtro de intents PAY
debe tener una etiqueta <meta-data>
que
identifica el identificador de la forma de pago predeterminada para la
en tu app.
Para admitir varias formas de pago, agrega una etiqueta <meta-data>
con una
Recurso <string-array>
.
<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://bobbucks.dev/pay" />
<meta-data
android:name="org.chromium.payment_method_names"
android:resource="@array/method_names" />
</activity>
resource
debe ser una lista de cadenas, cada una de las cuales debe ser un valor válido,
absoluta de Google 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 intent:
methodNames
methodData
topLevelOrigin
topLevelCertificateChain
paymentRequestOrigin
total
modifiers
paymentRequestId
val extras: Bundle? = intent?.extras
methodNames
Los nombres de los métodos que se usan. Los elementos son las claves en las
Diccionario de methodData
. Estas son las formas que admite la app de pagos.
val methodNames: List<String>? = extras.getStringArrayList("methodNames")
methodData
Una asignación de cada uno de los methodNames
al
methodData
val methodData: Bundle? = extras.getBundle("methodData")
merchantName
El contenido de la etiqueta HTML <title>
de la página de confirmación de compras del comercio (la
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
es
pasado como mystore.com
.
val topLevelOrigin: String? = extras.getString("topLevelOrigin")
topLevelCertificateChain
La cadena de certificados del comercio (la cadena de certificados de la
contexto de navegación). Nulo para localhost y archivo en el disco, que son seguros.
sin certificados SSL. Cada Parcelable
es un paquete con un
Clave certificate
y valor de array 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 invocó el constructor new
PaymentRequest(methodData, details, options)
en JavaScript. Si el botón
se invocó desde el contexto de nivel superior, el valor de este
es igual al valor del parámetro topLevelOrigin
.
val paymentRequestOrigin: String? = extras.getString("paymentRequestOrigin")
total
Es la cadena JSON que representa el importe total de la transacción.
val total: String? = extras.getString("total")
Este es un ejemplo de contenido de la cadena:
{"currency":"USD","value":"25.00"}
modifiers
El resultado de JSON.stringify(details.modifiers)
, en el que details.modifiers
contienen solo supportedMethods
y total
.
paymentRequestId
El campo PaymentRequest.id
que indica "push-payment" las apps deben asociarse con el
estado de transacción. Los sitios web de comercios usarán este campo para consultar
"push-payment" apps para el estado de 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://bobbucks.dev/pay")
putExtra("details", "{\"token\": \"put-some-data-here\"}")
})
finish()
Debes especificar dos parámetros como extras de intent:
methodName
: Es el nombre del método que se usa.details
: Es una cadena JSON que contiene la información necesaria para que el comercio pueda hacer lo siguiente: completar la transacción. Si el valor estrue
, entoncesdetails
debe construido de tal manera queJSON.parse(details)
funcionará correctamente.
Puedes pasar RESULT_CANCELED
si la transacción no se completó en el
app de pago, por ejemplo, si el usuario no escribe el código PIN correcto de
su cuenta en la aplicación de pago. El navegador puede permitirle al usuario elegir
otra app de pagos.
setResult(RESULT_CANCELED)
finish()
Si el resultado de la actividad de una respuesta de pago recibida del pago invocado
la app está configurada como RESULT_OK
, Chrome buscará methodName
que no estén vacías
details
en sus extras. Si la validación falla, Chrome devolverá
promesa de request.show()
con uno de los siguientes errores para el desarrollador
mensajes:
'Payment app returned invalid response. Missing field "details".'
'Payment app returned invalid response. Missing field "methodName".'
Permiso
La actividad puede verificar el llamador con su método getCallingPackage()
.
val caller: String? = callingPackage
El último paso es verificar el certificado de firma del emisor para confirmar que el que llama tiene la firma correcta.
Paso 4: Verifica el certificado de firma del emisor
Puedes verificar el nombre del paquete del llamador con Binder.getCallingUid()
en
IS_READY_TO_PAY
y Activity.getCallingPackage()
en PAY
. Para
verificar que el emisor sea el navegador que tienes en mente, deberías
revisa el certificado de firma y asegúrate de que coincida con el
valor.
Si tu objetivo es el nivel de API 28 o uno superior, y realizas la integración con un navegador
que tiene un único certificado de firma, puedes usar
PackageManager.hasSigningCertificate()
val packageName: String = … // The caller's package name
val certificate: ByteArray = … // The correct signing certificate.
val verified = packageManager.hasSigningCertificate(
callingPackage,
certificate,
PackageManager.CERT_INPUT_SHA256
)
Se prefiere PackageManager.hasSigningCertificate()
para un solo certificado.
navegadores, ya que controla correctamente la rotación de certificados. (Chrome tiene un
certificado de firma único). Las apps que tienen varios certificados de firma no pueden
rotarlas.
Si necesitas admitir niveles de API anteriores 27 o inferiores, o si necesitas administrar
con múltiples certificados de firma, puedes usar
PackageManager.GET_SIGNATURES
val packageName: String = … // The caller's package name
val certificates: Set<ByteArray> = … // The correct set of signing certificates
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) } }