最近的项目在对接微信支付,所以抽出一些时间,将方法总结一下:

欢迎加群交流:724225958

官方开发文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=1_1;

文档中分别对支付账户(参数)、接口规则、支付业务场景,流程、API做了详细介绍,并提供了SDK以及DEMO ,大部分有一定基础的开发或研发,均可参照文档及其demo,一步一步的梳理整合。只需注意最重要的2点即可:

①  js调起微信支付时,需二次加签,统一下单返回的签名无法调起微信支付

②  二次加签的加签类型SignType是‘HMAC-SHA256’,此处不可为‘MD5’

微信支付业务场景最终都可以抽象为技术上的2个点:前端发起支付请求,后端响应请求。

首先,从前端入手,整合微信JS-SDK,

官方文档:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115。

按照文档指示的步骤开发即可,此处不做详细介绍。在支付调用页面注入jssdk即可:

/*** 配置jssdk*/
function jssdk(url){if(null == url || url == ''){url = location.href.split('#')[0];}$.ajax({type:'post',        url:buildUrl('villa/jssdk'),    data:{url:url},cache:false,    dataType:'json',    success:function(data){if (data.code == '200') {wx.config({debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。appId: data.obj.appId, // 必填,公众号的唯一标识timestamp: data.obj.timestamp, // 必填,生成签名的时间戳nonceStr: data.obj.nonceStr, // 必填,生成签名的随机串signature: data.obj.signature,// 必填,签名,见附录1jsApiList: ['checkJsApi', 'chooseWXPay'
] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2});wx.ready(function(){// config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。});wx.error(function(res){// config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。});}}    });
}
此函数方法可以调整,将使用的接口作为参数,传入函数,灵活使用。
发起支付的函数为:
function recharge(){//业务代码省略//统一下单$.ajax({type:'post',        url:buildUrl('villa/a/wxpay/unifiedOrder'),    data:{openid:’’,//用户openid必需,或后台单独获取totalFee:totalFee},cache:false,    dataType:'json',    success:function(data){if (data.code == '200') {var obj = data.obj;var prepay_id = 'prepay_id=' + obj.prepay_id;//再次生成支付签名$.ajax({type:'post',        url:buildUrl('villa/a/wxpay/generatePaySign'),    data:{prepay_id:prepay_id  //预支付订单id,二次加签必需},cache:false,    dataType:'json',    success:function(data){//业务代码if (data.code == '200') {var pay_obj = data.obj;//调起微信支付chooseWXPay(pay_obj.timeStamp, pay_obj.nonceStr, prepay_id, pay_obj.signType, pay_obj.paySign);} else {$("#errorMessage").html(data.msg);loadDialog('dialog');}}    });} else {$("#errorMessage").html(data.msg);loadDialog('dialog');}}    });
}

如果签名正确,基本就可以看到微信支付调用效果。

下面为后端整合代码:

微信支付DEMO中给出了一个统一下单的调用示例:
public class WXPayExample {public static void main(String[] args) throws Exception {MyWXPayConfig config = new MyWXPayConfig();WXPay wxpay = new WXPay(config);Map<String, String> data = new HashMap<String, String>();data.put("body", "腾讯充值中心-QQ会员充值");data.put("out_trade_no", "2016090910595900000012");data.put("device_info", "");data.put("fee_type", "CNY");data.put("total_fee", "1");data.put("spbill_create_ip", "123.12.12.123");data.put("notify_url", "http://www.masaike.com/wxpay/notify");
//        data.put("trade_type", "NATIVE");  // 此处指定为扫码支付data.put("trade_type", "JSAPI");data.put("openid", "masaike");data.put("product_id", "12");try {Map<String, String> resp = wxpay.unifiedOrder(data);System.out.println(resp);} catch (Exception e) {e.printStackTrace();}
//成功返还结果
//      {result_code=SUCCESS, sign=A7D34A90163C12BAFB887FC752B9EF22D8C75C05BE581917E0E2806C2E73F2F1, mch_id=1496788202, prepay_id=wx201802142113131695db46a40709589533, return_msg=OK, appid=’masaike’, nonce_str=ULtDr28SorCa5haa, return_code=SUCCESS, trade_type=JSAPI}}
}
调用失败的原因可能只有一个,那就是没有找到你的支付安全证书,证书的获取方法在上一篇博文《微信服务号之公众号支付配置》:http://blog.csdn.net/soongp/article/details/79405161查看。
WxPayInfo类为自己封装的一个微信参数类,用于传参:
public class WXPayInfo {/**用户唯一标识*/private String openid;/**充值金额*/private String totalFee;/**商品描述*/private String body;public String getOpenid() {return openid;}public void setOpenid(String openid) {this.openid = openid;}public String getTotalFee() {return totalFee;}public void setTotalFee(String totalFee) {this.totalFee = totalFee;}public String getBody() {return body;}public void setBody(String body) {this.body = body;}}
WxPayConfig为微信支付配置抽象类,WxPayConfig为其实现,说白了就是一些必要参数的获取,例如:appid(服务号账号id),appsecret(服务号秘钥),mchid(支付账户账号),key(支付秘钥,在微信商户平台配置),算了,还是把代码粘出来吧:
public abstract class WXPayConfig {/*** 获取appid* <p>Discription:[获取微信公众号唯一标识:appid]</p>* Created on 2018年1月24日* @return* @author:[soong]*/public abstract String getAppID();/*** 获取 Mch ID* <p>Discription:[获取微信支付商户号:商户申请微信支付后,由微信支付分配的商户收款账号]</p>* Created on 2018年1月24日* @return* @author:[soong]*/public abstract String getMchID();/*** 获取 API 密钥* <p>Discription:[交易过程生成签名的密钥,仅保留在商户系统和微信支付后台,不会在网络中传播;* 商户可根据邮件提示登录微信商户平台进行设置;* 也可按一下路径设置:微信商户平台(pay.weixin.qq.com)-->账户中心-->账户设置-->API安全-->密钥设置]</p>* Created on 2018年1月24日* @return* @author:[soong]*/public abstract String getKey();/*** 获取开发者密码* <p>Discription:[AppSecret是APPID对应的接口密码,用于获取接口调用凭证access_token时使用* 在微信支付中,先通过OAuth2.0接口获取用户openid,此openid用于微信内网页支付模式下单接口使用]</p>* Created on 2018年1月24日* @return* @author:[soong]*/public abstract String getAppsecret();/*** 获取商户证书内容** @return 商户证书内容*/public abstract InputStream getCertStream();/*** HTTP(S) 连接超时时间,单位毫秒** @return*/public int getHttpConnectTimeoutMs() {return 6*1000;}/*** HTTP(S) 读数据超时时间,单位毫秒** @return*/public int getHttpReadTimeoutMs() {return 8*1000;}/*** 获取WXPayDomain, 用于多域名容灾自动切换* @return*/public abstract IWXPayDomain getWXPayDomain();/*** 是否自动上报。* 若要关闭自动上报,子类中实现该函数返回 false 即可。** @return*/public boolean shouldAutoReport() {return true;}/*** 进行健康上报的线程的数量** @return*/public int getReportWorkerNum() {return 6;}/*** 健康上报缓存消息的最大数量。会有线程去独立上报* 粗略计算:加入一条消息200B,10000消息占用空间 2000 KB,约为2MB,可以接受** @return*/public int getReportQueueMaxSize() {return 10000;}/*** 批量上报,一次最多上报多个数据** @return*/public int getReportBatchSize() {return 10;}}public class MyWXPayConfig extends WXPayConfig{/**证书二级制字节流*/private byte[] certData;private static MyWXPayConfig INSTANCE;public MyWXPayConfig() throws Exception{String certPath = GlobalConstants.getCertPath();File file = new File(certPath);InputStream certStream = new FileInputStream(file);this.certData = new byte[(int) file.length()];certStream.read(this.certData);certStream.close();}public static MyWXPayConfig getInstance() throws Exception{if (INSTANCE == null) {synchronized (MyWXPayConfig.class) {if (INSTANCE == null) {INSTANCE = new MyWXPayConfig();}}}return INSTANCE;}@Overridepublic String getAppID() {return GlobalConstants.getAppid();}@Overridepublic String getMchID() {return GlobalConstants.getMchID();}@Overridepublic String getKey() {return GlobalConstants.getApiKey();}@Overridepublic InputStream getCertStream() {ByteArrayInputStream certBis = new ByteArrayInputStream(this.certData);return certBis;}@Overridepublic IWXPayDomain getWXPayDomain() {return WXPayDomainSimpleImpl.instance();}@Overridepublic String getAppsecret() {return GlobalConstants.getAppSecret ();}}GlobalConstants.getAppid();等方法的内部实现,其实就是获取了配置文件中的参数,只不过多封装了一层做了垂直解耦。
控制器WxPayController:
@Controller
@RequestMapping(value = "${adminPath}/wxpay")
public class WXPayController extends BaseController{@Autowiredprivate WxPayService wxPayService;/*** 除被扫支付场景以外,商户系统先调用该接口在微信支付服务后台生成预支付交易单,* 返回正确的预支付交易回话标识后再按扫码、JSAPI、APP等不同场景生成交易串调起支付* <p>Discription:[商户server调用统一下单接口请求订单,生成预支付交易单]</p>* Created on 2018年2月15日* @param wxPayInfo* @param req* @param resp* @return* @author:[soong]*/@RequestMapping(value = "unifiedOrder", method = RequestMethod.POST)@ResponseBodypublic Json<Map<String, String>> unifiedOrder(WXPayInfo wxPayInfo, HttpServletRequest req, HttpServletResponse resp) {Json<Map<String, String>> json = new Json<Map<String, String>>();if (StringUtils.isBlank(wxPayInfo.getOpenid())) {json.setFalid(Json.PARAM_EXCEPTION, "用户标识openid不能为空");logger.info("用户标识openid不能为空");return json;}if (StringUtils.isBlank(wxPayInfo.getTotalFee())) {json.setFalid(Json.PARAM_EXCEPTION, "充值金额totalFee不能为空");logger.info("充值金额totalFee不能为空");return json;}try {json = wxPayService.unifiedOrder(wxPayInfo, json);} catch (BusinessException e) {}
//      {result_code=SUCCESS, sign=B7C41F5D85E0F4579BC839FE9C864518B1C5F4DEF900F57AA5E4B1367159074F, mch_id=1496788202, prepay_id=wx20180214211654305951b3800266781582, return_msg=OK, appid=wxf79c25d666f76ee0, nonce_str=BDhKVLBBzukDxPAl, return_code=SUCCESS, trade_type=JSAPI}return json;}/*** * <p>Discription:[支付结果通知]</p>* Created on 2018年2月16日* @param openid* @param request* @param response* @return* @author:[soong]*/@RequestMapping(value = "notify", method = RequestMethod.POST)@ResponseBodypublic Json<Map<String, String>> notify(String openid, HttpServletRequest request, HttpServletResponse response) {Json<Map<String, String>> json = new Json<Map<String, String>>();
//      if (StringUtils.isBlank(openid)) {//
//          json.setFalid(Json.PARAM_EXCEPTION, "openid不能为空");
//          return json;
//      }logger.info("支付结果通知");
//      try {
//          json = wxPayService.unifiedOrder(json);
//      } catch (BusinessException e) {
//
//      }
//      {result_code=SUCCESS, sign=B7C41F5D85E0F4579BC839FE9C864518B1C5F4DEF900F57AA5E4B1367159074F, mch_id=1496788202, prepay_id=wx20180214211654305951b3800266781582, return_msg=OK, appid=wxf79c25d666f76ee0, nonce_str=BDhKVLBBzukDxPAl, return_code=SUCCESS, trade_type=JSAPI}return json;}/*** * <p>Discription:[再次生成签名,js发起微信支付请求]</p>* Created on 2018年2月16日* @param openid* @param request* @param response* @return* @author:[soong]*/@RequestMapping(value = "generatePaySign", method = RequestMethod.POST)@ResponseBodypublic Json<Map<String, String>> generatePaySign(HttpServletRequest request, HttpServletResponse response) {Json<Map<String, String>> json = new Json<Map<String, String>>();String prepay_id = request.getParameter("prepay_id");//预支付订单idif (StringUtils.isBlank(prepay_id)) {json.setFalid(Json.PARAM_EXCEPTION, "预支付订单ID(prepay_id)不能为空");}String appId = GlobalConstants.getAppid();//公众号idString timeStamp = GlobalConstants.getTimestamp();//时间戳String nonceStr = GlobalConstants.getUUID();//随机字符串String signType = SignType.HMACSHA256 + "";//加签类型Map<String, String> map = new HashMap<String, String>();map.put("appId", appId);map.put("timeStamp", timeStamp);map.put("nonceStr", nonceStr);map.put("package", prepay_id);map.put("signType", signType);try {String paySign = WXPayUtil.generateSignature(map, GlobalConstants.getApiKey(), SignType.HMACSHA256);map.put("paySign", paySign);json.setSuccess(Json.SUCCESS, "再次生成支付签名成功", map);} catch (Exception e) {e.printStackTrace();}return json;}/*** * <p>Discription:[查询订单]</p>* Created on 2018年2月17日* @param request* @param response* @return* @author:[soong]*/@RequestMapping(value = "orderQuery", method = RequestMethod.POST)@ResponseBodypublic Json<Map<String, String>> orderQuery(HttpServletRequest request, HttpServletResponse response) {Json<Map<String, String>> json = new Json<Map<String, String>>();try {} catch (Exception e) {e.printStackTrace();}return json;}
}
业务实现类WxPayServcieImpl:
@Service("wxPayService")
public class WxPayServiceImpl implements WxPayService{@Overridepublic Json<Map<String, String>> unifiedOrder(WXPayInfo wxPayInfo, Json<Map<String, String>> json) {User u = UserUtils.getUser();Map<String, String> data = new HashMap<String, String>();String body = wxPayInfo.getBody();if (StringUtils.isBlank(body)) {body = GlobalConstants.getWxpayBody();}data.put("body", body);//商品描述String out_trade_no = String.valueOf(WXPayUtil.getCurrentTimestamp());data.put("out_trade_no", out_trade_no);//商户订单号data.put("fee_type", WXPayConstants.FEE_TYPE);//标价币种data.put("total_fee", "1");
//        String totalFee = wxPayInfo.getTotalFee();
//        totalFee = String.valueOf(Integer.valueOf(totalFee) * 100);
//        data.put("total_fee", totalFee);//标价金额,单价为分(乘以100转化为元)data.put("spbill_create_ip", u.getLoginIp());//终端IPdata.put("notify_url", GlobalConstants.getNotifyUrl());//通知地址data.put("trade_type", WXPayConstants.TRADE_TYPE_JSAPI);//支付类型data.put("openid", wxPayInfo.getOpenid());//公众号支付必需:用户标识try {MyWXPayConfig config = new MyWXPayConfig();WXPay wxpay = new WXPay(config);Map<String, String> resp = wxpay.unifiedOrder(data);//通信成功判断if (null != resp.get("return_code") && resp.get("return_code").toString().equals("SUCCESS")) {//交易成功判断if (null != resp.get("result_code") && resp.get("result_code").toString().equals("SUCCESS")) {json.setSuccess(Json.SUCCESS, "统一下单成功", resp);} else {String err_code_des = "系统异常,请用相同参数重新调用";if (null != resp.get("err_code_des")) {err_code_des = resp.get("err_code_des").toString();}json.setFalid(Json.BUSINESS_EXCEPTION, "统一下单失败-交易失败:" + err_code_des, resp);}} else {String return_msg = "签名失败";if (null != resp.get("return_msg")) {return_msg = resp.get("return_msg").toString();}json.setFalid(Json.BUSINESS_EXCEPTION, "统一下单失败-通信失败:" + return_msg, resp);}} catch (Exception e) {e.printStackTrace();json.setFalid(Json.BUSINESS_EXCEPTION, "统一下单失败:" + e.getMessage());throw new BusinessException(Json.BUSINESS_EXCEPTION, "统一下单失败:" + e.getMessage());}return json;}}

其实,只要肯花时间,仔细认真的阅读文档即可,整合jssdk,能看懂支付DEMO就ok,剩下的就是一些细节调试,以及在调试过程中遇到问题,并解决的一个过程;

交流QQ群:724225958

微信服务号开发-整合微信支付相关推荐

  1. 微信服务号开发-获取用户位置信息

    微信服务号开发-获取用户位置信息 在微信公众号开发的中,获取用户位置信息是非常常见的功能需求,通过用户的位置信息,可以做一些地图导航,以及基于LBS的营销活动. 下面将介绍微信服务号获取用户位置信息的 ...

  2. Springmvc集成jfinal微信 微信服务号开发

    最近研究微信服务号开发,发现jfinal家封装的SDK还是不错的,于是就定下来用它了. 那么问题来了:git上有demo,那么如何集成到自己的项目中呢?研究研究呗.我们框架使用的是springmvc, ...

  3. 【微信服务号开发】01.接入指南

    前言 当作为小白,来开发微信的时候,只依据官方文档来开发是很痛苦的,怎么配置,怎么编写代码文件,怎么让映射到外网访问,问题很多,比较痛苦. 下面内容来解决这些痛点,有不懂的问题,可以在下面留言评论哦. ...

  4. 微信服务号开发的完整人性化版攻略

    前言: 本次要讲述的是一个本人完整微信服务号开发的经验分享,微信服务号的作品:请搜索微信号:zjaisino,名称:爱信诺Aisino一站式服务平台.(这里声明,这不是打广告,只是为了方便各位开花攻城 ...

  5. 微信服务号开发时获取授权遇到的问题

    1.问题 (遇到的问题)微信服务号开发时获取授权遇到的问题 公众平台返回原始数据为: 错误代码-40164,错误信息-invalid ip, not in whitelist hint: [59FKq ...

  6. 视频教程-基于python的微信公众号开发教程-微信开发

    基于python的微信公众号开发教程 微信企业号星级会员.10多年软件从业经历,国家级软件项目负责人,主要从事软件研发.软件企业员工技能培训.已经取得计算机技术与软件资格考试(软考)--"信 ...

  7. 多个微信服务号对接一个微信商户号流程

    微信服务号发送红包需要开通对应的微信商户号,如果多个微信服务号开通多个微信商户号会非常麻烦,项目管理上也会非常乱,写下多个微信服务号对接一个微信商户号的流程. 先登录微信商户平台,产品中心下的APPI ...

  8. 微信公众 mysql回复图片_微信公众号开发之微信公共平台消息回复类实例

    本文实例讲述了微信公众号开发之微信公共平台消息回复类.分享给大家供大家参考.具体如下: 微信公众号开发代码我在网上看到了有不少,其实都是大同小义了都是参考官方给出的demo文件进行修改的,这里就给各位 ...

  9. 微信公众号开发之微信公众平台与公众号第三方平台区别

    微信公众号开发分为微信公众平台和公众号第三方平台. 首先需要一个认证服务号,然后在设置,基本配置里面配置公众号开发信息和服务器配置. 这是我们会得到开发者ID(AppID),开发者密码(AppSecr ...

最新文章

  1. PHP Warning: File upload error - unable to create a temporary file in Unknown on line 0
  2. Matlab之M程序与M函数
  3. mysql枫叶_mysql总结
  4. hexo的yelee主题使用katex引擎(markdown渲染加速)
  5. Eclipse集成Maven功能
  6. 塑料模具计算机辅助设计,注塑模具3D计算机辅助设计系统
  7. RMSProp算法和AdaDelta算法
  8. 配电室站房监控改造工程 环境辅助控制系统
  9. yocto的hello world
  10. 统计|(可/无)重复双因素方差分析一般步骤及分析表
  11. 2.5万字讲解DDD领域驱动设计,从理论到实践掌握DDD分层架构设计,赶紧收藏起来吧
  12. 分布式路由策略(Hash取余,一致性Hash,Hash槽)
  13. 通达OA 工作流电子签章 盖章Or手写
  14. 快钱 支付 php,GitHub - laraveler/omnipay-99bill: 基于Omnipay的快钱支付SDK
  15. 服务器换主板后找不到磁盘,服务器硬盘频繁丢失的非常奇怪无解问题
  16. 【开源】硬件/软件i2c两种方式移植u8g2单色图形库驱动0.96吋OLED
  17. Java:浅谈InputStream的close方法
  18. 死磕RDP协议,从截图和爆破说起
  19. avue 按时间导出excel
  20. 10kv电压互感器型号_高压互感器型号含义

热门文章

  1. 【玉溪——飞机大战】
  2. Dotween SetEase Ease缓动函数
  3. mysql 数据拦截器_拦截器中操作数据库
  4. adb 下删除锁屏密码
  5. 想突破现状,就得付出更大的努力!
  6. SkiaSharp 之 WPF 自绘 粒子花园(案例版)
  7. 医疗管理系统-PDF报表生成
  8. 怎样找到项目打包后的文件
  9. 插画软件_插画家眼中的色彩互动
  10. Android OTG数据不能写入