iOS 微信支付(客户端生成预支付订单)

1.下载微信支付SDK https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419319164&token=&lang=zh_CN
并在发起支付之前初始化SDK[WXApi registerApp:@"项目申请得到的APPID"];

2.添加App 拉起白名单 和 https 网络访问配置

<key>LSApplicationQueriesSchemes</key>
    <array>
        <string>wechat</string>
        <string>weixin</string>
    </array>
<key>NSAppTransportSecurity</key>
    <dict>
        <key>NSAllowsArbitraryLoads</key>
        <true/>
    </dict>

3.添加 URL Types / URL Schemes

B64909EE-D214-463B-9EE3-C73A4D55A6D0.png

4.添加微信SDK 依赖库

SystemConfiguration.framework
libz.tbd
libsqlite3.0.tbd
CoreTelephony.framework
QuartzCore.framework

5.如何获取预支付订单号

    NSString* orderName = @"订单标题"; //订单标题,展示给用户
    NSString* orderPrice = @"100";//以分为单位的整数
    NSString* orderDevice = [[UIDevice currentDevice] identifierForVendor].UUIDString;//支付设备号
    NSString* orderType = @"APP";  //支付类型,固定为APP
    NSString* orderIP = [self fetchIPAddress]; //发器支付的机器ip
    NSString *noncestr  = [self generateRandomString]; //随机数串
    //订单号 --- (随机生成可以直接用时间戳)
    NSString *orderNO   = [NSString stringWithFormat:@"%ld",(long)[[NSDate date] timeIntervalSince1970]];
    /*预付单参数订单设置*/
    NSMutableDictionary *packageParams = [NSMutableDictionary dictionary];
    [packageParams setObject: @"项目申请得到的APPID"     forKey:@"appid"];       //开放平台appid
    [packageParams setObject: @"项目申请得到的商户号"     forKey:@"mch_id"];      //商户号
    [packageParams setObject: orderDevice  forKey:@"device_info"]; //支付设备号或门店号
    [packageParams setObject: noncestr      forKey:@"nonce_str"];   //随机串
    [packageParams setObject: orderType    forKey:@"trade_type"];  //支付类型,固定为APP
    [packageParams setObject: orderName    forKey:@"body"];        //订单描述,展示给用户
    [packageParams setObject: @"支付成功通知网址"  forKey:@"notify_url"];  //支付结果异步通知
    [packageParams setObject: orderNO       forKey:@"out_trade_no"];//商户订单号
    [packageParams setObject: orderIP        forKey:@"spbill_create_ip"];//发器支付的机器ip
    [packageParams setObject: orderPrice    forKey:@"total_fee"];       //订单金额,单位为分
    
    
    /** 下单获取到预支付ID(prepay_id)   **/
    NSString * prePayID = [self getPrePayId:packageParams];
