自微信支付、支付寶支付入世以來,移動端的支付日漸火熱。虛擬貨幣有取代實體貨幣的趨向(這句純屬扯淡,不用管),支付在app開發中是一項基本的功能,有必要去掌握。從難易程度上講,不論是微信支付仍是支付寶支付都是很是簡單的,由於第三方的支付文檔很是詳細,並且他們內部的安全性也很是高。做爲使用這些支付策略的咱們,只須要掌握流程,可以實現正常支付的功能便可。爲何要寫下這篇博文,緣由有二。其一,微信支付流程中有坑,其二,之後忘記了能夠拿出來看看。api
1.微信支付須要兩個帳號,財付通和微信開發者,註冊完成後須要開通支付功能,這些流程就不用多說了。在全部申請成功後,咱們要取出咱們支付功能須要的key:安全
appID、app密鑰(微信發給你的郵件中有如何生成密鑰的連接)、商戶號。服務器
2.在Xcode中配置app ID,須要設置下白名單,在url中配置app ID,不清楚的童鞋能夠百度一下。微信
今後處開始,進入本次的主題,開始支付。支付的過程且分爲四個步驟:session
獲取訂單號的途徑能夠是客戶端本身生成,也能夠去服務器生成,不過通常都是服務器生成。假設拿到了訂單號設爲order_no。微信開發
這個過程是很是關鍵的一步,也是坑常駐的一步。統一下單有的人作法是在服務器操做,有的在客戶端,無論在哪下單都是有必要搞懂的。接下來看看統一下單必需要的參數列表:app
/*應用ID 微信開放平臺審覈經過的應用app ID*/ @property (nonatomic, copy) NSString *appid; /*商戶號 微信支付分配的商戶號*/ @property (nonatomic, copy) NSString *mch_id; /*隨機字符串 隨機字符串,不長於32位*/ @property (nonatomic, copy) NSString *nonce_str; /*簽名*/ @property (nonatomic, copy) NSString *sign; /*商品描述 每天愛消除-遊戲充值。*/ @property (nonatomic, copy) NSString *body; /*商戶訂單號*/ @property (nonatomic, copy) NSString *out_trade_no; /*總金額 訂單總金額,單位爲分*/ @property (nonatomic, copy) NSString *total_fee; /*終端IP*/ @property (nonatomic, copy) NSString *spbill_create_ip; /*通知地址*/ @property (nonatomic, copy) NSString *notify_url; /*交易類型*/ @property (nonatomic, copy) NSString *trade_type;
其中,sign是其餘全部參數按照key1=value1&key2=value2...的方式拼接,而後進行加密獲得。參數拼接按照字母排序,舉個例子,參數爲appid=@"id",mch_id=@"mch"獲得的字符串應該是:@"appid=id&mch_id=mch",而後對該字符串進行加密,以下述代碼dom
- (instancetype)initWithDicInfo:(NSDictionary *)infoDic{post
if (self = [super init]) {微信支付
self.appid = WECHAT_SHARE_APPID;
self.mch_id = WECHAT_MCH_ID;
self.nonce_str = [AppMethod getRandomString];
self.body = @"test";
self.out_trade_no = [infoDic formateObjectForKey:@"order_no"];
self.total_fee = [NSString stringWithFormat:@"%@", [infoDic formateObjectForKey:@"amount"]];
self.spbill_create_ip = [AppMethod deviceIPAdress];
self.notify_url = [NSString stringWithFormat:@"%@%@", BASE_URL, WECHAT_NOTI_URL];
self.trade_type = @"APP";
self.payDic = [NSMutableDictionary dictionary];
[self.payDic setValue:self.appid forKey:@"appid"];
[self.payDic setValue:self.mch_id forKey:@"mch_id"];
[self.payDic setValue:self.nonce_str forKey:@"nonce_str"];
[self.payDic setValue:self.body forKey:@"body"];
[self.payDic setValue:self.out_trade_no forKey:@"out_trade_no"];
[self.payDic setValue:self.total_fee forKey:@"total_fee"];
[self.payDic setValue:self.spbill_create_ip forKey:@"spbill_create_ip"];
[self.payDic setValue:self.notify_url forKey:@"notify_url"];
[self.payDic setValue:self.trade_type forKey:@"trade_type"];
self.sign = [self partnerSignOrder:self.payDic];
[self.payDic setValue:self.sign forKey:@"sign"];
}
return self;
}
- (NSString *)partnerSignOrder:(NSDictionary*)paramDic{ NSArray *keyArray = [paramDic allKeys]; NSMutableArray *sortedKeyArray = [NSMutableArray arrayWithArray:keyArray]; [sortedKeyArray sortUsingComparator:^NSComparisonResult(NSString* key1, NSString* key2) { return [key1 compare:key2]; }]; NSMutableString *paramString = [NSMutableString stringWithString:@""]; // 拼接成 A=B&X=Y for (NSString *key in sortedKeyArray){ if ([paramDic[key] length] != 0){ [paramString appendFormat:@"&%@=%@", key, paramDic[key]]; } } if ([paramString length] > 1){ [paramString deleteCharactersInRange:NSMakeRange(0, 1)]; // remove first '&' } [paramString appendFormat:@"&key=%@", WeChatPARTNER_ID];//app密鑰 return [[AppMethod signString:paramString] uppercaseString]; } AppMethod.m + (NSString *)signString:(NSString*)origString{ const char *original_str = [origString UTF8String]; unsigned char result[32]; CC_MD5(original_str, (CC_LONG)strlen(original_str), result);//調用md5 NSMutableString *hash = [NSMutableString string]; for (int i = 0; i < 16; i++){ [hash appendFormat:@"%02X", result[i]]; } return hash; }
這樣,統一下單的參數已經準備好了,下面開始請求微信的下單接口:https://api.mch.weixin.qq.com/pay/unifiedorder,如今坑又來了。按照微信的下單說明,傳給微信服務器的參數必須是XML格式的數據。若是你傳過這種類型的天然好辦,不過我猜大多數童鞋沒有傳過這種類型的數據,好在AF有提供方法,否則就等着哭吧。繼續看代碼
+ (void)postWechatPayWithUrl:(NSString *)url params:(id)params andSuccess:(requestSuccessResult)successBlock andFailure:(requestFailureResult)failureBlock{ NSString *string = [params XMLString];//這裏須要導入XMLDictionary文件,裏面有該方法 AFHTTPSessionManager *session = [AFHTTPSessionManager manager]; // 這裏傳入的XML字符串只是形似XML,但不是正確是XML格式,須要使用AF方法進行轉義 session.responseSerializer = [[AFHTTPResponseSerializer alloc] init]; [session.requestSerializer setValue:@"text/xml; charset=utf-8" forHTTPHeaderField:@"Content-Type"]; [session.requestSerializer setValue:url forHTTPHeaderField:@"SOAPAction"]; [session.requestSerializer setQueryStringSerializationWithBlock:^NSString *(NSURLRequest *request, NSDictionary *parameters, NSError *__autoreleasing *error) { return string; }]; [session POST:url parameters:params progress:^(NSProgress * _Nonnull uploadProgress) { } success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { successBlock(responseObject); } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { HPError *hpError = [HPError errorWithCode:error.code desc:error.description]; failureBlock(hpError); }]; }
按照上述的步驟,到這裏就能夠正常下單了,若是微信服務器返回給你的數據中有"result_code" = SUCCESS; "return_code" = SUCCESS,說明下單成功,這個步驟也到此結束。
若是第二步正常下單,那麼微信會返回給你預支付ID,這個ID在最後的支付中相當重要,下面的代碼是比較統一的,你們都這麼寫。
HPWechatProduct *product = [[HPWechatProduct alloc] initWithDic:result]; PayReq *req = [[PayReq alloc] init]; req.partnerId = product.partnerid; req.prepayId = product.prepayid; req.nonceStr = product.noncestr; req.timeStamp = [product.timestamp intValue]; req.package = product.package; req.sign = product.sign; BOOL flag = [WXApi sendReq:req]; HPWechatProduct.m - (instancetype)initWithDic:(NSDictionary *)dic{ if (self = [super init]) { self.appid = WECHAT_SHARE_APPID; self.partnerid = mah_id; self.prepayid = [dic formateObjectForKey:@"prepay_id"]; self.package = @"Sign=WXPay"; self.noncestr = [AppMethod getRandomString]; self.timestamp = [self getTime]; NSMutableDictionary *dic = [NSMutableDictionary dictionary]; [dic setValue:self.appid forKey:@"appid"]; [dic setValue:self.partnerid forKey:@"partnerid"]; [dic setValue:self.prepayid forKey:@"prepayid"]; [dic setValue:self.package forKey:@"package"]; [dic setValue:self.noncestr forKey:@"noncestr"]; [dic setValue:self.timestamp forKey:@"timestamp"]; self.sign = [self partnerSignOrder:dic]; } return self; } - (NSString *)partnerSignOrder:(NSDictionary*)paramDic{ NSArray *keyArray = [paramDic allKeys]; NSMutableArray *sortedKeyArray = [NSMutableArray arrayWithArray:keyArray]; [sortedKeyArray sortUsingComparator:^NSComparisonResult(NSString* key1, NSString* key2) { return [key1 compare:key2]; }]; NSMutableString *paramString = [NSMutableString stringWithString:@""]; // 拼接成 A=B&X=Y for (NSString *key in sortedKeyArray){ if ([paramDic[key] length] != 0){ [paramString appendFormat:@"&%@=%@", key, paramDic[key]]; } } if ([paramString length] > 1){ [paramString deleteCharactersInRange:NSMakeRange(0, 1)]; // remove first '&' } [paramString appendFormat:@"&key=%@", WeChatPARTNER_ID]; return [[AppMethod signString:paramString] uppercaseString]; } - (NSString *)getTime{ NSTimeInterval interval = [[NSDate date] timeIntervalSince1970]; return [NSString stringWithFormat:@"%ld", (long)interval]; } AppMethod.m + (NSString *)getRandomString { NSString *str = [NSString stringWithFormat:@"%s",genRandomString(32)]; return str; }
若是到了這一步,並且跑到了微信並完成了支付,那麼微信會有一個回調,告訴你支付成功了。然而真的成功了嘛,請繼續看第四步。
即便微信告訴你支付成功了,你也不能相信,只有錢真正打到大家的帳號裏面了,纔算支付成功。任什麼時候候都不能以微信的回調的值判斷支付是否成功(這是微信文檔說的)。