再版說明
由于想把本庫(kù)發(fā)布到【jitpack】 上去以方便使用者直接可以進(jìn)行g(shù)radle依賴,但想要能放上jitpack上,需要一個(gè)完整的項(xiàng)目才能成功,可參考教程【將自己寫的庫(kù)發(fā)布到JitPack】,而之前并沒有考慮到這一點(diǎn),庫(kù)【CommonPayLib】與Demo工程【WxAlipayDemo】是分開寫的,而不能單獨(dú)將一個(gè)Android庫(kù)發(fā)布到JitPack上,故打算重新開一個(gè)完整項(xiàng)目,并與其他開源庫(kù)一樣,直接在項(xiàng)目中通用支付庫(kù)以library的形式存在,這樣才能發(fā)布到j(luò)itpack上去,本項(xiàng)目的地址為【CommonPaySdk】。
依賴步驟
因?yàn)楸編?kù)已經(jīng)發(fā)布到JitPack上去了,所以APP的使用可以直接進(jìn)行g(shù)radle依賴
依賴步驟:
1、在項(xiàng)目的根目錄下的build.gradle中添加JitPack的maven 倉(cāng)庫(kù)
allprojects{
repositories{
? ?...
maven{url'https://jitpack.io'}
? ? ? }
}
2、在項(xiàng)目的app目錄(moudle)的build.gradle文件中添加如下代碼:
dependencies{
...
compile'com.github.feer921:CommonPaySdk:1.1'//目錄為1.1版本
}
再更改了*.gradle文件時(shí) AndroidStudio會(huì)提示需要同步(編譯),如果順利的話,本庫(kù)則應(yīng)該會(huì)依賴成功。
使用方法及步驟
本庫(kù)的核心類為【PayEntryActivity】,之所以說是微信與支付寶支付的通用庫(kù),其調(diào)起微信/支付寶的以及接收支付的結(jié)果響應(yīng)邏輯全在這個(gè)類里面,一個(gè)類通用兩種支付方式,看過別人寫支付功能時(shí),微信支付的功能就按照官方的Demo寫一個(gè)WxPayEntryActivity,支付寶的就另寫一個(gè)邏輯,這樣會(huì)造成需要分開來處理,不太方便,而且如果好幾個(gè)項(xiàng)目都要集成支付功能,每個(gè)項(xiàng)目里面都要寫一樣的代碼,維護(hù)也不爽,因而很有必要寫這樣一個(gè)通用庫(kù)工程到處可以使用。
1、步驟一
因?yàn)槲⑿胖Ц禨DK需要一個(gè)appid,該appid為在微信支付開放平臺(tái)為所要申請(qǐng)開通支付功能的app生成的
所以必要先給本庫(kù)的中的【CommonPayConfig】的變量【W(wǎng)X_APP_ID】賦上你的APP在開放平臺(tái)上的appid的值,一般寫在你的APP里的application類下,如:
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
...
CommonPayConfig.WX_APP_ID="wxb4bbf0651d312ab6";
}
..........
}
2、準(zhǔn)備選擇支付方式的界面(APP自備),以及解析服務(wù)端返回的支付訂單信息
一般來講APP上在付款前都會(huì)提供一個(gè)供用戶選擇使用何種支付方式的界面,如下:
用戶選擇相應(yīng)的支付方式后,用戶點(diǎn)擊支付時(shí),APP會(huì)提交<支付方式、支付相關(guān)的數(shù)據(jù)>給APP的后臺(tái)服務(wù)器,后臺(tái)服務(wù)器則依支付方式對(duì)應(yīng)的去微信支付服務(wù)器或者支付寶支付服務(wù)器獲取相關(guān)的預(yù)支付訂單信息(該信息數(shù)據(jù)需要滿足各自官網(wǎng)上的示例數(shù)據(jù)信息)返回給APP,示例如下,
返回的微信訂單信息數(shù)據(jù)示例(參考別人公司的):
{
"status": "1",
"msg": "請(qǐng)求成功",
"data": {
"orderNo": "551515515151551",//這個(gè)字段是APP服務(wù)器自己加的,可以用來讓APP再去APP服務(wù)器上查詢一遍是否本次訂單支付成功
"appid": "wxbee9e8888665656",
"partnerid": "01234598565",
"prepayid": "wx20160122151438e832d724940443134124",
"noncestr": "uphct75fl9qexvpeeiy8k0cdzo13h7ap",
"timestamp": "1453446877",
"package": "Sign=WXPay",
"paySign": "1C71DBC9F32B41723165554987DD0F7"
}
返回的支付寶訂單信息(參考別人公司的):
{
"status": "1",
"msg": "請(qǐng)求成功",
"data": {
"orderNo": "2016011310211365556",//這個(gè)字段是APP服務(wù)器自己加的
"payInfo": "partner="2088101568358171"&seller_id="xxx@alipay.com"&out_trade_no="0819145412-6177"&subject="測(cè)試"&body="測(cè)試測(cè)試"&total_fee="0.01"?ify_url="http://notify.msp.hk/notify.htm"&service="mobile.securitypay.pay"&payment_type="1"&_input_charset="utf-8"&it_b_pay="30m"&sign="lBBK%2F0w5LOajrMrji7DUgEqNjIhQbidR13GovA5r3TgIbNqv231yC1NksLdw%2Ba3JnfHXoXuet6XNNHtn7VE%2BeCoRO1O%2BR1KugLrQEZMtG5jmJIe2pbjm%2F3kb%2FuGkpG%2BwYQYI51%2BhA3YBbvZHVQBYveBqK%2Bh8mUyb7GM1HxWs9k4%3D"&sign_type="RSA"}
}
以上參考的從服務(wù)端返回的訂單數(shù)據(jù)信息為Json格式的(有可能一些公司返回的不是Json數(shù)據(jù)格式),但不管返回什么格式,一般APP在接收到網(wǎng)絡(luò)請(qǐng)求的響應(yīng)時(shí),會(huì)使用一些框架如Gson,F(xiàn)astJson,JackSon等來把響應(yīng)信息解析成Java對(duì)象,上面的這種返回?cái)?shù)據(jù)關(guān)注的是”data"字段,將這個(gè)字段解析成Java對(duì)象,此時(shí),關(guān)鍵的一環(huán)來了,要使用本庫(kù)來調(diào)起微信、支付寶支付,則所解析成的Java對(duì)象,需要實(shí)現(xiàn)本庫(kù)的一個(gè)接口【ICanPayOrderInfo】該接口的目的即為統(tǒng)一以及通用各APP從服務(wù)端解析的支付訂單數(shù)據(jù)對(duì)象,該接口【ICanPayOrderInfo】代碼為:
package common.pay.sdk;
import com.tencent.mm.sdk.modelpay.PayReq;
import java.io.Serializable;
/**
*User: fee(1176610771@qq.com)
*Date: 2016-11-01
*Time: 18:51
*DESC: 能發(fā)起微信/支付寶 支付的訂單信息接口,各APP在使用本庫(kù)時(shí),因?yàn)楦髯缘姆?wù)端返回的微信、支付寶的支付訂單信息數(shù)據(jù)
*本庫(kù)不能統(tǒng)一,所以只要各APP的某個(gè)訂單信息實(shí)體對(duì)象實(shí)現(xiàn)該接口并正確實(shí)現(xiàn)接口中的方法
*/
public interface ICanPayOrderInfo extends Serializable {
/**
*將服務(wù)端的返回支付訂單信息轉(zhuǎn)換成微信支付訂單請(qǐng)求對(duì)象,如果當(dāng)前是微信支付的話,
*參考:
*
PayReq request = new PayReq();
request.appId = "wxd930ea5d5a258f4f";
request.partnerId = "1900000109";
request.prepayId= "1101000000140415649af9fc314aa427",;
request.packageValue = "Sign=WXPay";
request.nonceStr= "1101000000140429eb40476f8896f4c9";
request.timeStamp= "1398746574";
request.sign= "7FFECB600D7157C5AA49810D2D8F28BC2811827B";
*@return 微信訂單支付請(qǐng)求對(duì)象
*/
PayReq convert2WxPayReq();
/**
*獲取阿里--支付寶的訂單信息
*該支付寶的訂單信息規(guī)則需要參見
*https://doc.open.alipay.com/doc2/detail?treeId=59&articleId=103662&docType=1
*上面鏈接中請(qǐng)求參數(shù)示例對(duì)應(yīng)PayTask payTask = new PayTask(activity); payTask.pay(orderInfo,true);
*目前有新版本
*https://doc.open.alipay.com/docs/doc.htm?spm=a219a.7629140.0.0.vvNnw2&treeId=204&articleId=105300&docType=1
*新版本的支付SDK對(duì)應(yīng)PayTask payTask = new PayTask(activity); payTask.payV2(orderInfo,true);
*@return
*/
String getAlipayInfo();
/**
*本次訂單信息是否可以支付
*@return
*/
boolean canPayThisOrder();
/**
*是否為支付寶的訂單
*@return 依據(jù)是否當(dāng)前有支付寶的支付訂單請(qǐng)求信息來判斷
*/
boolean isAliPayOrder();
/**
*是否為微信支付的訂單
*@return 依據(jù)是否當(dāng)前有微信支付訂單請(qǐng)求的關(guān)鍵字段來判斷
*/
boolean isWxPayOrder();
}
你自己解析服務(wù)端返回的支付訂單信息成Java對(duì)象(通用微信/支付寶兩支付訂單信息)并實(shí)現(xiàn)本庫(kù)接口【ICanPayOrderInfo】并正確實(shí)現(xiàn)該接口中的方法后,可參考本庫(kù)自帶的一個(gè)示例類【PrePayOrderInfo】則可以使用本庫(kù)發(fā)起支付了。
3、調(diào)用本庫(kù)的API發(fā)起微信、支付寶支付
API方法在【PayEntryActivity】中,為全局靜態(tài)方法,有三種方式,見下,
方式一:
/**注:該啟動(dòng)支付的方法目前只支持(阿里支付寶支付)
*微信支付時(shí)也可以調(diào)用,但由于微信支付SDK對(duì)響應(yīng)回調(diào)的WxPayEntryActivity的包路徑限制很死,所以本庫(kù)的PayEntryActivity接收不到
*微信支付SDK的響應(yīng)回調(diào),所以為了通用請(qǐng)直接調(diào)用{@linkplain #startPayActivity(Activity, ICanPayOrderInfo, int, Class)}
*@param activity 發(fā)起支付的當(dāng)前Activity
*@param curPrePayOrderInfo??當(dāng)前服務(wù)器返回的支付請(qǐng)求信息數(shù)據(jù)對(duì)象
*@param requestCode 區(qū)分請(qǐng)求的請(qǐng)求碼
*@deprecated
*/
public static void startPayActivity(Activity activity, ICanPayOrderInfo curPrePayOrderInfo, int requestCode) {
startPayActivity(activity, curPrePayOrderInfo, requestCode, PayEntryActivity.class);
}
注意該方法已被標(biāo)注為過期了,因?yàn)樵摲椒ㄔ诎l(fā)起支付寶支付時(shí)沒問題,但如果發(fā)起微信支付時(shí),會(huì)收不到微信支付SDK的支付結(jié)果響應(yīng),所以最好不要使用這個(gè)方式,原因是微信支付SDK對(duì)接收它的支付結(jié)果響應(yīng)的Activity一定要在指定的包路徑下才能接收到結(jié)果響應(yīng),因而下面的方式二,方式三正是解決這個(gè)問題。
方式二:
首先說一下,微信支付SDK對(duì)接收支付結(jié)果響應(yīng)的Activity的限制情況,按照微信支付官方以及其Demo的做法來看,要想接收到微信支付SDK的支付結(jié)果,必須一定要滿足以下條件,
其一:一定要有這樣一個(gè)名字的Activity類,即【W(wǎng)xPayEntryActivity】;
其二:類【W(wǎng)xPayEntryActivity】一定要在【wxapi】包下;
其三:【wxapi】這個(gè)目錄一定要在當(dāng)前項(xiàng)目的包名的直接目錄下,并且該項(xiàng)目包名即為在微信開放平臺(tái)上申請(qǐng)時(shí)填寫的包名。
所有以上滿足條件后的目錄結(jié)構(gòu)示例(也即本項(xiàng)目的目錄結(jié)構(gòu))為:
(吐槽一下,其實(shí)微信支付SDK這樣把目錄限制死,很不利于有些項(xiàng)目有多渠道打包(并且連包名都不同)的需求,應(yīng)該像支付寶支付SDK一樣,只要調(diào)用SDK里的方法就行,管它在哪調(diào)用的在哪接收。),順帶提一下,怎么解決多渠道打包的需求,比如說你的APP根據(jù)不同的應(yīng)用市場(chǎng)(如360手機(jī)應(yīng)用,應(yīng)用寶等),并且在不同的渠道應(yīng)用市場(chǎng)上時(shí)包名也不一樣這種場(chǎng)景,那么解決辦法類似,即如果有兩個(gè)包名(決定該包名是什么,是在app目錄下build.gradle文件中的applicationId=''的賦值來決定)需要打包發(fā)布,則在你的APP項(xiàng)目代碼下,也要建立相對(duì)應(yīng)的包路徑,然后,再在包路徑下,復(fù)制粘貼放進(jìn)【wxapi】以及【W(wǎng)xPayEntryActivity】放進(jìn)【wxapi】目錄下,即目錄是這樣的:com.myapp.360ver-->wxapi-->WxPayEntryActivity。
并且所寫的【W(wǎng)xPayEntryActivity】類需要繼承本庫(kù)的【PayEntryActivity】類,可以是一個(gè)空類,示例-->
并且別忘記在AndroidMenifest中注冊(cè)該【W(wǎng)xPayEntryActivity】如:
<activity?
android:name=".wxapi.WXPayEntryActivity"
android:exported="true"
android:launchMode="singleTop"
android:screenOrientation="portrait"
/>
則可以使用以下方式發(fā)起支付了,見代碼:
/**
為了解除微信支付SDK限制死集成微信支付的APP內(nèi)一定要在包名內(nèi)下建立一個(gè)wxapi包再在該包下建立WxPayEntryActivity類才能正?;卣{(diào)出響應(yīng)
*所以本庫(kù)改為此方法來調(diào)起支付
*@param startActivity 發(fā)起支付的當(dāng)前Activity
*@param curPrePayOrderInfo 當(dāng)前服務(wù)器返回的支付請(qǐng)求信息數(shù)據(jù)對(duì)象
*@param requestCode 區(qū)分請(qǐng)求的請(qǐng)求碼
*@param localWxPayEntryActivityClass 即你的APP內(nèi)的wxapi包下建立的WxPayEntryActivity(該類你什么也不用寫就繼承PayEntryActivity就行)
*/
public static void startPayActivity(Activity startActivity, ICanPayOrderInfo curPrePayOrderInfo, int requestCode, Class<? extends PayEntryActivity> localWxPayEntryActivityClass) {
Intent startIntent = new Intent(startActivity, localWxPayEntryActivityClass);
startIntent.putExtra(CommonPayConfig.INTENT_KEY_CUR_PAY_ORDER_INFO, curPrePayOrderInfo);
startActivity.startActivityForResult(startIntent, requestCode);
}
方式三(為了解決一些APP中存在在Fragment中調(diào)起支付的場(chǎng)景):
/**
* 該方法供在Fragment界面里跳轉(zhuǎn)支付的情況,這樣就能直接在Fragment的onActivityResult()方法中直接拿到支付結(jié)果并處理了
*@param fragment 當(dāng)前碎片界面
*@param curPrePayOrderInfo 當(dāng)前服務(wù)器返回的支付請(qǐng)求信息數(shù)據(jù)對(duì)象
*@param requestCode 區(qū)分請(qǐng)求的請(qǐng)求碼
*@param localWxPayEntryActivityClass 即你的APP內(nèi)的wxapi包下建立的WxPayEntryActivity
*/
public static void startPayActivity(Fragment fragment, ICanPayOrderInfo curPrePayOrderInfo, int requestCode, Class<? extends PayEntryActivity>?localWxPayEntryActivityClass) {
if (fragment == null) {
return;
}
Intent startIntent = new Intent(fragment.getContext(), localWxPayEntryActivityClass);
startIntent.putExtra(CommonPayConfig.INTENT_KEY_CUR_PAY_ORDER_INFO, curPrePayOrderInfo);
fragment.startActivityForResult(startIntent, requestCode);
}
可以看到,方式二與方式三的第四個(gè)參數(shù)即需要傳入你APP內(nèi)自己寫的那個(gè)WxPayEntryActivity的Class,并且該Class對(duì)象需要繼承PayEntryActivity,這樣就從本庫(kù)的角度出發(fā)解決微信支付SDK對(duì)接收支付結(jié)果響應(yīng)的Activity的包路徑問題并通用。
4、可自定義調(diào)起支付時(shí)的過度界面
雖然使用本庫(kù)時(shí),你需要寫一個(gè)【W(wǎng)xPayEntryActivity】并且繼承本庫(kù)的【PayEntryActivity】,就能發(fā)起支付了,但本庫(kù)的【PayEntryActivity】里面的一些等待支付結(jié)果時(shí)過度界面使用了默認(rèn)的一套UI,可能對(duì)于一些APP來說,不太美觀,所以其實(shí)在你寫的【W(wǎng)xPayEntryActivity】中重寫父類的一些方法來自定義你喜歡的過度界面,比如可重寫父類【PayEntryActivity】中的
/**
*子類可重寫此方法用來提供自己喜歡的過度界面
*@return
*/
@Override
protected int getProvideContentViewResID() {
return R.layout.def_pay_activity_layout;
}
等其他你認(rèn)為需要優(yōu)化UI的細(xì)節(jié)。
5、接收微信/支付寶的支付結(jié)果
以上步驟完成后,就只剩下等待支付響應(yīng)結(jié)果了,本庫(kù)的支付響應(yīng)結(jié)果,是通過【PayEntryActivity】中來setResult()方法設(shè)置的,所以不管你是在【Activity】中還是在【Fragment】中使用本庫(kù)來調(diào)起支付,都是在"protected voidonActivityResult(intrequestCode, intresultCode,Intent data){}" 方法中來接收支付結(jié)果,可參考本項(xiàng)目的【MainActivity】中的
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case TEST_REQUEST_PAY_CODE:
String toastHint = "支付模式:%s,響應(yīng)碼:%s,結(jié)果描述:%s";
String payModeDesc = "未知";
String payRespCode = "unKnow";
if (data != null) {
int payMode = data.getIntExtra(CommonPayConfig.INTENT_KEY_CUR_PAY_MODE, CommonPayConfig.PAY_MODE_WX);
payModeDesc = payMode == CommonPayConfig.PAY_MODE_ALIPAY ? "[支付寶]" : "[微信]";
payRespCode = data.getStringExtra(CommonPayConfig.INTENT_KEY_REAL_PAY_RESULT_STATUS_CODE);
}
String resultDesc = "支付失敗";
switch (resultCode) {
case CommonPayConfig.REQ_PAY_RESULT_CODE_OK:
resultDesc = "支付成功";
break;
case CommonPayConfig.REQ_PAY_RESULT_CODE_CANCEL:
resultDesc = "支付被取消了";
break;
case CommonPayConfig.REQ_PAY_RESULT_CODE_NO_WX:
resultDesc = "支付失敗,未安裝微信APP";
break;
case CommonPayConfig.REQ_PAY_RESULT_CODE_ERROR:
resultDesc = "支付失敗";
break;
}
String payResultInfo = "支付模式:" + payModeDesc + "\n" +
"支付SDK的實(shí)際響應(yīng)碼:" + payRespCode + "\n" +
"結(jié)果描述:" + resultDesc;
toastShow(String.format(toastHint, payModeDesc, payRespCode, resultDesc));
break;
}
}
至此,整個(gè)支付使用流程就結(jié)束了。
6、打包測(cè)試
由于微信支付SDK要能真正輸入密碼并付款,需要你的APP用正式簽名文件進(jìn)行打包才能使用,而打正式包并且安裝到手機(jī)上這挺費(fèi)事的,那么可以直接通過AndroidStudio運(yùn)行(debug模式)到手機(jī)上來作正式測(cè)試,就是讓AndroidStudio跑debug模式時(shí)也指定簽名配置為使用正式的簽名文件來打包,教程可網(wǎng)上搜索或者參考:
番外
如果本支付通用庫(kù)有幸被你使用了,順便說一句,那么微信SDK中的微信分享等功能也可以使用,同理,你有APP的包名路徑下的【wxapi】目錄下寫一個(gè)【WXEntryActivity】即可,具體邏輯就自己寫吧。
感謝各位的使用,歡迎提交【issue】【建議】【指正】【批評(píng)】,謝謝!