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.

Yayınlanma tarihi: 5 Mayıs 2020, Son güncelleme tarihi: 27 Mayıs 2025

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.

Browser Support

  • Chrome: 60.
  • Edge: 15.
  • Firefox: behind a flag.
  • Safari: 11.1.

Source

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.
  • Uygulama, mevcut ödeme uygulamanıza ek olarak 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, banka havaleleri ve daha fazlası gibi tüm ödeme yöntemleri entegre edilebilir. Android cihazlardaki ödeme uygulamaları, cihazdaki donanım çipine erişimi 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ı doğrulama 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

Web uygulaması manifest dosyasında related_applications mülkünü Ödeme yöntemi oluşturma bölümündeki talimatlara göre ayarlayın.

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ı, müşterinin ödeme yapıp yapamayacağını sorgulamak için hasEnrolledInstrument()'ü arayabilir. Bu sorguyu yanıtlamak için IS_READY_TO_PAY'ü 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ımlanır. Aşağıdaki içeriğe sahip iki AIDL dosyası oluşturun:

org/chromium/IsReadyToPayServiceCallback.aidl

package org.chromium;

interface IsReadyToPayServiceCallback {
    oneway void handleIsReadyToPay(boolean isReadyToPay);
}

org/chromium/IsReadyToPayService.aidl

package org.chromium;

import org.chromium.IsReadyToPayServiceCallback;

interface IsReadyToPayService {
    oneway void isReadyToPay(IsReadyToPayServiceCallback callback, in Bundle parameters);
}

IsReadyToPayService kullanımı

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

Kotlin

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

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

Java

import org.chromium.IsReadyToPayService;

public class SampleIsReadyToPayService extends Service {
    private final IsReadyToPayService.Stub mBinder =
        new IsReadyToPayService.Stub() {
            @Override
            public void isReadyToPay(IsReadyToPayServiceCallback callback, Bundle parameters) {
                if (callback != null) {
                    callback.handleIsReadyToPay(true);
                }
            }
        };

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
}

Yanıt

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

Kotlin

callback?.handleIsReadyToPay(true)

Java

if (callback != null) {
    callback.handleIsReadyToPay(true);
}

İzin

Arayanın kim olduğunu kontrol etmek için Binder.getCallingUid() kullanabilirsiniz. Android OS, hizmet bağlantısını önbelleğe alıp yeniden kullanabildiğinden ve bu da onBind() yöntemini tetiklemediğinden, bunu onBind yönteminde değil, isReadyToPay yönteminde yapmanız gerektiğini unutmayın.

Kotlin

