iOS客户端支持微信支付

对于一个iOS的APP,如果有一些虚拟的商品或者服务需要通过在线支付来收费的话,一般有几种主流的选择。

如果是通过APP调用支付平台APP的思路的话,一个是调起支付宝客户端,一个则是调起微信支付。

实际上,从代码的角度,调起支付APP就是把一些关键的参数通过一定方式打包成为一个订单,然后发送到支付平台的服务器。所以,只要搞清楚了参数设置,搞清楚了每个支付平台的SDK里面一些关键API的使用,基本上就可以很简单的支持支付。

今天记录一下客户端里面,如何支持微信支付。首先。我们要仔细阅读一下微信SDK的开发文档,了解一下整个支付的大概流程。

http://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=8_1

然后根据提示,把相应的SDK下载下来,所谓的SDK,也就是一个链接库和两个头文件,很简单。

下载完毕,需要把SDK导入到工程里面,并且配置一下工程。因为开发者文档已经有详细描述,这里就不再复述。

http://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=8_5

从文档看到,调起微信支付其实最核心的是一下这么一段

PayReq *request = [[[PayReq alloc] init] autorelease];
request.partnerId = @"10000100";
request.prepayId= @"1101000000140415649af9fc314aa427";
request.package = @"Sign=WXPay";
request.nonceStr= @"a462b76e7436e98e0ed6e13c64b4fd1c";
request.timeStamp= @"1397527777";
request.sign= @"582282D72DD2B03AD892830965F428CB16E7A256";
[WXApi sendReq:request];

这里的范例是一段hardcode,真正使用的时候,参数都需要自行传入。

为了搞清楚如何使用API,我们可以下载Sample代码。不过,这个sample代码应该是微信的实习生写的,而且应该是一个对于C++比较熟悉,对于ObjectC比较陌生的实习生。。。代码风格可以看出很多东西哈。。所以这个sample读起来总觉得有点奇怪。当然,写出这个demo也是需要不错的水平,因为这个sample不仅仅是一些API的调用,还包括了一些算法的实现,MD5之类的。

看懂了sample之后,一般可以自己重构一下,成为自己APP里面的一个Manager类。

我是在2015 5 23下载的微信Sampel代码,里面包括有:

ApiXml.h

ApiXml.m

WXUtil.h

WXUtil.m

payRequestHandler.h

payRequestHandler.m

如果比较看重命名规范的OC程序猿,就会觉得这个payRequestHandler类非常别扭,不符合camel命名规则,而且handler这个词更偏向于c++风格。我就以这个类为原型,重构了一下,并改装成一个传参的方法,供自己的APP调用。APP里面卖商品,一般就是商品名字,价格两个关键参数。所以这个重构的方法也只是提供这两个参数的接口。

ApiXml.h && ApiXml.m && WXUtil.h && WXUtil.m不变

//
//  WechatPayManager.h
//
//  Created by HuangCharlie on 5/24/15.
//
//

#import <Foundation/Foundation.h>
#import "WXUtil.h"
#import "ApiXml.h"
#import "WXApi.h"

// 账号帐户资料
// 更改商户把相关参数后可测试
#define APP_ID          @"wx427a2f57bc4456d1"        //APPID
#define APP_SECRET      @""                          //appsecret,看起来好像没用
//商户号,填写商户对应参数
#define MCH_ID          @"1242316102"
//商户API密钥,填写相应参数
#define PARTNER_ID      @"12345678901234567890123456789020"
//支付结果回调页面
#define NOTIFY_URL      @"http://wxpay.weixin.qq.com/pub_v2/pay/notify.v2.php"
//获取服务器端支付数据地址(商户自定义)(在小吉这里,签名算法直接放在APP端,故不需要自定义)
#define SP_URL          @"http://wxpay.weixin.qq.com/pub_v2/app/app_pay.php"

@interface WechatPayManager : NSObject
{
}

//预支付网关url地址
@property (nonatomic,strong) NSString* payUrl;

