自己總結(jié)的微信支付寶支付流程和注意點(diǎn):
準(zhǔn)備工作:
需要公司的營業(yè)執(zhí)照,稅務(wù)信息,等老板的身份證信息等,我記得,用這些材料,去支付寶注冊一個(gè)商家賬戶(審核周期大概5個(gè)工作日),或者微信的開發(fā)者賬號(hào)(審核周期大概5個(gè)工作日,300元費(fèi)用),微信的話,需要你的app已經(jīng)上架有了APPID,才能開通;大概也是5個(gè)工作日
用微信支付時(shí):用戶點(diǎn)擊支付按鈕時(shí)要先判斷有沒有安裝微信,以及當(dāng)前的微信版本是否支持支付,都滿足則進(jìn)入到微信界面,調(diào)起微信支付,在進(jìn)行微信支付的
支付文檔鏈接:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_1
支付寶支付分為:支付寶有“移動(dòng)快捷支付”(支付時(shí)跳轉(zhuǎn)到支付寶APP,需要用戶安裝支付寶APP),或者“移動(dòng)WAP網(wǎng)頁支付”(支付時(shí)打開一個(gè)WebView里邊登陸支付寶進(jìn)行支付)我看到大部分app美團(tuán)大眾點(diǎn)評(píng)支付寶支付都是第二種
支付寶支付步驟:
1.先與支付寶簽約,獲得商戶ID(partner)和賬號(hào)ID(seller)
2.下載相應(yīng)的公鑰私鑰文件(用來加密簽名)
3.下載支付寶SDK
4.生產(chǎn)訂單信息(客戶端或放在服務(wù)端生成訂單號(hào),支付寶支付成功后會(huì)通知服務(wù)端,這樣在服務(wù)端生成訂單的話,可以掌握所有的訂單,而且還會(huì)更加安全)
5.調(diào)用支付寶支付接口發(fā)送訂單,由支付寶客戶端跟支付寶安全服務(wù)器打交道
6.支付完畢后返回支付結(jié)果給商戶客戶端,進(jìn)行結(jié)構(gòu)處理
安全問題:接收到的支付結(jié)果可能被截獲修改。所以,就需要在生成訂單和處理支付結(jié)果時(shí)做安全性校驗(yàn)
生成訂單時(shí)對(duì)數(shù)據(jù)簽名,收到支付結(jié)果時(shí)對(duì)數(shù)據(jù)進(jìn)行簽名驗(yàn)證,以檢驗(yàn)數(shù)據(jù)是否被篡改過。
支付寶目前只支持采用RSA加密方式做簽名驗(yàn)證。
RSA加密算法除了可加解密外,還可用來作簽名校驗(yàn)。
簡單的說,RSA會(huì)生成一個(gè)私鑰和一個(gè)公鑰,私鑰你應(yīng)該獨(dú)自保管,公鑰你可以分發(fā)出去。
做簽名驗(yàn)證時(shí),你可以用私鑰對(duì)需要傳輸?shù)臄?shù)據(jù)做簽名加密,生成一個(gè)簽名值,之后分發(fā)數(shù)據(jù),接收方通過公鑰對(duì)簽名值做校驗(yàn),如果一致則認(rèn)為數(shù)據(jù)無篡改
具體到支付寶使用RSA做簽名驗(yàn)證,就是在生產(chǎn)訂單時(shí),需要使用私鑰生成簽名值;在處理返回的支付結(jié)果時(shí),需要使用公鑰驗(yàn)證返回結(jié)果是否被篡改了。
實(shí)例代碼:
@WeakObj(self);
NDHttpRequestManage*request = [NDHttpRequestManagerequestManage];
//需要傳給支付寶的AppScheme
NSString*appScheme =@"aaa2015080600202133";
[requestuserObtainAliSign:[self.user.userIDndValue]
AndTradeNo:_tradeNo
complete:^(idcompleteObject,NSError*error )
{
@StrongObj(self);
[self.indicationstopAnimationWithLoadText:@"finish"withType:YES];
self.indication=nil;
if(!error){
//簽名信息以及訂單信息
NSString*String = [completeObjectobjectForKey:@"sign"];
NSString*orderSpec = [completeObjectobjectForKey:@"content"];
if([signisKindOfClass:[NSNullclass]] || sign ==nil){
[selfshowDissmissSelfAlertViewTitle:@""message:@"支付失敗,請(qǐng)重新嘗試。"complete:^{
}];
return;}
//urlcode加密
NSString*signedString = [selfurlEncodedString:String];
NSString*orderString ?=nil;
if(signedString !=nil){
//生成訂單信息及簽名請(qǐng)求參數(shù)
orderString = [NSStringstringWithFormat:@"%@&sign=\"%@\"&sign_type=\"%@\"",
orderSpec, signedString,@"RSA"];
}
//調(diào)用支付寶的SDK[[AlipaySDKdefaultService]payOrder:orderStringfromScheme:appSchemecallback:^(NSDictionary*resultDic){
//resultDic ?這個(gè)字典是返回我所有的支付成功或者失敗的信息
NSString* key =@"ResultStatus";
if(![[resultDicallKeys]containsObject:key]) {
key =@"resultStatus";
}
if(key !=nil){
//返回9000 就是成功
if([[resultDicobjectForKey:key]integerValue] ==9000) {
//訂單成功.
[selfpaySuccess];
}else{
//訂單失敗.
[selfpayFailed];
}
}
}];
}
}];
-(NSString*)urlEncodedString:(NSString*)string
{
NSString* encodedString = (__bridge_transferNSString*)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (__bridgeCFStringRef)string,NULL, (__bridgeCFStringRef)@"!*'();:@&=+$,/?%#[]",kCFStringEncodingUTF8);
returnencodedString;
}
集成
清楚了流程后,就好理解怎么集成了。
支付SDK
如果只需要發(fā)送訂單和處理支付返回結(jié)果,只需要添加AlipaySDK.bundle和AlipaySDK.framework就行了。
這里再吐槽下,之前用的舊版本,和現(xiàn)在的版本相比,還不光是把類名字給改了,原先是用的類方法,現(xiàn)在新版又給改成了單例了。。還真是任性啊,這要是哪家小廠的SDK,估計(jì)早被棄用了把。。
發(fā)送訂單的方法:
- (void)payOrder:(NSString *)orderStr
fromScheme:(NSString *)schemeStr
callback:(CompletionBlock)completionBlock;
如果手機(jī)內(nèi)沒安裝支付寶的app,會(huì)直接展現(xiàn)支付寶web支付界面,通過callback返回支付結(jié)果;
如果手機(jī)內(nèi)安裝了支付寶的app,會(huì)跳轉(zhuǎn)到支付寶的app支付,然后通過openURL的回調(diào)返回支付結(jié)果。
支付寶的SDK只給了一個(gè)處理返回結(jié)果的方法,而不像其他第三方的SDK提供一個(gè)處理openURL的方法,所以你需要通過DEMO或者在第二個(gè)文檔里找到處理openURL的方式:
if ([url.host isEqualToString:@"safepay"]) {
[[AlipaySDK defaultService] processOrderWithPaymentResult:url
standbyCallback:^(NSDictionary *resultDic) {
NSLog(@"result = %@",resultDic);
}]; }
SDK也提供了一個(gè)處理openURL返回結(jié)果的方法
- (void)processOrderWithPaymentResult:(NSURL *)resultUrl
standbyCallback:(CompletionBlock)completionBlock;
兩個(gè)回調(diào)block都統(tǒng)一定義為typedef void(^CompletionBlock)(NSDictionary *resultDic);,
返回了一個(gè)字典,但是SDK里完全沒有提示有哪些key。。
你可以在文檔里找到,或者自己實(shí)際試一下,返回的信息如下:
resultStatus,狀態(tài)碼,SDK里沒對(duì)應(yīng)信息,第一個(gè)文檔里有提到:
9000 訂單支付成功
8000 正在處理中
4000 訂單支付失敗
6001 用戶中途取消
6002 網(wǎng)絡(luò)連接出錯(cuò)
memo, 提示信息,比如狀態(tài)碼為6001時(shí),memo就是“用戶中途取消”。但千萬別完全依賴這個(gè)信息,如果未安裝支付寶app,采用網(wǎng)頁支付時(shí),取消時(shí)狀態(tài)碼是6001,但這個(gè)memo是空的。。(當(dāng)我發(fā)現(xiàn)這個(gè)問題的時(shí)候,我就決定,對(duì)于這么不靠譜的SDK,還是盡量靠自己吧。。)
result,訂單信息,以及簽名驗(yàn)證信息。如果你不想做簽名驗(yàn)證,那這個(gè)字段可以忽略了。。
如果你對(duì)支付的安全性不那么在意或重視的話,到這里就可以完成支付寶的集成了。
如果想更加安全,還是需要增加下面的簽名驗(yàn)證的。
簽名驗(yàn)證
首先,RSA只是一種算法,所以你可以使用任何一種開源的、或者自己去實(shí)現(xiàn)這個(gè)算法來實(shí)現(xiàn)簽名和驗(yàn)證的目的。
在整個(gè)流程當(dāng)中,因?yàn)樯婕暗搅薘SA公鑰、私鑰的生產(chǎn),RSA的簽名、驗(yàn)證簽名,SHA1值的計(jì)算,base64和URL編碼,所以支付寶用了一個(gè)開源的代碼來統(tǒng)一解決這些問題,就是openssl(順便再吐槽下,這DEMO里一放openssl,不知道又會(huì)引來多少公司的產(chǎn)品里使用openssl了,估計(jì)阿里自己也沒少用,什么時(shí)候都能跟老羅、華為一樣去贊助點(diǎn)呢。。)
如果你想省事,也用openssl,那你需要把這些東西都加入到項(xiàng)目中:DEMO中的openssl目錄頭文件,兩個(gè)庫文件libcrypto.a libssl.a,DEMO里支付寶自己寫的Util目錄
訂單簽名
上面說了,訂單簽名應(yīng)該用私鑰,但是把私鑰放到app里其實(shí)本身就不安全,因?yàn)槟愕腶pp是分發(fā)到用戶手里的,私鑰應(yīng)該放在自己的手里,分發(fā)出去的應(yīng)該是公鑰。
所以私鑰最好是放在自己的服務(wù)器上,訂單加密這個(gè)工作放在服務(wù)器端來做,服務(wù)器將包含簽名的訂單信息返回給app,app再通過SDK發(fā)送給支付寶,這樣會(huì)更安全些;而且服務(wù)器也能掌握所有的訂單狀況。
如果你非要將私鑰集成到app里,那可以參考SDK的DEMO,因?yàn)檫@個(gè)DEMO就是在app本地通過私鑰做的訂單簽名。。
支付結(jié)果簽名驗(yàn)證
上面的回調(diào)block提到了返回的內(nèi)容,返回的支付結(jié)果中的result字段里是帶有訂單信息和簽名信息的,所以簽名驗(yàn)證就是需要這個(gè)字段的值。
文檔中有一個(gè)這個(gè)字段的例子,實(shí)際結(jié)果沒有換行,我換一下行便于閱讀:
partner="2088101568358171"&seller_id="xxx@alipay.com"&out_trade_no="0819145412-6177"&subject="測試"&body="測試測試"&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"&success="true"&sign_type="RSA"&sign="hkFZr+zE9499nuqDNLZEF7W75RFFPsly876QuRSeN8WMaUgcdR00IKy5ZyBJ4eldhoJ/2zghqrD4E2G2mNjs3aE+HCLiBXrPDNdLKCZ gSOIqmv46TfPTEqopYfhs+o5fZzXxt34fwdrzN4mX6S13cr3UwmEV4L3Ffir/02RBVtU="
總共分為三個(gè)部分
第一部分是訂單信息,每個(gè)字段的具體含義可以在文檔里找;
中間sign_type是簽名用的算法,文檔里說了,目前只支持RSA;
最后的sign就是簽名值。
驗(yàn)證的步驟如下:
首先把訂單信息和簽名值分別提取出來(SDK居然都不給處理好。。)
訂單信息就是sign_type的連字符&之前的所有字符串
簽名值是sign后面雙引號(hào)內(nèi)的內(nèi)容,注意簽名的結(jié)尾也是=,所以不要用split字符串的方式提取
如果你想簡單,可以直接使用Util目錄下的DataVerifier來作簽名驗(yàn)證
- (BOOL)verifyString:(NSString *)string withSign:(NSString *)signString;
第一個(gè)參數(shù)就是訂單信息,第二個(gè)參數(shù)就是簽名值。
其實(shí)不使用openssl,用其他第三方RSA的開源代碼也是可以的??梢钥聪翫EMO里openssl_wrapper的源碼和SDK的文檔。
對(duì)于訂單信息,先做一個(gè)base64編碼(DEMO中這個(gè)還要調(diào)openssl來實(shí)現(xiàn)。。),再計(jì)算SHA1的值(這個(gè)也可以完全不用openssl,蘋果的庫中都有的。。),然后再簽名比對(duì)。
對(duì)于公鑰,如果使用其他第三方代碼,需要注意格式問題。支付寶的DEMO實(shí)現(xiàn)中,是把這個(gè)公鑰又轉(zhuǎn)回成openssl生成的本地文件格式,然后再寫入本地文件,再讓openssl讀取出來使用。。
以上,就是支付寶 iOS SDK的一些介紹。
總體來說,我覺得能靠自己處理的地方還是盡量不要依賴這個(gè)不太靠譜的SDK了。
微信支付步驟:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1#
郵件中參數(shù)API參數(shù)名詳細(xì)說明
APPIDappidappid是微信公眾賬號(hào)或開放平臺(tái)APP的唯一標(biāo)識(shí),在公眾平臺(tái)申請(qǐng)公眾賬號(hào)或者在開放平臺(tái)申請(qǐng)APP賬號(hào)后,微信會(huì)自動(dòng)分配對(duì)應(yīng)的appid,用于標(biāo)識(shí)該應(yīng)用??稍谖⑿殴娖脚_(tái)-->開發(fā)者中心查看,商戶的微信支付審核通過郵件中也會(huì)包含該字段值。
微信支付商戶號(hào)mch_id商戶申請(qǐng)微信支付后,由微信支付分配的商戶收款賬號(hào)。
API密鑰key交易過程生成簽名的密鑰,僅保留在商戶系統(tǒng)和微信支付后臺(tái),不會(huì)在網(wǎng)絡(luò)中傳播。商戶妥善保管該Key,切勿在網(wǎng)絡(luò)中傳輸,不能在其他客戶端中存儲(chǔ),保證key不會(huì)被泄漏。商戶可根據(jù)郵件提示登錄微信商戶平臺(tái)進(jìn)行設(shè)置。也可按一下路徑設(shè)置:微信商戶平臺(tái)(pay.weixin.qq.com)-->賬戶設(shè)置-->API安全-->密鑰設(shè)置
AppsecretsecretAppSecret是APPID對(duì)應(yīng)的接口密碼,用于獲取接口調(diào)用憑證access_token時(shí)使用。
商戶系統(tǒng)和微信支付系統(tǒng)主要交互說明:
步驟1:用戶在商戶APP中選擇商品,提交訂單,選擇微信支付。
步驟2:商戶后臺(tái)收到用戶支付單,調(diào)用微信支付統(tǒng)一下單接口。參見【統(tǒng)一下單API】。
步驟3:統(tǒng)一下單接口返回正常的prepay_id,再按簽名規(guī)范重新生成簽名后,將數(shù)據(jù)傳輸給APP。參與簽名的字段名為appId,partnerId,prepayId,nonceStr,timeStamp,package。注意:package的值格式為Sign=WXPay
以上信息都是有后臺(tái)服務(wù)器調(diào)用微信的支付接口獲得并返給App端的
步驟4:商戶APP調(diào)起微信支付。api參見本章節(jié)【app端開發(fā)步驟說明】
步驟5:商戶后臺(tái)接收支付通知。api參見【支付結(jié)果通知API】
步驟6:商戶后臺(tái)查詢支付結(jié)果。,api參見【查詢訂單API】
例子
NDHttpRequestManage*request = [NDHttpRequestManagerequestManage];
[requestuserGetWeiXinSign:[self.user.userIDndValue]AndTradeNo:_tradeNocomplete:^(idcompleteObject,NSError*error)
{
@StrongObj(self);
[self.indicationstopAnimationWithLoadText:@"finish"withType:YES];
self.indication=nil;
if(!error)
{
appDelegate.loginDone=nil;
NSString*str = completeObject[@"sign"];
[selfcheckSignFromSign:str];
NSData*jsonData = [strdataUsingEncoding:NSUTF8StringEncoding];
NSError*err;
NSDictionary*dict = [NSJSONSerializationJSONObjectWithData:jsonData
options:NSJSONReadingMutableContainers
error:&err];
NSMutableString*prepayId = [dictobjectForKey:@"prepayid"];
if(prepayId){
NSMutableString*stamp ?= [dictobjectForKey:@"timestamp"];
//調(diào)起微信支付
PayReq* req ? ? ? ? ? ? = [[PayReqalloc]init];
req.openID=[dictobjectForKey:@"appid"];
req.partnerId= [dictobjectForKey:@"partnerid"];
req.prepayId= [dictobjectForKey:@"prepayid"];
req.nonceStr= [dictobjectForKey:@"noncestr"];
req.timeStamp= stamp.intValue;
req.package=@"Sign=WXPay";
req.sign= [dictobjectForKey:@"sign"];
BOOLret = ? [WXApisendReq:req];
if(ret ==NO)
{
[selfshowDissmissSelfAlertViewTitle:@""message:@"支付失敗,請(qǐng)重新嘗試。"complete:^{
}];
}
}
}
}];
以下項(xiàng)目開發(fā)環(huán)境以Xcode10.0,運(yùn)行環(huán)境為IOS7.0為例,說明其開發(fā)中需要的操作。
1、項(xiàng)目設(shè)置APPID
商戶在微信開放平臺(tái)申請(qǐng)開發(fā)APP應(yīng)用后,微信開放平臺(tái)會(huì)生成APP的唯一標(biāo)識(shí)APPID。在Xcode中打開項(xiàng)目,設(shè)置項(xiàng)目屬性中的URL Schemes為您的APPID。如圖8.7標(biāo)紅位置所示。
圖8.7
2、注冊APPID
商戶APP工程中引入微信lib庫和頭文件,調(diào)用API前,需要先向微信注冊您的APPID,代碼如下:
[WXApi registerApp:@"wxd930ea5d5a258f4f" withDescription:@"demo 2.0"];
3、調(diào)起支付
商戶服務(wù)器生成支付訂單,先調(diào)用【統(tǒng)一下單API】生成預(yù)付單,獲取到prepay_id后將參數(shù)再次簽名傳輸給APP發(fā)起支付。以下是調(diào)起微信支付的關(guān)鍵代碼:
PayReq *request = [[[PayReq alloc] init] autorelease];
request.partnerId = @"10000100";
request.prepayId= @"1101000000140415649af9fc314aa427";
request.package = @"Sign=WXPay";
request.nonceStr= @"a462b76e7436e98e0ed6e13c64b4fd1c";
request.timeStamp= @"1397527777";
request.sign= @"582282D72DD2B03AD892830965F428CB16E7A256";
[WXApi sendReq:request];
注意:該sign生成字段名列表見調(diào)起支付API
4、支付結(jié)果回調(diào)
照微信SDK Sample,在類實(shí)現(xiàn)onResp函數(shù),支付完成后,微信APP會(huì)返回到商戶APP并回調(diào)onResp函數(shù),開發(fā)者需要在該函數(shù)中接收通知,判斷返回錯(cuò)誤碼,如果支付成功則去后臺(tái)查詢支付結(jié)果再展示用戶實(shí)際支付結(jié)果。注意 一定不能以客戶端返回作為用戶支付的結(jié)果,應(yīng)以服務(wù)器端的接收的支付通知或查詢API返回的結(jié)果為準(zhǔn)。代碼示例如下:
-(void)onResp:(BaseResp*)resp{
if ([respisKindOfClass:[PayRespclass]]){
PayResp*response=(PayResp*)resp;
switch(response.errCode){
caseWXSuccess:
//服務(wù)器端查詢支付通知或查詢API返回的結(jié)果再提示成功
NSlog(@"支付成功");
break;
default:
NSlog(@"支付失敗,retcode=%d",resp.errCode);
break;
}
}
}
回調(diào)中errCode值列表:
名稱描述解決方案
0成功展示成功頁面
-1錯(cuò)誤可能的原因:簽名錯(cuò)誤、未注冊APPID、項(xiàng)目設(shè)置APPID不正確、注冊的APPID與設(shè)置的不匹配、其他異常等。
-2用戶取消無需處理。發(fā)生場景:用戶不支付了,點(diǎn)擊取消,返回APP。