Android多種支付方式的實(shí)現(xiàn)示例
1.場(chǎng)景
App 的支付流程,添加多種支付方式,不同的支付方式,對(duì)應(yīng)的操作不一樣,有的會(huì)跳轉(zhuǎn)到一個(gè)新的webview,有的會(huì)調(diào)用系統(tǒng)瀏覽器,有的會(huì)進(jìn)去一個(gè)新的表單頁(yè)面,等等。
并且可以添加的支付方式也是不確定的,由后臺(tái)動(dòng)態(tài)下發(fā)。
如下圖所示:
根據(jù)上圖 ui 理一下執(zhí)行流程:
- 點(diǎn)擊不同的添加支付方式 item。
- 進(jìn)入相對(duì)應(yīng)的添加支付方式流程(表單頁(yè)面、webview、彈框之類(lèi)的)。
- 在第三方回調(diào)里面根據(jù)不同的支付方式執(zhí)行不同的操作。
- 調(diào)用后臺(tái)接口查詢(xún)添加是否成功。
- 根據(jù)接口結(jié)果展示不同的成功或者失敗的ui.
2.以前的實(shí)現(xiàn)方式
用一個(gè) Activity 承載,上述所有的流程都在 Activity 中。Activity 包含了列表展示、多種支付方式的實(shí)現(xiàn)和 ui。
偽代碼如下:
class AddPaymentListActivity : AppCompatActivity(R.layout.activity_add_card) { private val addPaymentViewModel : AddPaymentViewModel = ... override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) addPaymentViewModel.checkPaymentStatusLiveData.observer(this) { isSuccess -> // 從后臺(tái)結(jié)果判斷是否添加成功 if (isSuccess) { addCardSuccess(paymentType) } else { addCardFailed(paymentType) } } } private fun clickItem(paymentType: PaymentType) { when (paymentType) { PaymentType.ADD_GOOGLE_PAY -> //執(zhí)行添加谷歌支付流程 PaymentType.ADD_PAY_PEL-> //執(zhí)行添加PayPel支付流程 PaymentType.ADD_ALI_PAY-> //執(zhí)行添加支付寶支付流程 PaymentType.ADD_STRIPE-> //執(zhí)行添加Stripe支付流程 } } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) when (resultCode) { PaymentType.ADD_GOOGLE_PAY -> { // 根據(jù)第三方回調(diào)的結(jié)果,拿到key // 根據(jù)key調(diào)用后臺(tái)的Api接口查詢(xún)是否添加成功 } PaymentType.ADD_PAY_PEL -> // 同上 // ... } } private fun addCardSuccess(paymentType: PaymentType){ when (paymentType) { PaymentType.ADD_GOOGLE_PAY -> // 添加對(duì)應(yīng)的支付方式成功,展示成功的ui,然后執(zhí)行下一步操作 PaymentType.ADD_PAY_PEL-> // 同上 // ... } } private fun addCardFailed(paymentType: PaymentType){ when (paymentType) { PaymentType.ADD_GOOGLE_PAY -> // 添加對(duì)應(yīng)的支付方式失敗,展示失敗的ui PaymentType.ADD_PAY_PEL-> // 同上 // ... } } enum class PaymentType { ADD_GOOGLE_PAY, ADD_PAY_PEL, ADD_ALI_PAY, ADD_STRIPE } }
雖然看起來(lái)根據(jù) paymentType 來(lái)判斷,邏輯條理也還過(guò)得去,但是實(shí)際上復(fù)雜度遠(yuǎn)遠(yuǎn)不止如此。
• 不同的支付方式跳轉(zhuǎn)的頁(yè)面相差很大。
• 結(jié)果的回調(diào)獲取也相差很大,并不是所有的都在onActivityResult中。
• 成功和失敗實(shí)際上也不能統(tǒng)一來(lái)處理,里面包含很多的if…else…判斷。
• 如果支付方式是后臺(tái)動(dòng)態(tài)下發(fā)的,處理起來(lái)判斷邏輯就更多了。
此外,最大的問(wèn)題:擴(kuò)展性問(wèn)題。
當(dāng)新來(lái)一種支付方式,例如微信支付之類(lèi)的,改動(dòng)代碼就很大了,基本就是將整個(gè)Activity中的代碼都要改動(dòng)??梢哉f(shuō)上面這種方式的可擴(kuò)展性為零,就是簡(jiǎn)單的將代碼都揉在一起。
3.優(yōu)化后的代碼
要想實(shí)現(xiàn)高內(nèi)聚低耦合,最簡(jiǎn)單的就是套用常見(jiàn)的設(shè)計(jì)模式,回想一下,發(fā)現(xiàn)策略模式+簡(jiǎn)單工廠模式非常這種適合這種場(chǎng)景。
先看下優(yōu)化后的代碼:
class AddPlatformActivity : BaseActivity() { private var addPayPlatform: IAddPayPlatform? = null private fun addPlatform(payPlatform: String) { // 將后臺(tái)返回的支付平臺(tái)字符串變成枚舉類(lèi) val platform: PayPlatform = getPayPlatform(payPlatform) ?: return addPayPlatform = AddPayPlatformFactory.getCurrentPlatform(this, platform) addPayPlatform?.getLoadingLiveData()?.observe(this@AddPlatformActivity) { showLoading -> if (showLoading) startLoading() else stopLoading() } addPayPlatform?.addPayPlatform(AddCardParameter(platform)) } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) // 將onActivityResult的回調(diào)轉(zhuǎn)接到需要監(jiān)聽(tīng)的策略類(lèi)里面 addPayPlatform?.thirdAuthenticationCallback(requestCode, resultCode, data) } }
4.策略模式
意圖: 定義一系列的算法,把它們一個(gè)個(gè)封裝起來(lái), 并且使它們可相互替換。
主要解決: 在有多種算法相似的情況下,使用if…else所帶來(lái)的復(fù)雜和難以維護(hù)。
何時(shí)使用: 一個(gè)系統(tǒng)有許多許多類(lèi),而區(qū)分它們的只是他們直接的行為。
如何解決: 將這些算法封裝成一個(gè)一個(gè)的類(lèi),任意地替換。
關(guān)鍵代碼: 實(shí)現(xiàn)同一個(gè)接口。
**優(yōu)點(diǎn): **
1、算法可以自由切換。
2、避免使用多重條件判斷。
3、擴(kuò)展性良好。
缺點(diǎn):
1、策略類(lèi)會(huì)增多。
2、所有策略類(lèi)都需要對(duì)外暴露。
**使用場(chǎng)景: **
1、如果在一個(gè)系統(tǒng)里面有許多類(lèi),它們之間的區(qū)別僅在于它們的行為,那么使用策略模式可以動(dòng)態(tài)地讓一個(gè)對(duì)象在許多行為中選擇一種行為。
2、一個(gè)系統(tǒng)需要?jiǎng)討B(tài)地在幾種算法中選擇一種。
3、如果一個(gè)對(duì)象有很多的行為,如果不用恰當(dāng)?shù)哪J?,這些行為就只好使用多重的條件選擇語(yǔ)句來(lái)實(shí)現(xiàn)。
5.需要實(shí)現(xiàn)的目標(biāo)
5.1 解耦宿主 Activity
現(xiàn)在宿主Activity中代碼太重了,包含多種支付方式實(shí)現(xiàn),還有列表ui的展示,網(wǎng)絡(luò)請(qǐng)求等。
現(xiàn)在目標(biāo)是將 Activity 中的代碼拆分開(kāi)來(lái),讓宿主 Activity 變得小而輕。
如果產(chǎn)品說(shuō)新增一種支付方式,只需要改動(dòng)很少的代碼,就可以輕而易舉的實(shí)現(xiàn)。
5.2 抽取成獨(dú)立的模塊
因?yàn)楣局杏锌赡艽嬖诙鄠€(gè)項(xiàng)目,支付模塊的分層應(yīng)該處于可復(fù)用的層級(jí),以后很有可能將其封裝成一個(gè)獨(dú)立的 mouble,給不同的項(xiàng)目使用。
現(xiàn)在代碼全在 Activity 中,以后若是抽取 mouble 的話,相當(dāng)于整個(gè)需求重做。
5.3 組件黑盒
"組件黑盒"這個(gè)名詞是我自己的一個(gè)定義。大致意思:
將一個(gè) View 或者一個(gè)類(lèi)進(jìn)行高度封裝,盡可能少的暴露public方法給外部使用,自成一體。
業(yè)務(wù)方在使用時(shí),可以直接黑盒使用某個(gè)業(yè)務(wù)組件,不必關(guān)心其中的邏輯。
業(yè)務(wù)方只需要一個(gè)簡(jiǎn)單的操作,例如點(diǎn)擊按鈕調(diào)用方法,然后邏輯都在組件內(nèi)部實(shí)現(xiàn),組件內(nèi)處理外部事件的所有操作,例如:Loading、請(qǐng)求網(wǎng)絡(luò)、成功或者失敗。
業(yè)務(wù)方都不需要知道組件內(nèi)部的操作,做到宿主和組件的隔離。
當(dāng)然這種處理也是要分場(chǎng)景考慮的,其中一個(gè)重點(diǎn)就是這個(gè)組件是偏業(yè)務(wù)還是偏功能,也就是是否要將業(yè)務(wù)邏輯統(tǒng)一包進(jìn)組件,想清楚這個(gè)問(wèn)題后,才能去開(kāi)發(fā)一個(gè)業(yè)務(wù)組件。
因?yàn)樘砑又Ц斗绞绞且粋€(gè)偏業(yè)務(wù)的功能,我的設(shè)計(jì)思路是:
外部 Activity 點(diǎn)擊添加對(duì)應(yīng)的支付方式,將支付方式的枚舉類(lèi)型和支付方式有關(guān)的參數(shù)通過(guò)傳遞,然后不同的策略類(lèi)組件執(zhí)行自己的添加邏輯,再通過(guò)一層回調(diào)將第三方支付的回調(diào)從 Activity 中轉(zhuǎn)接過(guò)來(lái),每個(gè)策略類(lèi)內(nèi)部處理自己的回調(diào)操作,具體的策略類(lèi)自己維護(hù)成功或者失敗的ui。摘自https://xuyisheng.top/author/xuyisheng/
6.具體實(shí)現(xiàn)
6.1 定義頂層策略接口
interface IAddPayPlatform { fun addPayPlatform(param: AddCardParameter) fun thirdAuthenticationCallback(requestCode: Int?, resultCode: Int?, data: Intent?) fun addCardFailed(message: String?) fun addCardSuccess() }
6.2 通用支付參數(shù)類(lèi)
open class AddCardParameter(val platform: PayPlatform) class AddStripeParameter(val card: Card, val setPrimaryCard: Boolean, platform: PayPlatform) : AddCardParameter(platform = PayPlatform.Stripe)
因?yàn)橛泻芏喾N添加支付方式,不同的支付方式對(duì)應(yīng)的參數(shù)都不一樣。
所以先創(chuàng)建一個(gè)通用的卡片參數(shù)基類(lèi)AddCardParameter, 不同的支付方式去實(shí)現(xiàn)不同的具體參數(shù)。這樣的話策略接口就可以只要寫(xiě)一個(gè)添加卡片的方法addPayPlatform(param: AddCardParameter)。
6.3 Loading 的處理
因?yàn)槲蚁雽?shí)現(xiàn)的是黑盒組件的效果,所有添加卡片的loading也是封裝在每一個(gè)策略實(shí)現(xiàn)類(lèi)里面的。
Loading的出現(xiàn)和消失這里有幾種常見(jiàn)的實(shí)現(xiàn)方式:
• 傳遞BaseActivity的引用,因?yàn)槲业膌oading有關(guān)的方法是放在BaseActivity中,這種方式簡(jiǎn)單但是會(huì)耦合BaseActivity。
• 使用消息事件總線,例如EventBus之類(lèi)的,這種方式解耦強(qiáng),但是消息事件不好控制,還要添加多余的依賴(lài)庫(kù)。
• 使用LiveData,在策略的通用接口中添加一個(gè)方法返回Loading的LiveData, 讓宿主Activity自己去實(shí)現(xiàn)。
interface IAddPayPlatform { // ... fun getLoadingLiveData(): LiveData<Boolean>? }
6.4 提取BaseAddPayStrategy
因?yàn)槊恳粋€(gè)添加卡的策略會(huì)存在很多相同的代碼,這里我抽取一個(gè)BaseAddPayStrategy來(lái)存放模板代碼。
需要實(shí)現(xiàn)黑盒組件的效果,宿主Activity中都不需要去關(guān)注添加支付方式是不是存在網(wǎng)絡(luò)請(qǐng)求這一個(gè)過(guò)程,所以網(wǎng)絡(luò)請(qǐng)求也分裝在每一個(gè)策略實(shí)現(xiàn)類(lèi)里面。
abstract class BaseAddPayStrategy(val activity: AppCompatActivity, val platform: PayPlatform) : IAddPayPlatform { private val loadingLiveData = SingleLiveData<Boolean>() protected val startActivityIntentLiveData = SingleLiveData<Intent>() override fun getLoadingLiveData(): LiveData<Boolean> = loadingLiveData protected fun startLoading() = loadingLiveData.setValue(true) protected fun stopLoading() = loadingLiveData.setValue(false) private fun reloadWallet() { startLoading() // 添加卡片完成后,重新刷新錢(qián)包數(shù)據(jù) } override fun addCardSuccess() { reloadWallet() } override fun addCardFailed(message: String?) { stopLoading() if (isEWalletPlatform(platform)) showAddEWalletFailedView() else showAddPhysicalCardFailedView(message) } /** * 添加實(shí)體卡片失敗展示ui */ private fun showAddPhysicalCardFailedView(message: String?) { showSaveErrorDialog(activity, message) } /** * 添加實(shí)體卡片成功展示ui */ private fun showAddPhysicalCardSuccessView() { showCreditCardAdded(activity) { activity.setResult(Activity.RESULT_OK) activity.finish() } } private fun showAddEWalletSucceedView() { // 添加電子錢(qián)包成功后的執(zhí)行 activity.setResult(Activity.RESULT_OK) activity.finish() } private fun showAddEWalletFailedView() { // 添加電子錢(qián)包失敗后的執(zhí)行 } // ---默認(rèn)空實(shí)現(xiàn),有些支付方式不需要這些方法--- override fun thirdAuthenticationCallback(requestCode: Int?, resultCode: Int?, data: Intent?) = Unit override fun getStartActivityIntent(): LiveData<Intent> = startActivityIntentLiveData }
6.5 具體的策略類(lèi)實(shí)現(xiàn)
通過(guò)傳遞過(guò)來(lái)的AppCompatActivity引用獲取添加卡片的ViewModel實(shí)例AddPaymentViewModel,然后通過(guò)AddPaymentViewModel去調(diào)用網(wǎng)絡(luò)請(qǐng)求查詢(xún)添加卡片是否成功。
class AddXXXPayStrategy(activity: AppCompatActivity) : BaseAddPayStrategy(activity, PayPlatform.XXX) { protected val addPaymentViewModel: AddPaymentViewModel by lazy { ViewModelProvider(activity).get(AddPaymentViewModel::class.java) } init { addPaymentViewModel.eWalletAuthorizeLiveData.observeState(activity) { onSuccess { addCardSuccess()} onError { addCardFailed(it.detailed) } } } override fun thirdAuthenticationCallback(requestCode: Int?, resultCode: Int?, result: Intent?) { val uri: Uri = result?.data ?: return if (uri.host == "www.xxx.com") { uri.getQueryParameter("transactionId")?.let { addPaymentViewModel.confirmEWalletAuthorize(platform.name, it) } } } override fun addPayPlatform(param: AddCardParameter) { startLoading() addPaymentViewModel.addXXXCard(param) } }
7.簡(jiǎn)單工廠進(jìn)行優(yōu)化
因?yàn)槲也幌朐贏ctivity中去引用每一個(gè)具體的策略類(lèi),只想引用抽象接口類(lèi)IAddPayPlatform, 這里通過(guò)一個(gè)簡(jiǎn)單工廠來(lái)優(yōu)化。
fun setCurrentPlatform(activity: AppCompatActivity, payPlatform: PayPlatform): IAddPayPlatform? { return when (payPlatform) { PayPlatform.STRIPE -> AddStripeStrategy(activity) PayPlatform.PAYPAL -> AddPayPalStrategy(activity) PayPlatform.LINEPAY -> AddLinePayStrategy(activity) PayPlatform.GOOGLEPAY -> AddGooglePayStrategy(activity) PayPlatform.RAPYD -> AddRapydStrategy(activity) else -> null } }
8.再增加一種支付方式
如果再增加一種支付方式,宿主Activity中的代碼都可以不要改動(dòng),只需要新建一個(gè)新的策略類(lèi),實(shí)現(xiàn)頂層策略接口即可。
這樣,不管是刪除還是新增一種支付方式,維護(hù)起來(lái)就很容易了。
策略模式的好處就顯而易見(jiàn)了。
以上就是Android多種支付方式的實(shí)現(xiàn)示例的詳細(xì)內(nèi)容,更多關(guān)于Android支付方式的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android 對(duì)話框(Dialog)大全示例(建立你自己的對(duì)話框)
android開(kāi)發(fā)中,對(duì)話框的使用還是很平凡的,本篇文章介紹了Android 對(duì)話框的實(shí)例,詳細(xì)的介紹了多種對(duì)話框的方法,有興趣的可以了解一下。2016-11-11Android Studio里如何使用lambda表達(dá)式
這篇文章主要介紹了Android Studio里如何使用lambda表達(dá)式,需要的朋友可以參考下2017-05-05Android開(kāi)發(fā)中總結(jié)的Adapter工具類(lèi)【附完整源碼下載】
這篇文章主要介紹了Android開(kāi)發(fā)中總結(jié)的Adapter工具類(lèi),簡(jiǎn)單說(shuō)明了Adapter的功能,并結(jié)合實(shí)例形式分析了Adapter工具類(lèi)的相關(guān)使用方法,并附帶完整源碼供讀者下載參考,需要的朋友可以參考下2017-11-11Android使用文件進(jìn)行數(shù)據(jù)存儲(chǔ)的方法
這篇文章主要介紹了Android使用文件進(jìn)行數(shù)據(jù)存儲(chǔ)的方法,較為詳細(xì)的分析了Android基于文件實(shí)現(xiàn)數(shù)據(jù)存儲(chǔ)所涉及的相關(guān)概念與使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-09-09老項(xiàng)目遷移AndroidStudio3.0遇到的坑
給大家分享了老的項(xiàng)目以及程序遷移到了AndroidStudio3.0遇到的坑和問(wèn)題解決辦法,希望給你做個(gè)參考。2017-11-11Android通知欄增加快捷開(kāi)關(guān)的功能實(shí)現(xiàn)教程
對(duì)于Android來(lái)說(shuō)其中一項(xiàng)很方便的操作便是下拉菜單,下拉菜單欄可以快捷打開(kāi)某項(xiàng)設(shè)置,這篇文章主要給大家介紹了關(guān)于Android通知欄增加快捷開(kāi)關(guān)的功能實(shí)現(xiàn),需要的朋友可以參考下2023-01-01