当前位置: 移动技术网 > 移动技术>移动开发>IOS > IOS客户端接入微信支付

IOS客户端接入微信支付

2019年07月24日  | 移动技术网移动技术  | 我要评论

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

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

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

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

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

<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不变

<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>
<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那一段)

<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里面实现,省的分开找不到。

<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客户端接入微信支付的全部内容,希望大家喜欢。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网