Saiba como adaptar seu app de pagamento para Android para funcionar com o Web Payments e proporcionar uma melhor experiência do usuário aos clientes.
A API Payment Request traz à Web uma interface integrada com base no navegador que permite que os usuários insiram as informações de pagamento necessárias mais fácil do que nunca. A API também pode invocar pagamentos específicos da plataforma apps.
Em comparação com o uso apenas de intenções do Android, os pagamentos na Web permitem uma integração melhor com o navegador, a segurança e a experiência do usuário:
- O app de pagamento é iniciado como um modal, no contexto do site do comerciante.
- A implementação é complementar ao seu app de pagamento atual, permitindo que você aproveitar sua base de usuários.
- A assinatura do app de pagamento é verificada para evitar sideload.
- Os apps de pagamento aceitam várias formas de pagamento.
- Qualquer forma de pagamento, como criptomoedas, transferências bancárias e muito mais, pode ser integradas. Os apps de pagamento em dispositivos Android podem até mesmo integrar métodos que pedem acesso ao chip de hardware do dispositivo.
São necessárias quatro etapas para implementar pagamentos na Web em um app de pagamento para Android:
- Permita que os comerciantes descubram seu app de pagamento.
- Informe um comerciante se um cliente tem um instrumento registrado (como crédito está pronto para pagar.
- Permitir que um cliente faça um pagamento.
- Verifique o certificado de assinatura do autor da chamada.
Para ver o Web Payments em ação, confira a android-web-payment demonstração.
Etapa 1: permitir que os comerciantes descubram seu app de pagamento
Para que um comerciante use seu app de pagamento, ele precisa utilizar a página Pagamentos API Request e especifique a forma de pagamento aceita usando a forma de pagamento identificador.
Se você tiver um identificador de forma de pagamento exclusivo para seu app de pagamento, você pode configurar sua própria forma de pagamento manifesto para que os navegadores possam a descobrir seu app.
Etapa 2: informar ao comerciante se um cliente tem um instrumento registrado pronto para pagamento
O comerciante pode chamar hasEnrolledInstrument()
para consultar se o cliente
consegue fazer um pagamento. Você pode
implemente IS_READY_TO_PAY
como um serviço do Android para responder a essa consulta.
AndroidManifest.xml
Declarar o serviço com um filtro de intent com a ação
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>
O serviço IS_READY_TO_PAY
é opcional. Se não houver um gerenciador de intents no
o aplicativo de pagamento, o navegador da web presumir que o aplicativo sempre pode fazer
pagamentos.
AIDL
A API do serviço IS_READY_TO_PAY
é definida na AIDL. Criar duas AIDLs
com o seguinte conteúdo:
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);
}
Implementação de IsReadyToPayService
A implementação mais simples de IsReadyToPayService
é mostrada abaixo
exemplo:
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
}
}
Resposta
O serviço pode enviar a resposta pelo método handleIsReadyToPay(Boolean)
.
callback?.handleIsReadyToPay(true)
Permissão
Use Binder.getCallingUid()
para saber quem é o autor da chamada. Observe que você
precisa fazer isso no método isReadyToPay
, não no onBind
.
override fun isReadyToPay(callback: IsReadyToPayServiceCallback?) {
try {
val callingPackage = packageManager.getNameForUid(Binder.getCallingUid())
// …
Consulte Verificar o certificado de assinatura do autor da chamada para saber como para verificar se o pacote de chamada tem a assinatura correta.
Etapa 3: permitir que um cliente faça um pagamento
O comerciante chama show()
para iniciar o pagamento
app
para que o cliente possa fazer um pagamento. O aplicativo de pagamento é invocado por uma
PAY
com informações de transação nos parâmetros da intent.
O app de pagamento responde com methodName
e details
, que são apps de pagamento.
específicos e opacos para o navegador. O navegador converte o details
.
em um objeto JavaScript para o comerciante via desserialização JSON, mas
não aplica qualquer validade além disso. O navegador não modifica a
details
o valor dele é transmitido diretamente ao comerciante.
AndroidManifest.xml
A atividade com o filtro de intent PAY
precisa ter uma tag <meta-data>
que
identifica o identificador da forma de pagamento padrão do
do app.
Para aceitar várias formas de pagamento, adicione uma tag <meta-data>
com um
<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>
O resource
precisa ser uma lista de strings, e cada uma delas precisa ser um valor válido
URL absoluto com um esquema HTTPS, como mostrado aqui.
<?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
Os parâmetros a seguir são passados para a atividade como extras de intent:
methodNames
methodData
topLevelOrigin
topLevelCertificateChain
paymentRequestOrigin
total
modifiers
paymentRequestId
val extras: Bundle? = intent?.extras
methodNames
Os nomes dos métodos que estão sendo usados. Os elementos são as chaves na
Dicionário methodData
. Essas são as formas de pagamento compatíveis com o app.
val methodNames: List<String>? = extras.getStringArrayList("methodNames")
methodData
Um mapeamento de cada methodNames
para o
methodData
.
val methodData: Bundle? = extras.getBundle("methodData")
merchantName
O conteúdo da tag HTML <title>
da página de finalização da compra do comerciante (o atributo
contexto de navegação de nível superior do navegador).
val merchantName: String? = extras.getString("merchantName")
topLevelOrigin
A origem do comerciante sem o esquema (a origem sem esquema do
contexto de navegação de nível superior). Por exemplo, https://mystore.com/checkout
é
transmitido como mystore.com
.
val topLevelOrigin: String? = extras.getString("topLevelOrigin")
topLevelCertificateChain
A cadeia de certificados do comerciante (a cadeia de certificados do nível mais alto
contexto de navegação). Nulo para localhost e arquivo em disco, que são seguros
contextos sem certificados SSL. Cada Parcelable
é um pacote com um
chave certificate
e um 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
A origem sem esquema do contexto de navegação iframe que invocou o construtor new
PaymentRequest(methodData, details, options)
no JavaScript. Se o
construtor foi invocado a partir do contexto de nível superior, então o valor deste
é igual ao valor do parâmetro topLevelOrigin
.
val paymentRequestOrigin: String? = extras.getString("paymentRequestOrigin")
total
A string JSON que representa o valor total da transação.
val total: String? = extras.getString("total")
Confira um exemplo de conteúdo da string:
{"currency":"USD","value":"25.00"}
modifiers
A saída de JSON.stringify(details.modifiers)
, em que details.modifiers
contêm apenas supportedMethods
e total
.
paymentRequestId
O campo PaymentRequest.id
que "push-payment" e apps devem se associar
estado da transação. Os sites dos comerciantes vão usar esse campo para consultar
"push-payment" para o estado de transação fora da banda.
val paymentRequestId: String? = extras.getString("paymentRequestId")
Resposta
A atividade pode enviar a resposta de volta pelo setResult
com RESULT_OK
.
setResult(Activity.RESULT_OK, Intent().apply {
putExtra("methodName", "https://bobbucks.dev/pay")
putExtra("details", "{\"token\": \"put-some-data-here\"}")
})
finish()
Especifique dois parâmetros como extras de intent:
methodName
: o nome do método que está sendo usado.details
: string JSON contendo informações necessárias para o comerciante. para concluir a transação. Se o sucesso fortrue
, entãodetails
precisará ser construído de forma queJSON.parse(details)
tenha sucesso.
Você pode transmitir RESULT_CANCELED
se a transação não tiver sido concluída no
app de pagamento, por exemplo, se o usuário não digitar o código PIN correto do
da conta no app de pagamento. O navegador pode permitir que o usuário escolha
outro app de pagamento.
setResult(RESULT_CANCELED)
finish()
Se o resultado da atividade de uma resposta de pagamento recebida do pagamento invocado
app estiver definido como RESULT_OK
, o Chrome procurará methodName
não vazio e
details
nos extras. Se a validação falhar, o Chrome retornará uma mensagem
promessa de request.show()
com um dos seguintes erros voltados ao desenvolvedor
mensagens:
'Payment app returned invalid response. Missing field "details".'
'Payment app returned invalid response. Missing field "methodName".'
Permissão
A atividade pode verificar o autor da chamada com o método getCallingPackage()
.
val caller: String? = callingPackage
A etapa final é verificar o certificado de assinatura do autor da chamada para confirmar se o o pacote que está chamando tem a assinatura correta.
Etapa 4: verificar o certificado de assinatura do autor da chamada
É possível verificar o nome do pacote do autor da chamada com Binder.getCallingUid()
em
IS_READY_TO_PAY
e com Activity.getCallingPackage()
em PAY
. Para
confirmar que o autor da chamada é o navegador que você tem em mente,
verifique o certificado de assinatura dele e verifique se ele corresponde ao
.
Se você tem como alvo uma API de nível 28 ou superior e está fazendo a integração com um navegador
com um único certificado de assinatura,
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
)
PackageManager.hasSigningCertificate()
é preferível para um único certificado
navegadores, porque ele lida corretamente com a rotação de certificados. (O Chrome tem um
certificado de assinatura única. Os apps com vários certificados de assinatura não podem
girá-los.
Se você precisa oferecer suporte a APIs de nível 27 e anteriores ou lidar com
navegadores com vários certificados de assinatura, é possível 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) } }