override fun isReadyToPay(callback: IsReadyToPayServiceCallback?, parameters: Bundle?) {
    try {
        val untrustedPackageName = parameters?.getString("packageName")
        val actualPackageNames = packageManager.getPackagesForUid(Binder.getCallingUid())
        // ...

Java

@Override
public void isReadyToPay(IsReadyToPayServiceCallback callback, Bundle parameters) {
    try {
        String untrustedPackageName = parameters != null
                ? parameters.getString("packageName")
                : null;
        String[] actualPackageNames = packageManager.getPackagesForUid(Binder.getCallingUid());
        // ...

İşlemler Arası İletişim (IPC) çağrıları alırken her zaman null için giriş parametrelerini kontrol edin. Android OS'in farklı sürümleri veya çatalları beklenmedik şekilde davranabileceği ve uygun şekilde ele alınmadığı takdirde hatalara yol açabileceği için bu özellikle önemlidir.

packageManager.getPackagesForUid() genellikle tek bir öğe döndürür ancak kodunuz, bir araya çağıranın birden fazla paket adı kullandığı yaygın olmayan senaryoyu ele almalıdır. Bu sayede uygulamanızın sağlam kalmasını sağlayabilirsiniz.

Arayan paketinin doğru imzaya sahip olduğunu doğrulama hakkında bilgi edinmek için Arayanın imzalama sertifikasını doğrulama başlıklı makaleyi inceleyin.

Parametreler

parameters paketi Chrome 139'a eklendi. Her zaman null ile karşılaştırılmalıdır.

Aşağıdaki parametreler parameters paketinde hizmete iletilir:

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

packageName, Chrome 138'e eklendi. Değerini kullanmadan önce bu parametreyi Binder.getCallingUid() ile doğrulamanız gerekir. parameters paketi arayan kullanıcının tam kontrolü altındayken Binder.getCallingUid() Android OS tarafından kontrol edildiği için bu doğrulama işlemi gereklidir.

topLevelCertificateChain, WebView'de ve genellikle yerel test için kullanılan https olmayan web sitelerinde (ör. http://localhost) null olur.

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 kullanılarak çağrılır.

Ödeme uygulaması, ödeme uygulamasına özgü olan ve tarayıcı tarafından görülemeyen methodName ve details ile yanıt verir. Tarayıcı, JSON dizesi serileştirmesini kullanarak details dizesini satıcı için bir JavaScript sözlüğüne 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/chromium_payment_method_names" />
</activity>

android:resource, her biri burada gösterildiği gibi HTTPS şemasına sahip geçerli ve mutlak bir URL olması gereken bir dize listesi olmalıdır.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="chromium_payment_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 iletilir:

  • methodNames
  • methodData
  • merchantName
  • topLevelOrigin
  • topLevelCertificateChain
  • paymentRequestOrigin
  • total
  • modifiers
  • paymentRequestId
  • paymentOptions
  • shippingOptions

Kotlin

val extras: Bundle? = getIntent()?.extras

Java

Bundle extras = getIntent() != null ? getIntent().getExtras() : null;

methodNames

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

Kotlin

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

Java

List<String> methodNames = extras.getStringArrayList("methodNames");

methodData

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

Kotlin

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

Java

Bundle methodData = extras.getBundle("methodData");

merchantName

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

Kotlin

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

Java

String merchantName = extras.getString("merchantName");

topLevelOrigin

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

Kotlin

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

Java

String topLevelOrigin = extras.getString("topLevelOrigin");

topLevelCertificateChain

Satıcının sertifika zinciri (üst düzey tarama bağlamının sertifika zinciri). Değer, WebView, localhost veya diskteki bir dosya için null olur. Her Parcelable, certificate anahtarı ve bayt dizisi değeri içeren bir pakettir.

Kotlin

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

Java

Parcelable[] topLevelCertificateChain =
        extras.getParcelableArray("topLevelCertificateChain");
if (topLevelCertificateChain != null) {
    for (Parcelable p : topLevelCertificateChain) {
        if (p != null && p instanceof Bundle) {
            ((Bundle) p).getByteArray("certificate");
        }
    }
}

paymentRequestOrigin

JavaScript'te new PaymentRequest(methodData, details, options) kurucusunu çağıran iframe tarama 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.

Kotlin

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

Java

String paymentRequestOrigin = extras.getString("paymentRequestOrigin");

total

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

Kotlin

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

Java

String total = extras.getString("total");

Aşağıda dizenin örnek içeriği verilmiştir:

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

modifiers

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

paymentRequestId

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

Kotlin

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

Java

String paymentRequestId = extras.getString("paymentRequestId");

Yanıt

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

Kotlin

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

Java

Intent result = new Intent();
Bundle extras = new Bundle();
extras.putString("methodName", "https://bobbucks.dev/pay");
extras.putString("details", "{\"token\": \"put-some-data-here\"}");
result.putExtras(extras);
setResult(Activity.RESULT_OK, result);
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. Döndürülmesi gereken veri yoksa bu dize "{}" olabilir. Satıcı web sitesi, bu dizeyi boş bir JavaScript sözlüğü olarak alır.

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

Kotlin

setResult(Activity.RESULT_CANCELED)
finish()

Java

setResult(Activity.RESULT_CANCELED);
finish();

Çağrılan ödeme uygulamasından alınan bir ödeme yanıtının etkinlik sonucu RESULT_OK olarak ayarlanırsa Chrome, ekstralarında 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 kişiyi kontrol edebilir.

Kotlin

val caller: String? = callingPackage

Java

String caller = getCallingPackage();

Son adımda, arayan paketinin doğru imzaya sahip olduğunu onaylamak için arayanın imzalama sertifikasını doğrulayın.

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

Arayanın paket adını IS_READY_TO_PAY içinde Binder.getCallingUid(), PAY içinde ise 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 sonraki sürümleri hedefliyorsanız ve tek bir imzalama sertifikasına sahip bir tarayıcı ile entegrasyon yapıyorsanız PackageManager.hasSigningCertificate() kullanabilirsiniz.

Kotlin

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
)

Java

String packageName =  // The caller's package name
byte[] certificate =  // The correct signing certificate
boolean verified = packageManager.hasSigningCertificate(
        callingPackage,
        certificate,
        PackageManager.CERT_INPUT_SHA256);

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

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

Kotlin

val packageName: String =  // The caller's package name
val expected: Set<String> =  // The correct set of signing certificates

val packageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES)
val sha256 = MessageDigest.getInstance("SHA-256")
val actual = packageInfo.signatures.map {
    SerializeByteArrayToString(sha256.digest(it.toByteArray()))
}
val verified = actual.equals(expected)

Java

String packageName  =  // The caller's package name
Set<String> expected =  // The correct set of signing certificates

PackageInfo packageInfo =
        packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
Set<String> actual = new HashSet<>();
for (Signature it : packageInfo.signatures) {
    actual.add(SerializeByteArrayToString(sha256.digest(it.toByteArray())));
}
boolean verified = actual.equals(expected);

Hata Ayıklama

Hataları veya bilgilendirme mesajlarını gözlemlemek için aşağıdaki komutu kullanın:

adb logcat | grep -i pay