Android ödeme uygulaması geliştiricileri kılavuzu

Android ödeme uygulamanızı Web Payments ile çalışacak şekilde nasıl uyarlayacağınızı ve müşterilere daha iyi bir kullanıcı deneyimi sunacağınızı öğrenin.

Payment Request API, web'e kullanıcıların gerekli ödeme bilgilerini her zamankinden daha kolay girmelerine olanak tanıyan yerleşik bir tarayıcı tabanlı arayüz getirir. API, platforma özel ödeme uygulamalarını da çağırabilir.

Tarayıcı desteği

  • Chrome: 60.
  • Edge: 15.
  • Firefox: İşaretli.
  • Safari: 11.1

Kaynak

Web ödemelerini kullanan platforma özel Google Pay uygulamasıyla ödeme akışı.

Yalnızca Android Intent'leri kullanmaya kıyasla Web Payments, tarayıcı, güvenlik ve kullanıcı deneyimiyle daha iyi entegrasyon sağlar:

  • Ödeme uygulaması, satıcı web sitesi bağlamında modal olarak başlatılır.
  • Mevcut ödeme uygulamanıza ek olarak uygulanan bu çözüm, kullanıcı tabanınızdan yararlanmanızı sağlar.
  • Bilinmeyen kaynaklardan yükleme işlemini önlemek için ödeme uygulamasının imzası kontrol edilir.
  • Ödeme uygulamaları birden fazla ödeme yöntemini destekleyebilir.
  • Kripto para birimi ve banka havalesi gibi tüm ödeme yöntemleri entegre edilebilir. Android cihazlardaki ödeme uygulamaları, cihazdaki donanım çipine erişmeyi gerektiren yöntemleri bile entegre edebilir.

Web ödemelerini bir Android ödeme uygulamasına uygulamak için dört adım gerekir:

  1. Satıcıların ödeme uygulamanızı keşfetmesine izin verin.
  2. Bir müşterinin ödeme yapmaya hazır kayıtlı bir aracısı (ör. kredi kartı) olup olmadığını satıcıya bildirin.
  3. Müşterinin ödeme yapmasına izin verin.
  4. Arayanı imzalama sertifikasını doğrulayın.

Web ödemelerinin nasıl kullanıldığını görmek için android-web-payment demosuna göz atın.

1. adım: Satıcıların ödeme uygulamanızı keşfetmesine izin verin

Satıcıların ödeme uygulamanızı kullanabilmesi için PaymentRequest API'yi kullanmaları ve ödeme yöntemi tanımlayıcısını kullanarak desteklediğiniz ödeme yöntemini belirtmeleri gerekir.

Ödeme uygulamanıza özgü bir ödeme yöntemi tanımlayıcınız varsa tarayıcıların uygulamanızı keşfedebilmesi için kendi ödeme yöntemi manifestinizi oluşturabilirsiniz.

2. adım: Bir müşterinin ödeme yapmaya hazır kayıtlı bir aracısı olup olmadığını satıcıya bildirin

Satıcı, hasEnrolledInstrument()'ü arayarak müşterinin ödeme yapıp yapamayacağını sorgulayabilir. Bu sorguyu yanıtlamak için IS_READY_TO_PAY uygulamasını bir Android hizmeti olarak uygulayabilirsiniz.

AndroidManifest.xml

Hizmetinizi, org.chromium.intent.action.IS_READY_TO_PAY işlemini içeren bir intent filtresiyle beyan edin.

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

IS_READY_TO_PAY hizmeti isteğe bağlıdır. Ödeme uygulamasında böyle bir intent işleyici yoksa web tarayıcısı, uygulamanın her zaman ödeme yapabileceğini varsayar.

AIDL

IS_READY_TO_PAY hizmetinin API'si AIDL'de tanımlanmıştır. Aşağıdaki içeriğe sahip iki AIDL dosyası oluşturun:

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);
}

IsReadyToPayService kullanımı

IsReadyToPayService işlevinin en basit uygulaması aşağıdaki örnekte gösterilmektedir:

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
 
}
}

Yanıt

Hizmet, yanıtını handleIsReadyToPay(Boolean) yöntemiyle gönderebilir.

callback?.handleIsReadyToPay(true)

İzin

Arayanın kim olduğunu kontrol etmek için Binder.getCallingUid()'ü kullanabilirsiniz. Bunu onBind yönteminde değil, isReadyToPay yönteminde yapmanız gerektiğini unutmayın.

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

Çağrı paketinin doğru imzaya sahip olduğunu doğrulama hakkında bilgi edinmek için Arayanın imzalama sertifikasını doğrulama bölümüne bakın.

3. Adım: Müşterinin ödeme yapmasına izin verin

Satıcı, müşterinin ödeme yapabilmesi için show()'i arayarak ödeme uygulamasını başlatır. Ödeme uygulaması, intent parametrelerinde işlem bilgileri bulunan bir Android intent'i PAY aracılığıyla çağrılır.

Ödeme uygulaması, ödeme uygulamasına özel ve tarayıcı için opak olan methodName ve details ile yanıt verir. Tarayıcı, details dizesini JSON'dan serileştirme yoluyla satıcı için JavaScript nesnesine dönüştürür ancak bunun dışında herhangi bir geçerlilik koşulu uygulamaz. Tarayıcı, details parametresini değiştirmez; bu parametrenin değeri doğrudan satıcıya iletilir.

AndroidManifest.xml

