文档真的很重要,不管是写还是看的人都应该认真点

本文主要写给公众平台h5支付开发者

真的,区分大小写,注意前面的参数就很少会出现问题了。

除了博客园,这篇文章的内容也放在这里自己的blog 【http://mimeay.cc/】

一、技术栈

  • 语言:Node
  • web框架 Express
  • 开发过程中依赖的模块
    1. express-xml-bodyparser 用于解析客户端post过来的xml格式的数据
    2. jstoxml 用于将js对象转换成xml
    3. xml2js 用户将xml转换js对象

 

二、微信开发之旅

 

  1. 注册商户平台

     这一步不设计开发。。。不详说。

  2. 支付目录设置

     微信公众平台-> 微信支付-> 开发配置,见微信官方图,具体细节见图片

alt

  3. 统一下单

    接口地址 :https://api.mch.weixin.qq.com/pay/unifiedorder

必须提交的参数

 appid 公众平台的appid
 attach 自定义消息
 body 商品类别【商品信息都可以】
 mch_id 商户平台的账号
 nonce_str 随机字符串,不超过32位
 notify_url 微信生成prepay_id后,会发送请求到所设置的回调地址
 out_trade_no 商户自定义生成的订单号,要保证不能重复
 spbill_create_ip 客户端的ip地址
 total_fee 支付的金额,以分为单位
 trade_type 交易类型 取值有JSAPI,NATIVE,APP ,因为是在h5页面触发支付,我选择了 JSAPI,要根据实际需求选择

根据项目,需要提交的参数

 openid 标示用户的id 【下面会贴相关链接】,如果是h5支付开发就一定要传

    以上提交参数要注意区分大小写【如果有大写的话】,针对几个参数说明一下 openid :用户授权后,微信会在你设置的授权回调域名后拼接code参数,通过这个code的值,然后通过code换取网页授权accesstoken。    

    openid。请注意,这里需要通过网页授权获取
    mch_id :注册商户平台后,微信会把商户号和密码发到指定的邮箱,这里用到商户账号而已

  4.代码

看代码啦

    后端代码

    为了展示,我把内容整合成在一起,实际开发并没有写在一起

  let config = {
    appid : '这里是appid',
    mch_id : '商户平台账号'
    notify_url : 'http://mimeay.cc/pay/receive',
    key : '在商户平台设置的'
 }

 let util = {
  createNonceStr () {
    return Math.random().toString(36).substr(2, 15);
  },
  createTimestamp  () {
    return parseInt(new Date().getTime() / 1000) + '';
  },
  raw (args) {
    let keys = Object.keys(args);
    keys = keys.sort()
    let newArgs = {};
    keys.forEach(function (key) {
      newArgs[key] = args[key];
    });
    let string = '';
    for (let k in newArgs) {
      string += '&' + k + '=' + newArgs[k];
    }
    string = string.substr(1);
    return string;
  },
  paySign (data,key) {
    let stringA,stringSignTemp,sign;
    stringA = module.exports.raw(data);
    stringSignTemp = stringA +'&key=' + key;
    sign = md5(stringSignTemp).toUpperCase();
    return sign;
  },
  post_request_xml(url,data){
    // 这里返回响应的内容
  }
 }

    生成 prepay_id

unified_order ({openid,ip,out_trade_no,total_fee,category = '纪念品',trade_type = 'JSAPI',device_info = 'WEB',attach = '网上商城'}){  
    let baseUrl = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
    let nonce_str = util.createNonceStr();
    // ip 可以从请求对象取
    // openid 通过客户端传递
    // out_trade_no 请求统一下单前先生成【不管用户最终是否完成支付】
    // total_fee 支付金额,单位为分,通过商品的id从数据库获取价格并转化成分【前端传递金额不可靠】
    // 其他参数不传则默认
    let data = {
      appid : config.appid,
      attach : attach,
      body : '公司名-' + category,
      mch_id : config.mch_id,
      nonce_str: nonce_str,
      notify_url : config.notify_url,
      openid : openid,
      out_trade_no : out_trade_no,
      spbill_create_ip : ip,
      total_fee:total_fee,
      trade_type : trade_type,
      device_info : device_info
    };
    let sign  = util.paySign(data,config.key);
    data['sign'] = sign;
    let xmlData = {
      xml : data
    }
    //jstoxml.toXML 是把js转换成xml的一个方法来的
    let xml = jstoxml.toXML(xmlData);
    // post_request_xml 
    return util.post_request_xml(baseUrl,xml).then((result)=>{
      const {sign,prepay_id,return_code,return_msg} = result;
      if(return_code == 'FAIL'){
        return {
          return_code,
          return_msg
        }
      }else{
        return {
          return_code,
          return_msg,
          prepay_id : prepay_id
        }
      }
    })
  }

    结合prepay_id生成客户端支付请求的参数,这里的 appId, timeStamp, nonceStr, package, signType严格区分大小写,我一开始没注意,把参数都小写了,结果导致客户端签名失败

wx_pay_sign (prepay_id){
    let baseUrl = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
    let nonce_str,data,sign,timestamp,xmlData,xml,_package;
    nonce_str = util.createNonceStr();
    timestamp = util.createTimestamp();
    _package = 'prepay_id=' + prepay_id;
    //这里的参数要区分大小写
    data  = {
      appId : config.appId,
      timeStamp : timestamp,
      nonceStr : nonce_str,
      package : _package,
      signType : 'MD5'
    }
    // appId, timeStamp, nonceStr, package, signType
    sign  = util.paySign(data,config.key);
    data['paySign'] = sign;
    return data;
  }

 

    到此,后端统一下单做的事情告一段落


    前端代码

    还记得wx_pay_sign函数做的事情吗,假设我们通过接口请求,拿到了wx_pay_sign返回的值

