iOS 微信支付(客戶端下單)

SunnyD發表於2017-12-14

一、註冊登入微信開放平臺賬號

  1. 註冊登入微信開放平臺賬號
  2. 新增一個用於支付/分享的移動應用,等待稽核通過
  3. 前往管理中心-移動應用,為通過稽核的移動應用申請支付功能,並等待稽核通過
    申請支付功能

詳細步驟參照微信支付官方文件 微信APP支付接入商戶服務中心

二、整合微信SDK

  • 前往微信資源中心下載 iOS 的 SDK,並新增到專案中

    整合SDK

  • 編輯 info.plist,將微信新增到 APP 跳轉白名單

    新增白名單

  • Targets-Info-URL Types 新增微信的 APP_ID

    設定 URL Types

  • AppDelegate 處理相關回撥(需要#import "WXApi.h"

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    // 註冊(如果使用了友盟,則先註冊友盟)
    [WXApi registerApp:@"wxd930ea5d5a258f4f"];
    
    return YES;
}

- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
    return [WXApi handleOpenURL:url delegate:self];
}

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
    return [WXApi handleOpenURL:url delegate:self];
}

// 微信支付結果的回撥
- (void)onResp:(BaseResp *)resp {
    //啟動微信支付的response
    NSString *strMsg = [NSString stringWithFormat:@"errcode:%d", resp.errCode];
    BOOL paySuccess = false;
    if([resp isKindOfClass:[PayResp class]]){
        //支付返回結果,實際支付結果需要去微信伺服器端查詢
        switch (resp.errCode) {
            case 0:
                strMsg = @"支付結果:成功!";
                paySuccess = true;
                break;
            case -1:
                strMsg = @"支付結果:失敗!";
                break;
            case -2:
                strMsg = @"使用者已經退出支付!";
                break;
            default:
                strMsg = [NSString stringWithFormat:@"支付結果:失敗!retcode = %d, retstr = %@", resp.errCode,resp.errStr];
                break;
        }
    }
    NSNotification *nofity = [[NSNotification alloc] initWithName:kNofificationWechatPayCallback object:nil userInfo:@{@"success":paySuccess ? @(YES) : @(NO) ,@"errMsg":paySuccess ? @"" : strMsg}];
    [[NSNotificationCenter defaultCenter] postNotification:nofity];
}
複製程式碼
  • 定義幾個需要用到的常量
// 商戶號
static NSString *const kWx_MchId = @"YOUR_MCH_ID";
// AppId
static NSString *const kWx_AppId = @"YOUR_WX_AppID";
// 商戶API金鑰,商戶平臺登入賬戶和密碼登入http://pay.weixin.qq.com 平臺設定的“API金鑰”,為了安全,請設定為以數字和字母組成的32字串。
static NSString *const kWx_PartnerKey = @"YOUR_WX_PartnerKey";
// AppSecret
static NSString *const kWx_AppSecret = @"YOUR_WX_AppSecret";
// 服務端接收支付非同步通知回撥地址
static NSString *const kWx_NotifyURL = @"http://www.xxx.com/xxx.php";
複製程式碼
  • 呼叫微信支付關鍵程式碼
/**
 微信支付

 @param orderNo 訂單號
 @param orderDesc 訂單資訊
 @param fee 金額(分)
 */
