iOS 微信支付流程詳解

背景

  自微信支付、支付寶支付入世以來,移動端的支付日漸火熱。虛擬貨幣有取代實體貨幣的趨向(這句純屬扯淡,不用管),支付在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;
}

  若是到了這一步,並且跑到了微信並完成了支付,那麼微信會有一個回調,告訴你支付成功了。然而真的成功了嘛,請繼續看第四步。

第四步  去服務器查詢是否支付成功

  即便微信告訴你支付成功了,你也不能相信,只有錢真正打到大家的帳號裏面了,纔算支付成功。任什麼時候候都不能以微信的回調的值判斷支付是否成功(這是微信文檔說的)。

相關文章
相關標籤/搜索