//debug信息
@property (nonatomic,strong) NSMutableString *debugInfo;
@property (nonatomic,assign) NSInteger lastErrCode;//返回的错误码

//商户关键信息
@property (nonatomic,strong) NSString *appId,*mchId,*spKey;

//初始化函数
-(id)initWithAppID:(NSString*)appID
             mchID:(NSString*)mchID
             spKey:(NSString*)key;

//获取当前的debug信息
-(NSString *) getDebugInfo;

//获取预支付订单信息(核心是一个prepayID)
- (NSMutableDictionary*)getPrepayWithOrderName:(NSString*)name
                                         price:(NSString*)price
                                        device:(NSString*)device;

@end
//
//  WechatPayManager.m
//
//  Created by HuangCharlie on 5/24/15.
//
//

#import "WechatPayManager.h"

@implementation WechatPayManager

//初始化函数
-(id)initWithAppID:(NSString*)appID mchID:(NSString*)mchID spKey:(NSString*)key
{
    self = [super init];
    if(self)
    {
        //初始化私有参数,主要是一些和商户有关的参数
        self.payUrl    = @"https://api.mch.weixin.qq.com/pay/unifiedorder";
        if (self.debugInfo == nil){
            self.debugInfo  = [NSMutableString string];
        }
        [self.debugInfo setString:@""];
        self.appId = appID;//微信分配给商户的appID
        self.mchId = mchID;//
        self.spKey = key;//商户的密钥
    }
    return self;
}

//获取debug信息
-(NSString*) getDebugInfo
{
    NSString *res = [NSString stringWithString:self.debugInfo];
    [self.debugInfo setString:@""];
    return res;
}

//创建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=%@", self.spKey];
    //得到MD5 sign签名
    NSString *md5Sign =[WXUtil md5:contentString];

    //输出Debug Info
    [self.debugInfo appendFormat:@"MD5签名字符串:\n%@\n\n",contentString];

    return md5Sign;
}

//获取package带参数的签名包
-(NSString *)genPackage:(NSMutableDictionary*)packageParams
{
    NSString *sign;
    NSMutableString *reqPars=[NSMutableString string];
    //生成签名
    sign        = [self createMd5Sign:packageParams];
    //生成xml的package
    NSArray *keys = [packageParams allKeys];
    [reqPars appendString:@"<xml>\n"];
    for (NSString *categoryId in keys) {
        [reqPars appendFormat:@"<%@>%@</%@>\n", categoryId, [packageParams objectForKey:categoryId],categoryId];
    }
    [reqPars appendFormat:@"<sign>%@</sign>\n</xml>", sign];

    return [NSString stringWithString:reqPars];
}

//提交预支付
-(NSString *)sendPrepay:(NSMutableDictionary *)prePayParams
{
    NSString *prepayid = nil;

    //获取提交支付
    NSString *send      = [self genPackage:prePayParams];

    //输出Debug Info
    [self.debugInfo appendFormat:@"API链接:%@\n", self.payUrl];
    [self.debugInfo appendFormat:@"发送的xml:%@\n", send];

    //发送请求post xml数据
    NSData *res = [WXUtil httpSend:self.payUrl method:@"POST" data:send];

    //输出Debug Info
    [self.debugInfo appendFormat:@"服务器返回:\n%@\n\n",[[NSString alloc] initWithData:res encoding:NSUTF8StringEncoding]];

    XMLHelper *xml  = [[XMLHelper alloc] autorelease];

    //开始解析
    [xml startParse:res];

    NSMutableDictionary *resParams = [xml getDict];

    //判断返回
    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_code = 0;

                [self.debugInfo appendFormat:@"获取预支付交易标示成功!\n"];
            }
        }else{
            self.lastErrCode = 1;
            [self.debugInfo appendFormat:@"gen_sign=%@\n   _sign=%@\n",sign,send_sign];
            [self.debugInfo appendFormat:@"服务器返回签名验证错误!!!\n"];
        }
    }else{
        self.lastErrCode = 2;
        [self.debugInfo appendFormat:@"接口返回错误!!!\n"];
    }

    return prepayid;
}