PAY intent filtresine sahip etkinlikte, uygulamanın varsayılan ödeme yöntemi tanımlayıcısını tanımlayan bir <meta-data> etiketi olmalıdır.

Birden fazla ödeme yöntemini desteklemek için <string-array> kaynağı içeren bir <meta-data> etiketi ekleyin.

<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, her biri burada gösterildiği gibi HTTPS şemasına sahip geçerli ve mutlak bir URL olan dizelerin listesi olmalıdır.

<?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>

Parametreler

Aşağıdaki parametreler etkinliğe Intent ekstraları olarak aktarılır:

  • methodNames
  • methodData
  • topLevelOrigin
  • topLevelCertificateChain
  • paymentRequestOrigin
  • total
  • modifiers
  • paymentRequestId
val extras: Bundle? = intent?.extras

methodNames

Kullanılan yöntemlerin adları. Öğeler, methodData sözlüğündeki anahtarlardır. Bunlar, ödeme uygulamasının desteklediği yöntemlerdir.

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

methodData

methodNames öğelerinin her biri ile methodData arasında bir eşleme.

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

merchantName

Satıcının ödeme sayfasındaki <title> HTML etiketinin içeriği (tarayıcının üst düzey göz atma bağlamı).

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

topLevelOrigin

Satıcının şema olmadan kaynağı (Üst düzey tarama bağlamının şemasız kaynağı). Örneğin, https://mystore.com/checkout mystore.com olarak iletilir.

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

topLevelCertificateChain

Satıcının sertifika zinciri (üst düzey tarama bağlamının sertifika zinciri). Hem SSL sertifikası olmayan güvenli bağlamlar olan localhost hem de diskteki dosya için null. Her Parcelable, certificate anahtarı ve bayt dizisi değeri içeren bir pakettir.

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

paymentRequestOrigin

JavaScript'te new PaymentRequest(methodData, details, options) oluşturucuyu çağıran iframe göz atma bağlamının şemasız kaynağı. Oluşturucu üst düzey bağlamda çağrıldıysa bu parametrenin değeri topLevelOrigin parametresinin değerine eşittir.

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

total

İşlemin toplam tutarını temsil eden JSON dizesi.

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

Aşağıda, dizenin örnek bir içeriği belirtilmiştir:

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

modifiers

details.modifiers yalnızca supportedMethods ve total içeriyorsa JSON.stringify(details.modifiers) işlevinin çıkışı.

paymentRequestId

"Ödeme gönderme" uygulamalarının işlem durumuyla ilişkilendirmesi gereken PaymentRequest.id alanı. Satıcı web siteleri, bant dışı işlem durumu için "push-payment" uygulamalarını sorgulamak üzere bu alanı kullanır.

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

Yanıt

Etkinlik, yanıtını setResult üzerinden RESULT_OK ile geri gönderebilir.

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

Intent ekstraları olarak iki parametre belirtmeniz gerekir:

  • methodName: Kullanılan yöntemin adı.
  • details: Satıcının işlemi tamamlaması için gerekli bilgileri içeren JSON dizesi. Başarı true ise details, JSON.parse(details)'nin başarılı olacağı şekilde oluşturulmalıdır.

İşlem ödeme uygulamasında tamamlanmadıysa (örneğin, kullanıcı, ödeme uygulamasında hesabı için doğru PIN kodunu yazamadıysa) RESULT_CANCELED iletebilirsiniz. Tarayıcı, kullanıcının farklı bir ödeme uygulaması seçmesine izin verebilir.

setResult(RESULT_CANCELED)
finish
()

Çağrılan ödeme uygulamasından alınan ödeme yanıtının etkinlik sonucu RESULT_OK olarak ayarlanırsa Chrome, ekstralarda boş olmayan methodName ve details olup olmadığını kontrol eder. Doğrulama başarısız olursa Chrome, request.show() tarafından reddedilen bir söz döndürür ve geliştiriciye aşağıdaki hata mesajlarından birini gösterir:

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

İzin

Etkinlik, getCallingPackage() yöntemiyle arayanı kontrol edebilir.

val caller: String? = callingPackage

Son adım, çağrı paketinin doğru imzaya sahip olduğunu onaylamak için arayanın imzalama sertifikasını doğrulamaktır.

4. Adım: Arayanın imzalama sertifikasını doğrulayın

Arayanın paket adını IS_READY_TO_PAY içinde Binder.getCallingUid() ve PAY içinde Activity.getCallingPackage() ile kontrol edebilirsiniz. Arayanın aklınızdaki tarayıcı olduğunu doğrulamak için imza sertifikasını kontrol etmeniz ve doğru değerle eşleştiğinden emin olmanız gerekir.

API düzeyi 28 ve üzerini hedefliyorsanız ve tek bir imzalama sertifikasına sahip bir tarayıcı ile entegrasyon yapıyorsanız PackageManager.hasSigningCertificate() kullanabilirsiniz.

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
)

Sertifika rotasyonunu doğru şekilde işlediğinden tek sertifika tarayıcıları için PackageManager.hasSigningCertificate() tercih edilir. (Chrome'un tek bir imzalama sertifikası vardır.) Birden fazla imzalama sertifikası olan uygulamalar bu sertifikaları döndüremese de

27 ve önceki API düzeylerini desteklemeniz veya birden fazla imzalama sertifikası olan tarayıcıları yönetmeniz gerekiyorsa PackageManager.GET_SIGNATURES kullanabilirsiniz.

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) } }