iOS 微信支付(客户端下单)

一、注册登录微信开放平台账号

  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;
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容

  • 前言:本篇文章目的在于梳理知识,巩固思想,学习总结。有什么好的建议,都可以留言。互相促进!总观,微信支付,也没心思...
    麦穗0615阅读 10,027评论 8 70
  • 银联支付,支付宝支付,微信支付的三大总结,之前也有写过两篇。 微信支付,支付宝支付,银联支付——三大支付总结: h...
    LucasAdam阅读 4,916评论 2 23
  • 自己总结的微信支付宝支付流程和注意点: 准备工作: 需要公司的营业执照,税务信息,等老板的身份证信息等,我记得,用...
    Www刘阅读 18,558评论 2 50
  • 什么是类 具有某些相同属性和行为,被归为同一类。例如:汽车是一个类,具有相同的属性:轮子,发动机,相同的行为:刹车...
    天空的羁绊阅读 223评论 0 0
  • 一位妈妈告诉我,孩子刚3岁,最近居然学会骂人了,什么“傻瓜”、“笨蛋”、“臭屁”、“去死”、“打死你”等脏话狠话随...
    小北北妈阅读 292评论 0 0