- (NSMutableDictionary*)getPrepayWithOrderName:(NSString*)name
                                         price:(NSString*)price
                                        device:(NSString*)device
{
    //订单标题,展示给用户
    NSString* orderName = name;
    //订单金额,单位(分)
    NSString* orderPrice = price;//以分为单位的整数
    //支付设备号或门店号
    NSString* orderDevice = device;
    //支付类型,固定为APP
    NSString* orderType = @"APP";
    //发器支付的机器ip,暂时没有发现其作用
    NSString* orderIP = @"196.168.1.1";

    //随机数串
    srand( (unsigned)time(0) );
    NSString *noncestr  = [NSString stringWithFormat:@"%d", rand()];
    NSString *orderNO   = [NSString stringWithFormat:@"%ld",time(0)];

    //================================
    //预付单参数订单设置
    //================================
    NSMutableDictionary *packageParams = [NSMutableDictionary dictionary];

    [packageParams setObject: self.appId  forKey:@"appid"];       //开放平台appid
    [packageParams setObject: self.mchId  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: NOTIFY_URL  forKey:@"notify_url"];  //支付结果异步通知
    [packageParams setObject: orderNO      forKey:@"out_trade_no"];//商户订单号
    [packageParams setObject: orderIP      forKey:@"spbill_create_ip"];//发器支付的机器ip
    [packageParams setObject: orderPrice   forKey:@"total_fee"];       //订单金额,单位为分

    //获取prepayId(预支付交易会话标识)
    NSString *prePayid;
    prePayid = [self sendPrepay:packageParams];

    if(prePayid == nil)
    {
        [self.debugInfo appendFormat:@"获取prepayid失败!\n"];
        return nil;
    }

    //获取到prepayid后进行第二次签名
    NSString    *package, *time_stamp, *nonce_str;
    //设置支付参数
    time_t now;
    time(&now);
    time_stamp  = [NSString stringWithFormat:@"%ld", now];
    nonce_str = [WXUtil md5:time_stamp];
    //重新按提交格式组包,微信客户端暂只支持package=Sign=WXPay格式,须考虑升级后支持携带package具体参数的情况
    //package       = [NSString stringWithFormat:@"Sign=%@",package];
    package         = @"Sign=WXPay";
    //第二次签名参数列表
    NSMutableDictionary *signParams = [NSMutableDictionary dictionary];
    [signParams setObject: self.appId  forKey:@"appid"];
    [signParams setObject: self.mchId  forKey:@"partnerid"];
    [signParams setObject: nonce_str    forKey:@"noncestr"];
    [signParams setObject: package      forKey:@"package"];
    [signParams setObject: time_stamp   forKey:@"timestamp"];
    [signParams setObject: prePayid     forKey:@"prepayid"];

    //生成签名
    NSString *sign  = [self createMd5Sign:signParams];

    //添加签名
    [signParams setObject: sign         forKey:@"sign"];

    [self.debugInfo appendFormat:@"第二步签名成功,sign=%@\n",sign];

    //返回参数列表
    return signParams;
}

@end

然后,在需要调用微信支付的Controller里面,新建一个方法。在合适的地方调用。这个方法里面利用WechatPayManager这个类进行了初始化和参数封装,然后把上述的核心代码(PayReq那一段)

- (void)wxPayWithOrderName:(NSString*)name price:(NSString*)price
{
    //创建支付签名对象 && 初始化支付签名对象
    WechatPayManager* wxpayManager = [[[WechatPayManager alloc]initWithAppID:APP_ID mchID:MCH_ID spKey:PARTNER_ID] autorelease];

    //获取到实际调起微信支付的参数后,在app端调起支付
    //生成预支付订单,实际上就是把关键参数进行第一次加密。
    NSString* device = [[UserManager defaultManager]userId];
    NSMutableDictionary *dict = [wxpayManager getPrepayWithOrderName:name
                                                               price:price
                                                            device:device];

    if(dict == nil){
        //错误提示
        NSString *debug = [wxpayManager getDebugInfo];
        return;
    }

    NSMutableString *stamp  = [dict objectForKey:@"timestamp"];

    //调起微信支付
    PayReq* req             = [[[PayReq alloc] init]autorelease];
    req.openID              = [dict objectForKey:@"appid"];
    req.partnerId          = [dict objectForKey:@"partnerid"];
    req.prepayId            = [dict objectForKey:@"prepayid"];
    req.nonceStr            = [dict objectForKey:@"noncestr"];
    req.timeStamp          = stamp.intValue;
    req.package            = [dict objectForKey:@"package"];
    req.sign                = [dict objectForKey:@"sign"];

//        BOOL flag = [WXApi sendReq:req];
    BOOL flag = [WXApi safeSendReq:req];
}