- (void)payWithOrderNo:(NSString *)orderNo orderDescription:(NSString *)orderDesc fee:(NSInteger)fee {
    // 請求引數 (引數說明:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1)
    NSMutableDictionary *params = @{}.mutableCopy;
    params[@"appid"] = kWx_AppId; // 應用ID!
    params[@"mch_id"] = kWx_MchId; // 商戶號!
    params[@"nonce_str"] = ({ // 隨機字串!
        srand( (unsigned)time(0) );
        [NSString stringWithFormat:@"%d", rand()];
    });
    params[@"body"] = orderDesc; // 商品描述!
    params[@"out_trade_no"] = kWx_AppId; // 商戶訂單號!
    params[@"total_fee"] = @(fee); // 總金額!
    params[@"spbill_create_ip"] = @"196.168.1.1"; // 終端IP!
    params[@"notify_url"] = kWx_NotifyURL; // 通知地址!
    params[@"trade_type"] = @"APP"; // 交易型別!
    params[@"sign"] = [self getSign:params]; // 簽名!
    
    // 轉成xml
    NSMutableString *xmlParams = @"".mutableCopy;
    [xmlParams appendString:@"<xml>"];
    [params enumerateKeysAndObjectsUsingBlock:^(NSString *  _Nonnull key, id  _Nonnull value, BOOL * _Nonnull stop) {
        [xmlParams appendFormat:@"<%@>%@</%@>", key, value, key];
    }];
    [xmlParams appendString:@"</xml>"];
    // 傳送請求
    NSURLSession *session = [NSURLSession sharedSession];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://api.mch.weixin.qq.com/pay/unifiedorder"]];
    request.HTTPMethod = @"POST";
    request.HTTPBody = [xmlParams dataUsingEncoding:NSUTF8StringEncoding];
    NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        if (!error) {
            // 返回的是xml
            NSString *respXml = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
            // 轉換成字典
            NSDictionary *respDict = [[[XEXMLHelper alloc] init] parseXML:respXml];
            NSString *return_code = respDict[@"return_code"];
            NSString *result_code = respDict[@"result_code"];
            if ([return_code isEqualToString:@"SUCCESS"] && [result_code isEqualToString:@"SUCCESS"]) {
                // 調起微信
                PayReq *req = [[PayReq alloc] init];
                req.partnerId = kWx_MchId;
                req.prepayId = respDict[@"prepayId"];
                req.nonceStr = respDict[@"nonce_str"];
                req.timeStamp = (UInt32)[[NSDate date] timeIntervalSince1970];
                req.package = @"Sign=WXPay";
                // 傳入引數進行二次簽名
                req.sign = [self getSign:@{
                                           @"appid": kWx_AppId,
                                           @"partnerid": req.partnerId,
                                           @"noncestr": req.nonceStr,
                                           @"package": req.package,
                                           @"timestamp": @(req.timeStamp).stringValue,
                                           @"prepayid": req.prepayId
                                           }];
                [WXApi sendReq:req];
            }
            else {
                NSLog(@"介面返回錯誤, %@", respDict);
            }
        }
        else {
            NSLog(@"%@", error);
        }
    }];
    [task resume];
}

/**
 MD5加密

 @param sourceString 源字串
 @return 加密後的字串
 */
- (NSString *)getMD5:(NSString *)sourceString {
    NSData *data = [sourceString dataUsingEncoding:NSUTF8StringEncoding];
    unsigned char result[CC_MD5_DIGEST_LENGTH];
    CC_MD5(data.bytes, (CC_LONG)data.length, result);
    return [NSString stringWithFormat:
            @"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
            result[0], result[1], result[2], result[3],
            result[4], result[5], result[6], result[7],
            result[8], result[9], result[10], result[11],
            result[12], result[13], result[14], result[15]
            ];
}

/**
 生成簽名引數sign

 @param params 引數
 @return sign
 */
- (NSString *)getSign:(NSDictionary *)params {
    NSMutableString *contentString  =[NSMutableString string];
    NSArray *keys = [params allKeys];
    //按字母順序排序
    NSArray *sortedArray = [keys sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
        return [obj1 compare:obj2 options:NSNumericSearch];
    }];
    //拼接字串
    for (NSString *categoryId in sortedArray) {
        if (![[params objectForKey:categoryId] isEqualToString:@""]
            && ![categoryId isEqualToString:@"sign"]
            && ![categoryId isEqualToString:@"key"]
            ) {
            [contentString appendFormat:@"%@=%@&", categoryId, [params objectForKey:categoryId]];
        }
        
    }
    //新增key欄位
    [contentString appendFormat:@"key=%@", kWx_PartnerKey];
    //得到MD5 sign簽名
    NSString *sign = [self getMD5:contentString];
    return sign;
}

複製程式碼

相關文章