//获取ip地址头文件
#include <ifaddrs.h>
#include <arpa/inet.h>
#pragma mark - 获取设备ip地址
- (NSString *)fetchIPAddress {
    NSString *address = @"error";
    struct ifaddrs *interfaces = NULL;
    struct ifaddrs *temp_addr = NULL;
    int success = 0;
    success = getifaddrs(&interfaces);
    if (success == 0) {
        temp_addr = interfaces;
        while(temp_addr != NULL) {
            if(temp_addr->ifa_addr->sa_family == AF_INET) {
                // Check if interface is en0 which is the wifi connection on the iPhone
                if([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@"en0"]) {
                    // Get NSString from C String
                    address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)];
                }
            }
            temp_addr = temp_addr->ifa_next;
        }
    }
    freeifaddrs(interfaces);
    return address;
}
#pragma mark - 产生随机字符串
- (NSString *)generateRandomString {
    static int kNumber = 15;
    NSString *sourceStr = @"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    NSMutableString *resultStr = [[NSMutableString alloc] init];
    srand((unsigned)time(0));
    for (int i = 0; i < kNumber; i++) {
        unsigned index = rand() % [sourceStr length];
        NSString *oneStr = [sourceStr substringWithRange:NSMakeRange(index, 1)];
        [resultStr appendString:oneStr];
    }
    return resultStr;
}
#pragma mark ---  获取prePayid
-(NSString *)getPrePayId:(NSMutableDictionary *)pakeParams{
    NSString *prepayid = nil;
    //获取提交支付
    NSString *send = [self genPackage:pakeParams];
    //发送请求post xml数据
    NSData *res = [self httpSend:@"https://api.mch.weixin.qq.com/pay/unifiedorder" method:@"POST" data:send];
    XMLHelper *xml  = [[XMLHelper alloc] init];
    //开始解析
    [xml startParse:res];
    NSMutableDictionary *resParams = [xml getDict];
    NSLog(@"%@",resParams);
    //判断返回
    NSString *return_code   = [resParams objectForKey:@"return_code"];
    NSString *result_code   = [resParams objectForKey:@"result_code"];
    if ([return_code isEqualToString:@"SUCCESS"]) {
        //生成返回数据进行排序签名
        NSString *sign = [self createMd5Sign:resParams ];
        NSString *send_sign =[resParams objectForKey:@"sign"];
        if ([sign isEqualToString:send_sign]) {
            if ([result_code isEqualToString:@"SUCCESS"]) {
                prepayid = [resParams objectForKey:@"prepay_id"];
            }
        }
    }
    return prepayid;
}
#pragma mark ---  创建package签名
-(NSString*) createMd5Sign:(NSMutableDictionary*)dict{
        NSMutableString *contentString  =[NSMutableString string];
        NSArray *keys = [dict allKeys];
        //按字母顺序排序
        NSArray *sortedArray = [keys sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
            return [obj1 compare:obj2 options:NSNumericSearch];
        }];
        //拼接字符串
        for (NSString *categoryId in sortedArray) {
            if (![[dict objectForKey:categoryId] isEqualToString:@""]&& ![categoryId isEqualToString:@"sign"] && ![categoryId isEqualToString:@"key"] )  {
                [contentString appendFormat:@"%@=%@&", categoryId, [dict objectForKey:categoryId]];
            }
        }
        //添加key字段
        [contentString appendFormat:@"key=%@", @"密钥api_key"];
        NSLog(@"%@",contentString);
        //得到MD5 sign签名
        NSString *md5Sign =[self md5:contentString];
        return md5Sign;
}

生成过程中需要用到xml 解析文件

#import <Foundation/Foundation.h>
/*
 XML 解析库api说明:
 //============================================================================
 
 //输入参数为xml格式串,初始化解析器
 -(void)startParse:(NSData *)data;
 
 //获取解析后的字典
-(NSMutableDictionary*) getDict;
 
//============================================================================
 */
@interface XMLHelper : NSObject<NSXMLParserDelegate> {

    //解析器
    NSXMLParser *xmlParser;
    //解析元素
    NSMutableArray *xmlElements;
    //解析结果
    NSMutableDictionary *dictionary;
    //临时串变量
    NSMutableString *contentString;
}
 //输入参数为xml格式串,初始化解析器
-(void)startParse:(NSData *)data;
 //获取解析后的字典
-(NSMutableDictionary*) getDict;
@end
#import <Foundation/Foundation.h>
#import "ApiXml.h"
/*
 XML 解析库
 */
@implementation XMLHelper
-(void) startParse:(NSData *)data{

    dictionary =[NSMutableDictionary dictionary];
    contentString=[NSMutableString string];
    
    //Demo XML解析实例
    xmlElements = [[NSMutableArray alloc] init];
    
    xmlParser = [[NSXMLParser alloc] initWithData:data];

    [xmlParser setDelegate:self];
    [xmlParser parse];
    
}
-(NSMutableDictionary*) getDict{
    return dictionary;
}
//解析文档开始
- (void)parserDidStartDocument:(NSXMLParser *)parser{
    //NSLog(@"解析文档开始");
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
    //NSLog(@"遇到启始标签:%@",elementName);
}

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
    //NSLog(@"遇到内容:%@",string);
    [contentString setString:string];
}

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
    //NSLog(@"遇到结束标签:%@",elementName);
    
    if( ![contentString isEqualToString:@"\n"] && ![elementName isEqualToString:@"root"]){
        [dictionary setObject: [contentString copy] forKey:elementName];
        //NSLog(@"%@=%@",elementName, contentString);
    }
}

//解析文档结束
- (void)parserDidEndDocument:(NSXMLParser *)parser{
    //NSLog(@"文档解析结束");
    xmlElements = nil;
    xmlParser = nil;
}

@end