再者,支付完成了需要调用一个delegate,这个delegate方便个性化显示支付结果。一般直接把这两个delegate放在AppDelegate就好了。因为有一些其他内容也是需要在AppDelegate里面实现,省的分开找不到。

-(void) onResp:(BaseResp*)resp
{
    //启动微信支付的response
    NSString *strMsg = [NSString stringWithFormat:@"errcode:%d", resp.errCode];
    if([resp isKindOfClass:[PayResp class]]){
        //支付返回结果,实际支付结果需要去微信服务器端查询
        switch (resp.errCode) {
            case 0:
                strMsg = @"支付结果:成功!";
                break;
            case -1:
                strMsg = @"支付结果:失败!";
                break;
            case -2:
                strMsg = @"用户已经退出支付!";
                break;
            default:
                strMsg = [NSString stringWithFormat:@"支付结果:失败!retcode = %d, retstr = %@", resp.errCode,resp.errStr];
                break;
        }
    }
}


注意事项:

1)如果APP里面已经使用了ShareSDK,就有一些地方要注意。不要再重复导入微信的SDK,因为shareSDK里面的extend已经包括了微信的SDK。

2)微信本身是鼓励客户APP把签名算法放到服务器上面,这样信息就不容易被破解。但是如果客户APP本身没有服务器端,或者认为不需要放到服务器端,也可以直接把签名(加密)的部分直接放在APP端。Sample代码的注释有点乱,多次提到服务器云云,但是其实可以不这么做。

3)微信的price单位是分。注意下即可。

4)暂时想不到,以后想到了再记录。。

时间: 2024-08-01 06:31:37

iOS客户端支持微信支付的相关文章

iOS客户端的微信支付接入

对于一个iOS的APP,如果有一些虚拟的商品或者服务需要通过在线支付来收费的话,一般有几种主流的选择. 如果是通过APP调用支付平台APP的思路的话,一个是调起支付宝客户端,一个则是调起微信支付. 实际上,从代码的角度,调起支付APP就是把一些关键的参数通过一定方式打包成为一个订单,然后发送到支付平台的服务器.所以,只要搞清楚了参数设置,搞清楚了每个支付平台的SDK里面一些关键API的使用,基本上就可以很简单的支持支付. 今天记录一下客户端里面,如何支持微信支付.首先.我们要仔细阅读一下微信SD

WordPress按钮秒支付插件发布,支持微信支付,支付宝,银联,京东,苏宁,易宝支付

痛点: 我们用WordPress建设网站和开发移动应用,有时候我们其实不需要太多的流程,只是需要一个收款通道,但是可能对支持的渠道更加关注,特别是手机应用.所以WordPress按钮秒支付插件诞生了,它是一个快速收款工具,只需要在后台填入金额,然后把这个按钮放在任意网站,或者微信,或者微博里面,就能实现收款. 特点: 功能特征 WordPress按钮支付插件,收款更轻松 轻松接入;插件模式即插即用,按需接入   自适应显示平台,兼容PC端页面和移动端H5页面   主流支付渠道全覆盖,支持微信支付

Vultr新推出3.5美元/月套餐,并且支持微信支付了

