מודול תשלומים מרכזי למיני-יישומים בסופר-אפ
מיני-אפליקציות בתוך סופר-אפ מיישמות לעיתים קרובות תשלומים באופן עצמאי, מה שמוביל לפיצול בהיקף PCI DSS, מאמצי אינטגרציה כפולים וחוויית משתמש לא עקבית. אנו פותרים זאת עם מודול תשלומים מאוחד שמרכז את עיבוד העסקאות ביישום המעטפת באמצעות ארכיטקטורת גשר, מבודד את לוגיקת התשלום ומספק היסטוריית תשלומים אחת לכל המיני-יישומים.
לצוות שלנו ניסיון של למעלה מ-5 שנים בבניית מערכות תשלום; סיפקנו מודולי תשלום מרכזיים ל-10+ סופר-אפים בתחום הקמעונאות והפינטק. התוצאה: זמן האינטגרציה לכל מיני-יישום חדש מצטמצם ל-3 ימים, ועלויות התחזוקה של היקף התשלום יורדות ב-60%. הזמינו ביקורת על הארכיטקטורה של הסופר-אפ שלכם — נכין תוכנית אישית.
אינטראקציה אופיינית בין המעטפת למיני-יישום
הסופר-אפ בנוי על WebView (או React Native / Flutter WebView) עבור מיני-יישומים. גשר התשלום מיושם באמצעות ממשק הרשום ב-WebView. באנדרואיד אנו משתמשים ב-addJavascriptInterface, וב-iOS — ב-WKScriptMessageHandler. המיני-יישום קורא למסך התשלום המקורי של יישום המעטפת דרך ממשק ה-API של הגשר.
// Android Shell App: регистрация bridge-метода
webView.addJavascriptInterface(PaymentBridge(this), "NativePayment")
class PaymentBridge(private val activity: AppCompatActivity) {
@JavascriptInterface
fun initiatePayment(requestJson: String) {
val request = PaymentRequest.fromJson(requestJson)
activity.runOnUiThread {
PaymentBottomSheet.show(activity, request) { result ->
val js = "window.onPaymentResult(${result.toJson()})"
webView.evaluateJavascript(js, null)
}
}
}
@JavascriptInterface
fun getSavedPaymentMethods(): String {
return paymentRepository.getSavedMethods().toJson()
}
} // Мини-программа (JS/React): вызов платёжного модуля
async function checkout(amount, orderId) {
return new Promise((resolve, reject) => {
window.onPaymentResult = (result) => {
if (result.status === 'success') resolve(result);
else reject(result.error);
};
NativePayment.initiatePayment(JSON.stringify({
amount,
currency: 'RUB',
orderId,
miniProgramId: 'com.yourshop.miniapp'
}));
});
}ב-iOS אותה תבנית משתמשת ב-// Android Shell App: регистрация bridge-метода webView.addJavascriptInterface(PaymentBridge(this), "NativePayment") class PaymentBridge(private val activity: AppCompatActivity) { @JavascriptInterface fun initiatePayment(requestJson: String) { val request = PaymentRequest.fromJson(requestJson) activity.runOnUiThread { PaymentBottomSheet.show(activity, request) { result -> val js = "window.onPaymentResult(${result.toJson()})" webView.evaluateJavascript(js, null) } } } @JavascriptInterface fun getSavedPaymentMethods(): String { return paymentRepository.getSavedMethods().toJson() } } :
class PaymentMessageHandler: NSObject, WKScriptMessageHandler {
func userContentController(
_ controller: WKUserContentController,
didReceive message: WKScriptMessage
) {
guard message.name == "initiatePayment",
let body = message.body as? [String: Any] else {
return
}
let request = PaymentRequest(from: body)
PaymentCoordinator.shared.present(request: request, from: hostViewController) { result in
let js = "window.onPaymentResult(\(result.jsonString))"
webView.evaluateJavaScript(js)
}
}
}
// Регистрация в WKWebViewConfiguration
configuration.userContentController.add(PaymentMessageHandler(), name: "initiatePayment") מדוע ריכוזיות מפחיתה עלויות תחזוקה
| היבט | מבוזר (תשלום בכל מיני-יישום) | מרכזי (מודול יחיד במעטפת) |
|---|---|---|
| היקף PCI DSS | מתרחב לכל מיני-יישום | מוגבל ליישום המעטפת — ביקורת אחת |
| עדכוני מפתחות/אישורים | דורש פריסה של כל המיני-יישומים | רק יישום המעטפת צריך עדכון |
| היסטוריית תשלומים מאוחדת | אין | זמינה במעטפת לכל המיני-יישומים |
| ממשק משתמש/חוויית תשלום | שונה לכל מיני-יישום | עקבי, מגביר אמון |
| זמן קליטת מיני-יישום חדש | 1–3 שבועות | 1–3 ימים (רק קריאת API לגשר) |
הריכוזיות מקצצת את המאמץ פי 5–10 לכל מיני-יישום וממזערת סיכונים במהלך ביקורות PCI DSS.
השוואת ספקים: מתי יש צורך בניתוב
| תרחיש | ספק יחיד | ספקים מרובים עם ניתוב |
|---|---|---|
| כל המיני-יישומים משתמשים בסולק אחד | כן, מספיק | מיותר |
| קטגוריות מוצרים שונות (מרקטפלייס מול פיננסים) | אין גמישות | כן, ניתוב לפי miniProgramId |
| גיבוי במקרה של כשל ספק | לא | כן, מעבר אוטומטי |
| אופטימיזציית עמלות | לא | בחירת העמלה הנמוכה ביותר |
מתאם התשלומים המאוחד
הרכיב המרכזי הוא // Мини-программа (JS/React): вызов платёжного модуля async function checkout(amount, orderId) { return new Promise((resolve, reject) => { window.onPaymentResult = (result) => { if (result.status === 'success') resolve(result); else reject(result.error); }; NativePayment.initiatePayment(JSON.stringify({ amount, currency: 'RUB', orderId, miniProgramId: 'com.yourshop.miniapp' })); }); } ביישום המעטפת. הוא יודע: אילו אמצעי תשלום זמינים (כרטיסים, Apple Pay / Google Pay, SBP, יתרת סופר-אפ), אילו כרטיסים שמורים למשתמש, ואיזה ספק מטפל בעסקה (אחד או כמה).
// Android: PaymentCoordinator как singleton в Shell
class PaymentCoordinator private constructor() {
companion object {
val shared = PaymentCoordinator()
}
private val activeProviders = mutableMapOf<String, PaymentProvider>()
fun registerProvider(id: String, provider: PaymentProvider) {
activeProviders[id] = provider
}
fun initiatePayment(request: PaymentRequest, callback: (PaymentResult) -> Unit) {
val provider = selectProvider(request)
provider.process(request, callback)
}
private fun selectProvider(request: PaymentRequest): PaymentProvider {
// Логика маршрутизации: разные мини-программы могут использовать разных провайдеров
return activeProviders[request.miniProgramId] ?: activeProviders["default"] ?: throw IllegalStateException("No payment provider registered")
}
}ניתוב לפי WKScriptMessageHandler מאפשר לסופר-אפ אחד לעבוד עם מספר סולקים — אחד למרקטפלייס, אחר לשירותי משלוחים, ושלישי למוצרים פיננסיים.
אמצעי תשלום שמורים
המשתמש מוסיף כרטיס פעם אחת — והוא זמין בכל המיני-יישומים. אסימוני כרטיס חייבים להישמר באופן מרכזי:
data class SavedPaymentMethod(
val id: String,
val type: PaymentMethodType, // CARD, SBP, APPLE_PAY
val displayName: String, // "Visa •••• 4242"
val providerToken: String, // токен конкретного провайдера (не PAN!)
val isDefault: Boolean
)
data class PaymentRequest(
val amount: Long, // в копейках
val currency: String,
val orderId: String,
val miniProgramId: String,
val miniProgramName: String, // "Доставка YourShop" — для отображения пользователю
val allowedMethods: List<PaymentMethodType>? = null // null = все доступные
)class PaymentMessageHandler: NSObject, WKScriptMessageHandler { func userContentController( _ controller: WKUserContentController, didReceive message: WKScriptMessage ) { guard message.name == "initiatePayment", let body = message.body as? [String: Any] else { return } let request = PaymentRequest(from: body) PaymentCoordinator.shared.present(request: request, from: hostViewController) { result in let js = "window.onPaymentResult(\(result.jsonString))" webView.evaluateJavaScript(js) } } } // Регистрация в WKWebViewConfiguration configuration.userContentController.add(PaymentMessageHandler(), name: "initiatePayment") הוא אסימון מ-Stripe (PaymentCoordinator), CloudPayments, או ספק אחר. PAN לעולם לא נשמר במכשיר. סנכרון בין מכשירים מטופל דרך השרת המקושר ל-// Android: PaymentCoordinator как singleton в Shell class PaymentCoordinator private constructor() { companion object { val shared = PaymentCoordinator() } private val activeProviders = mutableMapOf<String, PaymentProvider>() fun registerProvider(id: String, provider: PaymentProvider) { activeProviders[id] = provider } fun initiatePayment(request: PaymentRequest, callback: (PaymentResult) -> Unit) { val provider = selectProvider(request) provider.process(request, callback) } private fun selectProvider(request: PaymentRequest): PaymentProvider { // Логика маршрутизации: разные мини-программы могут использовать разных провайдеров return activeProviders[request.miniProgramId] ?: activeProviders["default"] ?: throw IllegalStateException("No payment provider registered") } } .
ממשק תשלום מאוחד
ה-Bottom Sheet עם אמצעי התשלום עקבי בכל הסופר-אפ. מיני-יישומים שונים לא יכולים לשנות את המראה שלו — זה קריטי לאמון המשתמש.
מסך התשלום המאוחד מספק:
- רשימת כרטיסים שמורים עם בחירה;
- הוספת כרטיס חדש (באמצעות SDK של הספק או הזנת כרטיס מותאמת);
- Apple Pay / Google Pay בהקשה אחת;
- SBP עם קישור עמוק לאפליקציית הבנק;
- הצגת הסכום ושם המיני-יישום (מקור הבקשה).
טיפול בשגיאות וניסיונות חוזרים
תשלום הוא פעולה קריטית. יישום המעטפת מטפל נכון בכשלים חלקיים:
- פסק זמן של ספק → הצגת "סטטוס התשלום נבדק", התחלת בדיקת סטטוס.
- הפניית 3DS → פתיחת WebView בתוך ה-bottom sheet, מבלי להוציא את המשתמש מהאפליקציה.
- בקשה כפולה →
miniProgramIdאידמפוטנטי בצד השרת.
אנו מבטיחים טיפול נכון בכל התרחישים, כולל ביטול על ידי המשתמש ואובדן חיבור.
טעויות נפוצות ביישום עצמי
- חוסר אידמפוטנטיות: קריאת API חוזרת לתשלום יוצרת תשלום כפול. פתרון: שימוש ב-
data class SavedPaymentMethod( val id: String, val type: PaymentMethodType, // CARD, SBP, APPLE_PAY val displayName: String, // "Visa •••• 4242" val providerToken: String, // токен конкретного провайдера (не PAN!) val isDefault: Boolean ) data class PaymentRequest( val amount: Long, // в копейках val currency: String, val orderId: String, val miniProgramId: String, val miniProgramName: String, // "Доставка YourShop" — для отображения пользователю val allowedMethods: List<PaymentMethodType>? = null // null = все доступные )ייחודי ברמת המודול. - שמירת PAN במכשיר: הפרה ישירה של PCI DSS. השתמשו תמיד באסימוני ספק.
- ממשק מבודד לכל מיני-יישום: המשתמש מאבד אמון. bottom sheet מאוחד הוא חובה.
מה כלול
- ניתוח ארכיטקטורת הסופר-אפ שלכם ומספר המיני-יישומים.
- עיצוב ממשק ה-API של הגשר (iOS/Android) והחוזים עבור
providerToken/pm_xxx. - יישום
userIdעם תמיכה בספק אחד או כמה. - פיתוח ממשק תשלום מאוחד (Bottom Sheet) עם Apple Pay, Google Pay, SBP, כרטיסים.
- אינטגרציה של טוקניזציה ואמצעי תשלום שמורים.
- בדיקות עם שני מיני-יישומים פיילוט.
- תיעוד ממשק ה-API של הגשר למפתחי מיני-יישומים.
- תמיכה במהלך אישור App Store ו-Google Play (בדיקות In-App Purchase, ATT).
לוח זמנים משוער
3–6 שבועות — מניתוח ועד השקה בשני מיני-יישומים. העלות מחושבת באופן אישי לאחר ביקורת הארכיטקטורה שלכם. לקבלת תוכנית מקדימה והערכת עלות, צרו קשר.
המהנדסים שלנו מחזיקים בהסמכות פיתוח ל-iOS ואנדרואיד, מה שמבטיח עמידה בהנחיות העדכניות של App Store Review ו-Google Play. אנו מבטיחים שהסופר-אפ שלכם יעבור אישור בניסיון הראשון.







