अपने Android पेमेंट ऐप्लिकेशन को वेबसाइट पर पेमेंट करने की सुविधा के साथ काम करने के लिए अडैप्ट करने का तरीका जानें. इससे, ग्राहकों को बेहतर उपयोगकर्ता अनुभव दिया जा सकता है.
Payment Request API, वेब पर ब्राउज़र पर काम करने वाला एक इंटरफ़ेस उपलब्ध कराता है. इसकी मदद से, उपयोगकर्ता पेमेंट के लिए ज़रूरी जानकारी को पहले से ज़्यादा आसानी से डाल सकते हैं. यह एपीआई, प्लैटफ़ॉर्म के हिसाब से बने पेमेंट ऐप्लिकेशन भी शुरू कर सकता है.
सिर्फ़ Android इंटेंट के मुकाबले वेब पेमेंट, ब्राउज़र, सुरक्षा, और उपयोगकर्ता अनुभव के साथ बेहतर इंटिग्रेशन की सुविधा देता है:
- पेमेंट ऐप्लिकेशन को व्यापारी/कंपनी की वेबसाइट के संदर्भ में, मॉडल के तौर पर लॉन्च किया जाता है.
- इसे लागू करने से, आपके मौजूदा पेमेंट ऐप्लिकेशन को बेहतर बनाने में मदद मिलेगी. साथ ही, आपको अपने उपयोगकर्ताओं का फ़ायदा भी मिलेगा.
- साइडलोडिंग को रोकने के लिए, पेमेंट ऐप्लिकेशन के हस्ताक्षर की जांच की जाती है.
- पेमेंट ऐप्लिकेशन में, पैसे चुकाने के कई तरीके इस्तेमाल किए जा सकते हैं.
- क्रिप्टो करंसी, बैंक ट्रांसफ़र वगैरह जैसे पेमेंट के किसी भी तरीके को इंटिग्रेट किया जा सकता है. Android डिवाइसों पर मौजूद पेमेंट ऐप्लिकेशन, ऐसे तरीकों को भी इंटिग्रेट कर सकते हैं जिनके लिए डिवाइस पर मौजूद हार्डवेयर चिप का ऐक्सेस ज़रूरी होता है.
Android के पेमेंट ऐप्लिकेशन में वेब पेमेंट की सुविधा लागू करने के लिए, चार चरण पूरे करने होंगे:
- कारोबारियों को अपना पेमेंट ऐप्लिकेशन खोजने दें.
- व्यापारी/कंपनी को बताएं कि ग्राहक के पास रजिस्टर किया गया कोई ऐसा पेमेंट कार्ड है या नहीं जिससे पेमेंट किया जा सकता है.
- ग्राहक को पेमेंट करने की अनुमति दें.
- कॉलर के हस्ताक्षर वाले सर्टिफ़िकेट की पुष्टि करें.
वेब पेमेंट की सुविधा को इस्तेमाल करते हुए देखने के लिए, android-web-payment के डेमो को देखें.
पहला चरण: कारोबारियों या कंपनियों को अपने पेमेंट ऐप्लिकेशन के बारे में बताना
कारोबारी या कंपनी को आपके पेमेंट ऐप्लिकेशन का इस्तेमाल करने के लिए, Payment Request API का इस्तेमाल करना होगा. साथ ही, पेमेंट के तरीके के आइडेंटिफ़ायर का इस्तेमाल करके, पेमेंट के उस तरीके के बारे में बताना होगा जिसका इस्तेमाल आपके ऐप्लिकेशन में किया जा सकता है.
अगर आपके पास पेमेंट के तरीके का ऐसा आइडेंटिफ़ायर है जो आपके पेमेंट ऐप्लिकेशन के लिए यूनीक है, तो आपके पास अपना पेमेंट के तरीके का मेनिफ़ेस्ट सेट अप करने का विकल्प है. इससे ब्राउज़र आपके ऐप्लिकेशन को खोज पाएंगे.
दूसरा चरण: व्यापारी/कंपनी को यह बताना कि किसी ग्राहक के पास रजिस्टर किया गया कोई ऐसा पेमेंट कार्ड है जिससे पेमेंट किया जा सकता है
कारोबारी, hasEnrolledInstrument()
को कॉल करके यह पूछ सकता है कि ग्राहक पेमेंट कर सकता है या नहीं. इस क्वेरी का जवाब देने के लिए, IS_READY_TO_PAY
को Android सेवा के तौर पर लागू किया जा सकता है.
AndroidManifest.xml
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>
IS_READY_TO_PAY
सेवा का इस्तेमाल करना ज़रूरी नहीं है. अगर पेमेंट ऐप्लिकेशन में ऐसा कोई इंटेंट हैंडलर नहीं है, तो वेब ब्राउज़र यह मान लेता है कि ऐप्लिकेशन कभी भी पेमेंट कर सकता है.
एआईडीएल
IS_READY_TO_PAY
सेवा के लिए एपीआई, एआईडीएल में तय किया गया है. यहां दिए गए कॉन्टेंट के साथ दो AIDL
फ़ाइलें बनाएं:
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
लागू करना
यहां दिए गए उदाहरण में, IsReadyToPayService
को लागू करने का सबसे आसान तरीका दिखाया गया है:
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
}
}
जवाब
सेवा, handleIsReadyToPay(Boolean)
तरीके से अपना जवाब भेज सकती है.
callback?.handleIsReadyToPay(true)
अनुमति
Binder.getCallingUid()
का इस्तेमाल करके, यह पता लगाया जा सकता है कि कॉल करने वाला व्यक्ति कौन है. ध्यान दें कि आपको isReadyToPay
तरीके से ऐसा करना होगा, न कि onBind
तरीके से.
override fun isReadyToPay(callback: IsReadyToPayServiceCallback?) {
try {
val callingPackage = packageManager.getNameForUid(Binder.getCallingUid())
// …
कॉल करने वाले के हस्ताक्षर वाले सर्टिफ़िकेट की पुष्टि करना लेख पढ़ें. इसमें, कॉल करने वाले पैकेज पर सही हस्ताक्षर होने की पुष्टि करने का तरीका बताया गया है.
तीसरा चरण: ग्राहक को पेमेंट करने की सुविधा देना
व्यापारी/कंपनी, पेमेंट ऐप्लिकेशन को लॉन्च करने के लिए show()
को कॉल करती है, ताकि खरीदार पेमेंट कर सके. पेमेंट ऐप्लिकेशन को Android इंटेंट PAY
के ज़रिए शुरू किया गया है. इंटेंट पैरामीटर में लेन-देन की जानकारी का इस्तेमाल किया गया है.
पेमेंट ऐप्लिकेशन, methodName
और details
के साथ रिस्पॉन्स देता है. ये अलग-अलग पेमेंट ऐप्लिकेशन के हिसाब से होते हैं और ब्राउज़र को साफ़ तौर पर नहीं दिखते. ब्राउज़र, JSON को डिससिरियलाइज़ेशन की मदद से, व्यापारी/कंपनी/कारोबारी के लिए details
स्ट्रिंग को JavaScript ऑब्जेक्ट में बदल देता है. हालांकि, वह इसके अलावा किसी भी तरह की पुष्टि नहीं करता. ब्राउज़र, details
में बदलाव नहीं करता; उस पैरामीटर की वैल्यू सीधे व्यापारी/कंपनी/कारोबारी को भेजी जाती है.
AndroidManifest.xml
PAY
इंटेंट फ़िल्टर वाली गतिविधि में <meta-data>
टैग होना चाहिए, जो ऐप्लिकेशन के लिए, पेमेंट के डिफ़ॉल्ट तरीके के आइडेंटिफ़ायर की पहचान करता हो.
पेमेंट के एक से ज़्यादा तरीकों का इस्तेमाल करने के लिए, <meta-data>
टैग के साथ <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
, स्ट्रिंग की सूची होनी चाहिए. इसमें मौजूद हर स्ट्रिंग, एचटीटीपीएस स्कीम वाला मान्य और सटीक यूआरएल होना चाहिए, जैसा कि यहां दिखाया गया है.
<?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>
पैरामीटर
यहां दिए गए पैरामीटर, गतिविधि में इंटेंट एक्सट्रा के तौर पर पास किए जाते हैं:
methodNames
methodData
topLevelOrigin
topLevelCertificateChain
paymentRequestOrigin
total
modifiers
paymentRequestId
val extras: Bundle? = intent?.extras
methodNames
इस्तेमाल किए जा रहे तरीकों के नाम. एलिमेंट, methodData
डिक्शनरी में मौजूद कुंजियां होती हैं. पेमेंट ऐप्लिकेशन में ये तरीके इस्तेमाल किए जा सकते हैं.
val methodNames: List<String>? = extras.getStringArrayList("methodNames")
methodData
हर methodNames
से methodData
तक की मैपिंग.
val methodData: Bundle? = extras.getBundle("methodData")
merchantName
व्यापारी/कंपनी/कारोबारी के चेकआउट पेज (ब्राउज़र के टॉप-लेवल ब्राउज़िंग कॉन्टेक्स्ट) के <title>
एचटीएमएल टैग का कॉन्टेंट.
val merchantName: String? = extras.getString("merchantName")
topLevelOrigin
स्कीम के बिना व्यापारी/कंपनी/कारोबारी का ऑरिजिन (टॉप-लेवल ब्राउज़िंग कॉन्टेक्स्ट का स्कीम-लेस ऑरिजिन). उदाहरण के लिए, https://mystore.com/checkout
को mystore.com
के तौर पर पास किया जाता है.
val topLevelOrigin: String? = extras.getString("topLevelOrigin")
topLevelCertificateChain
कारोबारी/कंपनी/कारोबारी की सर्टिफ़िकेट चेन (टॉप-लेवल ब्राउज़िंग कॉन्टेक्स्ट की सर्टिफ़िकेट चेन). डिस्क पर localhost और फ़ाइल के लिए शून्य है, जो बिना एसएसएल सर्टिफ़िकेट
के सुरक्षित कॉन्टेक्स्ट हैं. हर Parcelable
एक बंडल होता है, जिसमें एक certificate
कुंजी और एक बाइट कलेक्शन वैल्यू होती है.
val topLevelCertificateChain: Array<Parcelable>? =
extras.getParcelableArray("topLevelCertificateChain")
val list: List<ByteArray>? = topLevelCertificateChain?.mapNotNull { p ->
(p as Bundle).getByteArray("certificate")
}
paymentRequestOrigin
iframe ब्राउज़िंग कॉन्टेक्स्ट का स्कीम-लेस ऑरिजिन, जिसने JavaScript में new
PaymentRequest(methodData, details, options)
कंस्ट्रक्टर को शुरू किया है. अगर कॉन्स्ट्रक्टर को टॉप-लेवल कॉन्टेक्स्ट से शुरू किया गया था, तो इस पैरामीटर की वैल्यू, topLevelOrigin
पैरामीटर की वैल्यू के बराबर होगी.
val paymentRequestOrigin: String? = extras.getString("paymentRequestOrigin")
total
लेन-देन की कुल रकम दिखाने वाली JSON स्ट्रिंग.
val total: String? = extras.getString("total")
यहां स्ट्रिंग का एक उदाहरण दिया गया है:
{"currency":"USD","value":"25.00"}
modifiers
JSON.stringify(details.modifiers)
का आउटपुट, जहां details.modifiers
में सिर्फ़ supportedMethods
और total
शामिल हैं.
paymentRequestId
PaymentRequest.id
फ़ील्ड, जिसे "पुश-पेमेंट" ऐप्लिकेशन, लेन-देन की स्थिति से जोड़ता है. कारोबारी या कंपनी की वेबसाइटें, इस फ़ील्ड का इस्तेमाल करके, "पुश-पेमेंट" ऐप्लिकेशन से लेन-देन की स्थिति के बारे में जानकारी हासिल करेंगी.
val paymentRequestId: String? = extras.getString("paymentRequestId")
जवाब
गतिविधि setResult
के ज़रिए RESULT_OK
के ज़रिए अपना जवाब वापस भेज सकती है.
setResult(Activity.RESULT_OK, Intent().apply {
putExtra("methodName", "https://bobbucks.dev/pay")
putExtra("details", "{\"token\": \"put-some-data-here\"}")
})
finish()
आपको इंटेंट एक्स्ट्रा के तौर पर दो पैरामीटर तय करने होंगे:
methodName
: इस्तेमाल किए जा रहे तरीके का नाम.details
: JSON स्ट्रिंग, जिसमें लेन-देन पूरा करने के लिए व्यापारी/कंपनी के लिए ज़रूरी जानकारी होती है. अगर सफलताtrue
है, तोdetails
को इस तरह से बनाया जाना चाहिए किJSON.parse(details)
सफल हो.
अगर पेमेंट ऐप्लिकेशन में लेन-देन पूरा नहीं हुआ है, तो RESULT_CANCELED
को पास किया जा सकता है. उदाहरण के लिए, अगर उपयोगकर्ता ने पेमेंट ऐप्लिकेशन में अपने खाते के लिए सही पिन कोड नहीं डाला है. ब्राउज़र, उपयोगकर्ता को कोई दूसरा पेमेंट ऐप्लिकेशन चुनने की अनुमति दे सकता है.
setResult(RESULT_CANCELED)
finish()
अगर शुरू किए गए पेमेंट ऐप्लिकेशन से मिले पेमेंट के रिस्पॉन्स से जुड़ी गतिविधि का नतीजा, RESULT_OK
पर सेट है, तो Chrome अतिरिक्त जानकारी में methodName
और details
की जांच करेगा. अगर पुष्टि नहीं हो पाती है, तो Chrome, request.show()
से अस्वीकार किए गए प्रॉमिस के साथ, डेवलपर को गड़बड़ी का यह मैसेज दिखाएगा:
'Payment app returned invalid response. Missing field "details".'
'Payment app returned invalid response. Missing field "methodName".'
अनुमति
गतिविधि, getCallingPackage()
तरीके से कॉलर की पहचान कर सकती है.
val caller: String? = callingPackage
आखिरी चरण में, कॉल करने वाले के हस्ताक्षर वाले सर्टिफ़िकेट की पुष्टि की जाती है. इससे यह पक्का किया जाता है कि कॉलिंग पैकेज में सही हस्ताक्षर है.
चौथा चरण: कॉल करने वाले व्यक्ति के हस्ताक्षर करने वाले सर्टिफ़िकेट की पुष्टि करना
कॉलर के पैकेज का नाम देखने के लिए, IS_READY_TO_PAY
में Binder.getCallingUid()
और PAY
में Activity.getCallingPackage()
पर क्लिक करें. यह पुष्टि करने के लिए कि कॉल करने वाला ब्राउज़र वही है जिसे आपको ऐक्सेस करना है, आपको उसके हस्ताक्षर वाले सर्टिफ़िकेट की जांच करनी चाहिए. साथ ही, यह पक्का करना चाहिए कि वह सही वैल्यू से मेल खाता हो.
अगर एपीआई लेवल 28 और उसके बाद के वर्शन को टारगेट किया जा रहा है और किसी ऐसे ब्राउज़र के साथ इंटिग्रेट किया जा रहा है जिसमें एक साइनिंग सर्टिफ़िकेट है, तो 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()
को प्राथमिकता दी जाती है, क्योंकि यह सर्टिफ़िकेट रोटेशन को सही तरीके से मैनेज करता है. (Chrome के पास एक सिंगल साइनिंग सर्टिफ़िकेट होता है.) जिन ऐप्लिकेशन के पास साइन करने के लिए एक से ज़्यादा सर्टिफ़िकेट हैं उनके लिए, सर्टिफ़िकेट को रोटेट नहीं किया जा सकता.
अगर आपको एपीआई के पुराने लेवल 27 और उससे पहले के लेवल के साथ काम करना है या आपको एक से ज़्यादा साइनिंग सर्टिफ़िकेट वाले ब्राउज़र को मैनेज करना है, तो 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) } }