先前Vultr重新推出了2.5美元/月的套餐,但是不支持IPv4,所以不那么受国内朋友的欢迎,迫于压力,这不最近就推出了3.5美元/月的套餐了,这个套餐是支持IPv4的,有需要的朋友可以上车了,https://www.vultr.com/?ref=7122677. 另外值得一提的是,Vultr也支持微信支付了: 原文地址:https://www.f2ecoder.net/851.html 原文地址:https://www.cnblogs.com/xiaoweihuang/p/9751148.ht

iOS通俗易懂的微信支付接入和爬坑指南,十分钟轻松搞完

现在基本所有的App都会接入支付宝支付以及微信支付,也有很多第三方提供给你 SDK帮你接入,但是这种涉及到支付的东西还是自己服务器搞来的好一些,其实搞懂了 逻辑非常的简单,下面直接给大家说说下基本流程和接入需要注意的东西. 前期准备(这个东西一般来讲我们不需要来操心,但是还是稍微介绍下) 1.到微信开放平台注册账号点击打开链接 2.进入管理中心------移动应用------创建移动应用----根据页面完善应用资料 3.审核过后,通过应用详情页面,查看应用详情,查看AppID和AppSecret

iOS中 最新微信支付/最全的微信支付教程详解 韩俊强的博客

每日更新关注:http://weibo.com/hanjunqiang  新浪微博! 亲们, 首先让我们来看一下微信支付的流程吧. 1. 注册微信开放平台,创建应用获取appid,appSecret,申请支付功能,申请成功之后会返回一些参数. 2. 下载微信支付sdk 3. 客户端请求订单,后台与微信后台交互,返回给客户端支付参数 4. 调用微信客户端,由微信客户端和微信服务器打交道: 5. 客户端和服务端都会收到支付结果:(前台消息不可靠,我们需要去后台验证,如果后台没有收到支付通知,后台去微

iOS中 最新微信支付/最全的微信支付教程具体解释 韩俊强的博客

亲们, 首先让我们来看一下微信支付的流程吧. 1. 注冊微信开放平台,创建应用获取appid,appSecret,申请支付功能,申请成功之后会返回一些參数. 2. 下载微信支付sdk 3. client请求订单,后台与微信后台交互.返回给client支付參数 4. 调用微信client.由微信client和微信server打交道: 5. client和服务端都会收到支付结果:(前台消息不可靠.我们须要去后台验证,假设后台没有收到支付通知.后台去微信server验证然后将结果返回给client)

iOS应用之微信支付集成-直接前端集成

所有信息的生成都在前端完成,包括对订单进行sign签名以及MD5签名加密(此方法相对来说有些复杂,没有官方给的方法简单).注:官方给的是v3&v4支付流程,签名和加密都是在服务器端进行,由于没有对接过,所以就暂时没有办法分享. 1. 准备工作 工程所需的微信支付SDK,如下图: 微信支付所需的相关用户信息(由用户申请) //微信支付商户号 #define MCH_ID @"YOUR_MCH_ID" //开户邮件中的(公众账号APPID或者应用APPID) #define WX_

iOS开发集成微信支付

首先需要理清楚流程: 1.用户使用APP客户端,选择商品下单. 2.商户客户端(就是你做的APP)将用户的商品数据传给商户服务器,请求生成支付订单. 3.商户后台调用统一下单API向微信的服务器发送请求,微信服务器生成预付单,并生成一个prepay_id返回给商户后台. 4.商户后台将这个prepay_id返回给商户客户端. 5.用户点击确认支付,这时候商户客户端调用SDK打开微信客户端,进行微信支付. 6.微信客户端向微信服务器发起支付请求并返回支付结果(他们之间交互用的就是prepay_id

iOS 支付宝、微信支付的集成记录

一.前言 最近项目需求需要集成支付宝和微信支付,以前没有接触过,这次正好乘着这次机会了解下. 总的来说,客户端的工作不多,无非就是就是集成SDK,然后获取后端传来的参数,吊起支付,回调处理等. 先附上官方文档链接: 支付宝:https://docs.open.alipay.com/204 微信支付:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_1 废话不多说,开始吧. 二.支付宝 1.先集成SDK pod 'Alipay