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

推荐阅读更多精彩内容

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