$.ajax({
  url : 'xxxxxxx',
  type : 'post',
  data : {
    openid : '通过授权拿到的openid'
  },
  success (json){
   const {code,data,msg} = json;
   if(code == 0){
     wx.chooseWXPay({
       timestamp: data.timeStamp, // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
       nonceStr: data.nonceStr, // 支付签名随机串,不长于 32 位
       package: data.package, // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=***)
       signType: data.signType, // 签名方式,默认为'SHA1',使用新版支付需传入'MD5'
       paySign: data.paySign, // 支付签名
       success: function (res) {
         // 支付成功后的回调函数
       }
    });
   }
  }
})

 

  5. 服务端可能出现的问题
  • XML格式错误 :如果出现错误,可以在发送请求前打印在本地看下 ,格式一般如下(只展示格式,不代表这就是请求发送的参数):
  <xml>
     <appid>xxxxx</appid> 
  </xml>

 

  • 签名错误: 一般会出现这个问题,可能是参数值为空,如下情况【appid为空】就会导致签名失败(只展示格式,不代表这就是请求发送的参数)
 <xml>
     <appid></appid>
     <mch_id>23123123</mch_id> 
 </xml>

 

  6. 客户端调试可能出现的问题
  • 支付签名失败 :正常来说是 appId, timeStamp, nonceStr, package, signType 签名的时候出现问题,一有可能是没有区分大小写,要么就是参数的值设置错误,参与签名的时候key一定要正确,如果没有的话,可以在商户平台设置
  7. 部分参考地址

三  联系我

   因为是为了展示给大家看,所以对代码有所修改,可能会出现错误,请大家理解。大家如果在做微信h5支付开发过程中有遇到问题,可加我qq【200909050】,我尽自己能力帮忙。

posted @ 2016-11-16 17:20 mimeay 阅读(1469) 评论(0) 推荐(1)
摘要: 1、启动 NODE_ENV=production node index.js 如果出现启动不了的情况,在该命令加sudo sudo NODE_ENV=production node index.js 2、备份数据以及切换数据库 备份数据 进入http://your.domain/labs 操作很简单 阅读全文
posted @ 2016-10-21 14:31 mimeay 阅读(96) 评论(0) 推荐(0)
摘要: 在这里就不谈redis的安装与启动啦,网上太多人写这个了。 从最近的一个项目【钻石夺宝】说起,如果大家有玩过一元夺宝或者全名夺宝的话,大概会知道如果参与人数多的话,每隔几秒、快的话每隔一秒都会新生成一期,虽然app的流量不多,但还要先确定好如何生成期数。 第一个问题来了,那怎样生成期数呢,自己想到的 阅读全文
posted @ 2016-08-23 15:35 mimeay 阅读(851) 评论(1) 推荐(2)
摘要: 公司数据库迁移,所以补充了一下知识:1 集合的导入和导出命令行帮助 mongoexport --help导出导出newsServer 数据库下 news 集合mongoexport -d newsServer -c news -o D:\data\news.json同上,但只指定部分字段(id,ti... 阅读全文
posted @ 2016-01-25 10:27 mimeay 阅读(142) 评论(0) 推荐(0)
摘要: 自建新闻从无到有。当然,评论、点赞也是一步步来的。关于点赞的设计,也是会挖过坑,也把坑给填了。需求是这样子的:点赞有【好球,警告】两个选项,点了可以取消,取消之后可以再点,也可以直接从好球直接切换到警告,同时好球和警告数也要跟着更新采用数据库:mongodb,关于字段设计,一开始我是这样子的:sup... 阅读全文
posted @ 2015-11-13 23:33 mimeay 阅读(1904) 评论(7) 推荐(3)
摘要: 函数式编程,属于编程范式的一种1 函数是第一公民,可以返回值,也可以作为其他函数的参数//console是一个函数function con(v){ console.log(v)}// execute 也是一个函数function execute(fn){ fn(1)}//将con函数作为参数传进... 阅读全文
posted @ 2015-09-05 01:12 mimeay 阅读(159) 评论(0) 推荐(0)
摘要: 在js里面,可以有两种方法来定义正则表达式,第一种是通过字变量的形式,第二种则是通过构造函数的形式:1 字变量形式,格式是长这样子的 var expression = /pattern/flag patter : 模式,该部分可以是简单或复杂的正则表达式 flag : 标记,有 3个标志,分别是 g... 阅读全文
posted @ 2015-09-03 21:53 mimeay 阅读(116) 评论(0) 推荐(0)
摘要: 1 创建一个新的日期对象,如果不带参数,则对象自动获得当前的日期和时间var d = new Date()2 如果需要指定特定的日期,则可以通过Date.parse() 或者 Date().UTC(),返回时间戳作为 new Date()的参数Date.parse() 用法:var time =... 阅读全文
posted @ 2015-09-03 21:33 mimeay 阅读(224) 评论(0) 推荐(0)
摘要: 1 默认:application/x-www-form-urlencoded在网页表单中可设置 enctype的值,如果不设,默认是application/x-www-form-urlencoded在ajax中,默认也是以上的编码,如果需要其他格式的编码,如application/json ,则应该... 阅读全文
posted @ 2015-08-08 02:39 mimeay 阅读(123) 评论(0) 推荐(0)
摘要: 以下为最近有关git的学习笔记1 公司要求用 bitbucket ,个人偏向于用github,本地仓库要怎样设置才能推送到两个远程库呢? 以项目Qdemo为例子,在Qdemo目录下: 关键词: git remote add [shortName] [url] git remote add toGit... 阅读全文
posted @ 2015-08-05 22:37 mimeay 阅读(155) 评论(0) 推荐(0)
点击右上角即可分享
微信分享提示