במאמר הזה מוסבר איך להתאים את אפליקציית התשלומים ב-Android כך שיפעל עם תשלומים באינטרנט, ולספק חוויית משתמש טובה יותר ללקוחות.
Payment Request API באינטרנט ממשק מובנה מבוסס דפדפן שמאפשר למשתמשים להזין את התשלום הנדרש קל יותר מאי פעם. ה-API יכול גם להפעיל תשלומים ספציפיים לפלטפורמה. באפליקציות.
בהשוואה לשימוש באובייקטים של Intent של Android בלבד, תשלומים באינטרנט מאפשרים שילוב טוב יותר עם הדפדפן, האבטחה וחוויית המשתמש:
- אפליקציית התשלומים מופעלת כמודל, בהקשר של אתר המוכר.
- ההטמעה משלימה לאפליקציית התשלומים הקיימת, ומאפשרת לכם: לנצל את בסיס המשתמשים שלכם.
- החתימה של אפליקציית התשלומים נבדקה כדי למנוע התקנה ממקור לא ידוע.
- אפליקציות תשלום יכולות לתמוך בכמה אמצעי תשלום.
- אפשר להשתמש בכל אמצעי תשלום, כמו מטבע וירטואלי, העברות בנקאיות ועוד משולב. אפליקציות תשלום במכשירי Android יכולות אפילו לשלב אמצעי תשלום נדרשת גישה לצ'יפ של החומרה במכשיר.
יש ארבעה שלבים להטמעת תשלומים באינטרנט באפליקציית תשלומים ל-Android:
- רוצה לאפשר למוכרים לגלות את אפליקציית התשלומים שלך?
- ליידע את המוכר אם הלקוח מחזיק באמצעי תשלום רשום (כמו זיכוי) שניתן לשלם).
- מאפשרים ללקוח לבצע תשלום.
- מאמתים את אישור החתימה של המתקשר.
כדי לראות את התכונה 'תשלומים באינטרנט', אפשר לעבור אל android-web-payment .
שלב 1: מאפשרים למוכרים לגלות את אפליקציית התשלומים שלכם
כדי שמוֹכר יוכל להשתמש באפליקציית התשלומים שלכם, הוא צריך להשתמש בכלי Payment בקשת API וגם לציין את אמצעי התשלום שנתמך על ידי אמצעי התשלום" מזהה.
אם יש לכם מזהה אמצעי תשלום ייחודי לאפליקציית התשלומים שלכם, אתם יכולים להגדיר אמצעי תשלום משלכם מניפסט, כדי שדפדפנים לגלות את האפליקציה.
שלב 2: מודיעים למוכר אם יש ללקוח אמצעי תשלום רשום שמוכן לתשלום
המוכר יכול להתקשר אל hasEnrolledInstrument()
כדי לשאול אם הלקוח
יכול לבצע תשלום. אפשר
צריך להטמיע את IS_READY_TO_PAY
בתור שירות Android כדי לענות על השאילתה הזו.
AndroidManifest.xml
הצהרה על השירות שלכם עם מסנן Intent עם הפעולה
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
הוא אופציונלי. אם אין handler כזה של Intent
אפליקציית התשלום, אז דפדפן האינטרנט מניח שהאפליקציה תמיד יכולה
תשלומים.
AIDL
ה-API של השירות IS_READY_TO_PAY
מוגדר ב-AIDL. יצירת שני 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())
// …
במאמר אימות אישור החתימה של המתקשר מוסבר איך כדי לוודא שחבילת השיחות כוללת את החתימה הנכונה.
שלב 3: מאפשרים ללקוח לבצע תשלום
המוכר מתקשר אל show()
כדי להפעיל את התשלום
אפליקציה
כדי שהלקוח יוכל לבצע תשלום. אפליקציית התשלומים מופעלת דרך Android
Intent PAY
עם פרטי עסקאות בפרמטרים של Intent.
אפליקציית התשלומים מגיבה עם methodName
ו-details
, שהן אפליקציית תשלומים
ספציפיות והם אטומים לדפדפן. הדפדפן ממיר את הקובץ details
מחרוזת לאובייקט JavaScript עבור המוֹכר באמצעות פעולת deserialization של JSON, אבל
לא אוכפת תוקף מעבר לזה. הדפדפן לא משנה את
details
; הערך של הפרמטר יועבר ישירות אל המוכר.
AndroidManifest.xml
הפעילות עם מסנן Intent מסוג 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
חייב להיות רשימה של מחרוזות, שכל אחת מהן חייבת להיות חוקית,
כתובת URL מוחלטת עם סכימת HTTPS, כפי שמוצג כאן.
<?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>
פרמטרים
הפרמטרים הבאים מועברים לפעילות כתוספות של Intent:
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
התוכן של תג ה-HTML <title>
בדף התשלום של המוכר (החלק
הקשר הגלישה ברמה העליונה של הדפדפן).
val merchantName: String? = extras.getString("merchantName")
topLevelOrigin
המקור של המוכר ללא הסכמה (המקור ללא הסכמה של
הקשר גלישה ברמה העליונה). לדוגמה, https://mystore.com/checkout
הוא
הועברה בתור mystore.com
.
val topLevelOrigin: String? = extras.getString("topLevelOrigin")
topLevelCertificateChain
שרשרת האישורים של המוכר (שרשרת האישורים של הרמה העליונה)
בהתאם להקשר). הערך null מופיע ב-localhost ובדיסק, ושניהם מאובטחים.
הקשרים ללא אישורי SSL. כל Parcelable
הוא חבילה עם
מפתח certificate
וערך מערך של בייט.
val topLevelCertificateChain: Array<Parcelable>? =
extras.getParcelableArray("topLevelCertificateChain")
val list: List<ByteArray>? = topLevelCertificateChain?.mapNotNull { p ->
(p as Bundle).getByteArray("certificate")
}
paymentRequestOrigin
המקור ללא סכמות של הקשר הגלישה ב-iframe שהפעיל את ה-constructor של new
PaymentRequest(methodData, details, options)
ב-JavaScript. אם
הופעל מההקשר ברמה העליונה, אז הערך של
שווה לערך של הפרמטר 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
שבו "push-payment" צריכות להיות משויכות אל
על מצב העסקה. אתרים של מוכרים ישתמשו בשדה הזה כדי לשלוח שאילתה על
"push-payment" אפליקציות למצב של עסקאות מחוץ למסגרת.
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()
צריך לציין שני פרמטרים כתוספות Intent:
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
השלב האחרון הוא לאמת את אישור החתימה של המתקשר כדי לאשר החבילה של השיחות כוללת את החתימה הנכונה.
שלב 4: מאמתים את אישור החתימה של המתקשר
אפשר לבדוק את שם החבילה של המתקשר עם Binder.getCallingUid()
ב
IS_READY_TO_PAY
, ועם Activity.getCallingPackage()
ב-PAY
. כדי
לאמת שהמתקשר הוא הדפדפן הרצוי,
יש לבדוק את אישור החתימה שלו ולוודא שהוא תואם לתקן
עם ערך מסוים.
אם אתם מטרגטים רמת API של 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 יש
אישור חתימה יחידה.) אפליקציות עם אישורי חתימה מרובים לא יכולות
לסובב אותן.
אם אתם צריכים לתמוך ב-API ברמה 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) } }