对于一个iOS的APP,如果有一些虚拟的商品或者服务需要通过在线支付来收费的话,一般有几种主流的选择。
如果是通过APP调用支付平台APP的思路的话,一个是调起支付宝客户端,一个则是调起微信支付。

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

今天记录一下客户端里面,如何支持微信支付。首先。我们要仔细阅读一下微信SDK的开发文档,了解一下整个支付的大概流程。
 
然后根据提示,把相应的SDK下载下来,所谓的SDK,也就是一个链接库和两个头文件,很简单。
下载完毕,需要把SDK导入到工程里面,并且配置一下工程。因为开发者文档已经有详细描述,这里就不再复述。

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

?
1
2
3
4
5
6
7
8
<code class = "hljs" vbscript= "" >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];</code>

这里的范例是一段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不变

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<code class = "hljs" objectivec= "" > //
//  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          @wx @@@@@@@@@@@@@@@@        //APPID
#define APP_SECRET      @                          //appsecret,看起来好像没用
//商户号,填写商户对应参数
#define MCH_ID          @@@@@@@@@@@
//商户API密钥,填写相应参数
#define PARTNER_ID      @12345678901234567890123456789012
//支付结果回调页面
#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
</foundation></code>

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
<code class = "hljs" objectivec= "" > //
//  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 签名字符串:
%@
,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>
];
     for (NSString *categoryId in keys) {
         [reqPars appendFormat:@<%@>%@<!--%@-->
, categoryId, [packageParams objectForKey:categoryId],categoryId];
     }
     [reqPars appendFormat:@<sign>%@</sign>
</xml>, sign];
     return [NSString stringWithString:reqPars];
}
//提交预支付
-(NSString *)sendPrepay:(NSMutableDictionary *)prePayParams
{
     NSString *prepayid = nil;
     //获取提交支付
     NSString *send      = [self genPackage:prePayParams];
     //输出Debug Info
     [self.debugInfo appendFormat: @API 链接:%@
, self.payUrl];
     [self.debugInfo appendFormat:@发送的xml:%@
, send];
     //发送请求post xml数据
     NSData *res = [WXUtil httpSend:self.payUrl method: @POST data:send];
     //输出Debug Info
     [self.debugInfo appendFormat:@服务器返回:
%@
,[[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:@获取预支付交易标示成功!
];
             }
         } else {
             self.lastErrCode = 1 ;
             [self.debugInfo appendFormat: @gen_sign =%@
    _sign=%@
,sign,send_sign];
             [self.debugInfo appendFormat:@服务器返回签名验证错误!!!
];
         }
     } else {
         self.lastErrCode = 2 ;
         [self.debugInfo appendFormat:@接口返回错误!!!
];
     }
     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失败!
];
         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=%@
,sign];
     //返回参数列表
     return signParams;
}
@end </code>

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<code class = "hljs" objectivec= "" >- ( 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];
}</code>


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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<code class = "hljs" objectivec= "" >-( 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 ;
         }
     }
}</code>


注意事项:
1)如果APP里面已经使用了ShareSDK,就有一些地方要注意。不要再重复导入微信的SDK,因为shareSDK里面的extend已经包括了微信的SDK。
2)微信本身是鼓励客户APP把签名算法放到服务器上面,这样信息就不容易被破解。但是如果客户APP本身没有服务器端,或者认为不需要放到服务器端,也可以直接把签名(加密)的部分直接放在APP端。Sample代码的注释有点乱,多次提到服务器云云,但是其实可以不这么做。
3)微信的price单位是分。注意下即可。
4)暂时想不到,以后想到了再记录。。

