Android ödeme uygulaması geliştiricileri kılavuzu

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

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

Tarayıcı Desteği

  • 60
  • 15
  • 11.1

Kaynak

Web Payments'ı kullanan platforma özgü Google Pay uygulamasıyla ödeme akışı.

Web Payments, sadece Android Intents'i kullanmaya kıyasla tarayıcı, güvenlik ve kullanıcı deneyimi ile daha iyi entegrasyon sağlar:

  • Ödeme uygulaması, satıcının web sitesi bağlamında kalıcı olarak kullanıma sunulur.
  • Mevcut ödeme uygulamanıza ek olarak sunulan bu uygulama, kullanıcı tabanınızdan yararlanmanızı sağlar.
  • Başka cihazdan yüklemeyi ö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şimi gerektiren yöntemleri bile entegre edebilir.

Bir Android ödeme uygulamasında Web Ödemeleri'ni uygulamak için dört adım gerekir:

  1. Satıcıların ödeme uygulamanızı keşfetmesine izin verin.
  2. Müşterinin ödeme yapmaya hazır bir ödeme aracı (ör. kredi kartı) varsa satıcıya bunu bildirin.
  3. Müşterinin ödeme yapmasına izin verin.
  4. Arayanın imza sertifikasını doğrulayın.

Web Ödemeleri'nin işleyişini görmek için android-web-payment demosunu inceleyin.

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

Bir satıcının ödeme uygulamanızı kullanabilmesi için satıcının PaymentRequest API'sini kullanması ve ödeme yöntemi tanımlayıcısını kullanarak desteklediğiniz ödeme yöntemini belirtmesi 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: Müşterinin ödeme yapmaya hazır bir kayıtlı aracının olup olmadığını satıcıya bildirin

Satıcı, müşterinin ödeme yapıp yapamayacağını sorgulamak için hasEnrolledInstrument() numaralı telefonu arayabilir. 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şlemiyle intent filtresiyle tanımlayın.

<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çerikle 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() öğesini 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 amacıyla ödeme uygulamasını başlatmak için show() numaralı telefonu arar. Ödeme uygulaması, intent parametrelerindeki işlem bilgilerini içeren bir Android niyeti 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 serileştirmesi aracılığıyla satıcı için bir JavaScript nesnesine dönüştürür, ancak bunun ötesinde herhangi bir geçerliliği zorunlu kılmaz. Tarayıcı details öğesini 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 bulunmalı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 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

Her bir methodNames ile methodData arasındaki 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). Yerel ana makine ve diskteki dosya için null (her ikisi de SSL sertifikasız güvenli bağlamdır). Her Parcelable, bir certificate anahtarı ve bir bayt dizisi değerine sahip 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ğlamdan çağrıldıysa bu parametrenin değeri topLevelOrigin parametresinin değerine eşit olur.

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 öğesinin yalnızca supportedMethods ve total içerdiği JSON.stringify(details.modifiers) çıkışı.

paymentRequestId

"Push-ödeme" uygulamalarının işlem durumuyla ilişkilendirilmesi gereken PaymentRequest.id alanı. Satıcı web siteleri, işlemin bant dışı durumu için "push" uygulamalarını sorgulamak amacıyla bu alanı kullanır.

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

Yanıt

Etkinlik, RESULT_OK üzerinden setResult üzerinden yanıtını 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) başarılı olacak şekilde yapılandırılmalı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 gönderilen reddedilen sözü döndürür ve 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, arayanı getCallingPackage() yöntemiyle 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 imza sertifikasını doğrulayın

Arayanın paket adını IS_READY_TO_PAY uygulamasında Binder.getCallingUid(), PAY uygulamasında ise Activity.getCallingPackage() ile kontrol edebilirsiniz. Arayanın, aklınızdaki tarayıcı olduğunu gerçekten doğrulamak için imzalama sertifikasını kontrol etmeli ve doğru değerle eşleştiğinden emin olmalısınız.

API düzeyi 28 ve üstünü hedefliyorsanız ve tek imzalama sertifikası olan bir tarayıcıyla entegrasyon yapıyorsanız PackageManager.hasSigningCertificate() kullanılabilir.

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 imza sertifikası vardır.) Birden fazla imzalama sertifikası olan uygulamalar bunları döndüremez.

27 ve önceki eski API düzeylerini desteklemeniz veya birden fazla imzalama sertifikasına sahip tarayıcıları işlemeniz gerekiyorsa PackageManager.GET_SIGNATURES değerini 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) } }