6 . 获取到预支付订单之后,获取签名 发起支付

   /** 获取到prepayid后进行第二次签名  **/
    NSString *package = @"Sign=WXPay";
    NSString *time_stamp  = [NSString stringWithFormat:@"%ld",(long)[[NSDate date] timeIntervalSince1970]];//时间戳
    NSString *nonce_str = [self md5:time_stamp];//随机字符串(直接用时间戳来生成就可以了)
    NSMutableDictionary *signParams = [NSMutableDictionary dictionary];//用于二次签名的参数
    [signParams setObject:@"项目申请得到的APPID"  forKey:@"appid"];
    [signParams setObject:@"项目申请得到的商户号"  forKey:@"partnerid"];
    [signParams setObject: nonce_str    forKey:@"noncestr"];
    [signParams setObject: package      forKey:@"package"];
    [signParams setObject: time_stamp   forKey:@"timestamp"];
    [signParams setObject: prePayID     forKey:@"prepayid"];
    //调起微信支付
    PayReq* req  = [[PayReq alloc] init];
    req.partnerId = @"项目申请得到的商户号";
    req.prepayId  = prePayID;
    req.nonceStr  = nonce_str;
    req.timeStamp = time_stamp.intValue;
    req.package = package;
    req.sign = [self createMd5Sign:signParams];//二次签名
    [WXApi sendReq:req];

7 . 监听支付结果回调 实现 微信代理<WXApiDelegate>

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
    if ([url.host isEqualToString:@"pay"]) {
        return [WXApi handleOpenURL:url delegate:self];;
    }
    return YES;
}
-(BOOL)application:(UIApplication *)app handleOpenURL:(nonnull NSURL *)url{
    if ([url.host isEqualToString:@"pay"]) {
        return [WXApi handleOpenURL:url delegate:self];;
    }
    return YES;
}
-(void)onResp:(BaseResp *)resp{
    NSString *strTitle;
    if ([resp isKindOfClass:[SendMessageToWXResp class]]) {
        strTitle = [NSString stringWithFormat:@"发送媒体消息结果"];
    }
    if ([resp isKindOfClass:[PayResp class]]) {
        strTitle = [NSString stringWithFormat:@"支付结果"];
        switch (resp.errCode) {
            case WXSuccess:
            {
                NSLog(@"支付结果: 成功!");
            }
            break;
            case WXErrCodeCommon:
            { //签名错误、未注册APPID、项目设置APPID不正确、注册的APPID与设置的不匹配、其他异常等
                NSLog(@"支付结果: 失败!");
            }
            break;
            case WXErrCodeUserCancel:
            { //用户点击取消并返回
                NSLog(@"用户点击取消并返回");
            }
            break;
            case WXErrCodeSentFail:
            { //发送失败
                NSLog(@"发送失败");
            }
            break;
            case WXErrCodeUnsupport:
            { //微信不支持
                NSLog(@"微信不支持");
            }
            break;
            case WXErrCodeAuthDeny:
            { //授权失败
                NSLog(@"授权失败");
            }
            break;
            default:
            break;
        }
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,179评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,229评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,032评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,533评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,531评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,539评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,916评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,813评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,568评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,654评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,354评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,918评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,152评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,852评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,378评论 2 342

推荐阅读更多精彩内容

  • 实现支付宝支付的准备工作: 1.向支付宝签约,成为支付宝的商户 签约完成后,支付宝会提供一些必要的数据给我们 商户...
    Anson杨春安阅读 8,181评论 0 6
  • iOS支付 iOS支付分为两类,第三方支付和应用内支付(内购)。 第三方支付包括:支付宝支付、微信支付、银联支付、...
    请输入账号名阅读 6,173评论 3 22
  • 支付 1.支付宝和银联的支付流程 常用的支付方式有: 1、支付宝支付 https://openhome.alipa...
    丶逐渐阅读 1,670评论 3 11
  • 喜欢摄影,很简单的是因为它能记录下动人的瞬间,能在以后的日子久久回味当时按下快门的情景、心境。但是摄影并不等于照相...
    鑫韭缘设计阅读 15,421评论 0 4
  • 我时常会挑一些无人的、黑暗的房间,看着外面人来人往。 我已经穿着一身黑,更加特立独行了——加上一副令人畏惧的神情。...
    殒矽阅读 200评论 0 0