ios微信支付(ios自学笔记)相关推荐

  1. iOS微信支付的步骤以及问题描述

    本博客转自:http://www.jianshu.com/p/22a1ceee4d36 一.微信支付的步骤: 1.在微信开放平台注册.微信开放平台 2.创建一个App应用.(需要填写App的资料,审核 ...

  2. 微信小程序自学笔记-----文本和字体样式设置

    微信小程序自学笔记-----文本和字体样式设置 用于自己自学微信小程序知识点总结,新手小白一枚,请大佬勿喷 知识要点 · 各种字体样式属性命名及含义 · 利用style和class设置字体样式的方法 ...

  3. iOS微信支付集成 SDK1 7 5

    最近做了一个新项目,涉及到支付宝和微信支付,支付宝和微信都是业界的老大哥,相信大家都有所觉得文档.SDK都是各种坑吧(纯粹吐槽而已),这是继上篇支付宝支付集成后接着的微信支付集成. 1.微信商户申请步 ...

  4. ios微信支付失败 php,iOS 微信支付疑难杂症。

    哈喽,大家好,感谢您来阅读我的文章,作为iOS开发者,集成各类支付是家常便饭之事,今天我来给大家推荐几个不常见的微信支付问题. 最近,在项目的反馈版块中,收到了这么一位APP用户的反馈,他在使用我们家 ...

  5. java后台提供ios微信支付接口

    提供微信支付接口,需要先在微信开放平台(https://open.weixin.qq.com/)申请账号,进行开发者资格认证通过后创建应用. 创建成功后会有对应应用的AppID和AppSecret这个 ...

  6. unity iOS 微信支付 教程

    时间:2018.6 Unity版本:5.6.2 平台:Unity+iOS 微信SDK版本:2018.6月 1.8.2版本 适用人群:unity 开发,oc基础较弱 食用前提:请确保已经清楚官方的整个支 ...

  7. iOS微信支付集成全解

    所有信息的生成都在前端完成,包括对订单进行sign签名以及MD5签名加密(此方法相对来说有些复杂,没有官方给的方法简单). 注:官方给的是v3&v4支付流程,签名和加密都是在服务器端进行,由于 ...

  8. iOS 微信支付(服务端下单)

    一.注册登录微信开放平台账号 注册登录微信开放平台账号 添加一个用于支付/分享的移动应用,等待审核通过 前往管理中心-移动应用,为通过审核的移动应用申请支付功能,并等待审核通过 详细步骤参照微信支付官 ...

  9. php 苹果支付验证失败的原因,iOS 微信支付报错 支付验证签名失败

    可能的原因有两个: 一.传给微信sdk的参数中时间戳的格式错误. iOS中时间戳的格式要求是32位的int类型 image.png 一般服务器返回的是字符串需要强制转换成32位int类型的值 二.服务 ...

最新文章

  1. CentOS6.5下Gunicorn+Django+nginx部署的过程
  2. 维特根斯坦:为何夸大人工智能是对人性的贬损?
  3. knockout 学习笔记
  4. SQL Server pivot行列转换案例分析
  5. 无法删除所有指定的值_AutoCAD所有系统变量大全
  6. BGP 路由属性 公认可选 LOCAL_PREF
  7. 搭建负载均衡器nginx_为什么要使用NGINX作为负载均衡器?
  8. android浏览在线ppt,(简单好用的在线查看文件方法)Android 使用AgentWebView在线查看wrold丶ppt丶pdf等文件...
  9. 果蔬连连看java源代码_《基于Qt的连连看游戏的设计》-毕业论文.doc
  10. python从入门到精通需要多久-Python从入门到精通:一个月就够了
  11. mysql中主键数据类型_MySQL系列-详解mysql数据类型
  12. SaltStack 日志管理
  13. 连接局域网打印机简易教程
  14. NLP经典论文:Sequence to Sequence、Encoder-Decoder 、GRU 笔记
  15. python程序中每条语句以分号结尾,在Python程序中,每条语句末尾必须添加分号。...
  16. [MICO开发帖]MICOkit-Nucleo2.0开发连载贴——1、Micoder开发环境汉化
  17. 科幻的意义---《超新星纪元》后记
  18. 【C语言刷题】青蛙跳台阶
  19. 编程哲学-13条左右人生的金科玉律
  20. 区块链的发展及优缺点

热门文章

  1. java.mod.org,java
  2. 可消费性设计 让软件贴近用户
  3. vue按需加载组件-webpack require.ensure(转)
  4. WIN7下给C盘扩容
  5. python的官网都打不开了-python打不开
  6. ylbtech-Recode(记录)-数据库设计
  7. 给JIRA的工作流的每一个动作设置权限
  8. 四级语法1——简单句+并列句基本知识
  9. 给计算器写白盒测试用例Test case
  10. 仿美团饿了么选菜界面实现