文档章节

微信扫码支付功能实现(java版)

萧十一郎君
 萧十一郎君
发布于 2016/04/11 10:44
字数 1324
阅读 9407
收藏 33

微信支付接入方式总共分为四种:公众号支付、APP支付、扫码支付和刷卡支付。其中扫码支付最符合公司当前业务场景,扫码支付有两种接入方式(请参考扫码支付开发步骤),本文选择模式二方式接入扫码支付功能。

  模式二接入方式分为两个步骤:生成二维码图片和编写支付回调接口。下面分别详细阐述这两个步骤:

1、生成二维码图片

  二维码图片实际上是对一个预交易的url进行编码而生成的,因此要想得到二维码图片,必须先得到这个预交易的url,消费者扫描二维码图片后打开的网址链接就是这个url。那如何得到这个url呢?调用微信支付统一下单API。调用该API的各参数这里不赘述了,文档写的很详细。下面先贴上示例代码:

/**
 * 获取微信支付二维码图片
 * @param userId 当前用户ID,用来当作商户订单号,防止重复支付
 * @return
 */
public BufferedImage getWeChatPaymentImage(Integer userId) {
    HashMap<String, String> paramMap = new HashMap<>();
    paramMap.put("appid", "xxxxxxxxxxxx"); //appid:每个公众号都有一个appid
    paramMap.put("mch_id", "11111111111"); //商户号:开通微信支付后分配
    //随机数
    paramMap.put("nonce_str", RandomUtil.getRandomString(32, RandomUtil.LETTER_AND_NUMBER_RANGE));  
    paramMap.put("body", "香辣烤翅"); //商品描述
    //商户订单号:用户id + “|” + 随机16位字符
    paramMap.put("out_trade_no", userId + "|" + RandomUtil.getRandomString(16, RandomUtil.LETTER_AND_NUMBER_RANGE)); 
    paramMap.put("total_fee", 1000); //金额必须为整数  单位为分
    paramMap.put("spbill_create_ip", PaymentUtil.localIp()); //本机的Ip
    paramMap.put("notify_url", this.notifyUrl); //支付成功后,回调地址
    paramMap.put("trade_type", "NATIVE"); //交易类型
    paramMap.put("product_id", "100001"); // 商户根据自己业务传递的参数 当trade_type=NATIVE时必填
    //根据微信签名规则,生成签名。随机参数可以在商户后台管理系统中进行设置。
    paramMap.put("sign", PaymentUtil.getSignature(paramMap, "beGPax3F1EtxxxxxxofcerMRqNvt9XJ2"));

    String xmlData = PaymentUtil.mapToXml(paramMap);//把参数转换成XML数据格式

    String codeUrl = getCodeUrl(xmlData);   //获取二维码链接

    return PaymentUtil.encodeQrcode(codeUrl);   //将二维码链接信息编码成二维码图片,用BufferedImage对象表示

}

/**
 * 获取二维码链接
 * @param xmlData
 * @return
 */
private String getCodeUrl(String xmlData) {
    String resXml = HttpUtil.postData(WX_PAYMENT_API_URL, xmlData);
    String code_url = "";
    Map<String, Object> map;
    try {
        map = PaymentUtil.getMapFromXML(resXml);
        Object returnCode = map.get("return_code");
        if(PaymentUtil.SUCCESS.equals(returnCode)) {
            Object resultCode = map.get("result_code");
            if(PaymentUtil.SUCCESS.equals(resultCode)) {
                code_url = map.get("code_url").toString();
            }
        }
    } catch (Exception e) {
        return "";
    }

    return code_url;
}

  getWeChatPaymentImage方法中有4个参数需要特别说明,这4个都是调用统一下单API需要的参数。appid是每一个公众号都有的唯一标识,登录公众号即可看到;mch_id,即商户号,只有开通微信支付功能之后才有;参数签名sign的随机数可以通过商户后台设置,务必保证设置的随机数与代码中的一致,否则API调用报错:签名错误;最后一个特别要说明的参数是:notify_url,回调接口地址,也是本文接下来的阐述主题,请读者继续往下看。

2、编写回调接口

  回调接口是干嘛的呢?微信支付完成之后,会自动调用该接口,反馈此次支付的结果。举个例子:现有一个订单,支付状态为未支付,扫码支付之后,微信后台自动调用自定义的回调接口,将支付结果信息传递回来,接口方法通过判断支付结果参数,修改订单的支付状态。支付结果反馈形式参考:支付结果通用通知。下面是回调接口示例代码:

public void wechatPayNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
    String ret = "";

    // 解析结果存储在HashMap
    Map<String, String> map = new HashMap<String, String>();
    InputStream inputStream = request.getInputStream();
    // 读取输入流
    SAXReader reader = new SAXReader();
    Document document = reader.read(inputStream);
    // 得到xml根元素
    Element root = document.getRootElement();
    // 得到根元素的所有子节点
    List<Element> elementList = root.elements();

    // 遍历所有子节点
    for (Element e : elementList)
        map.put(e.getName(), e.getText());

    // 释放资源
    inputStream.close();
    inputStream = null;

    Map<String, String> retMap = new HashMap<String, String>();

    String returnCode = map.get("return_code");
    if("SUCCESS".equals(returnCode)) {
        String resultCode = map.get("result_code");
        String outTradeNo = map.get("out_trade_no");
        if("SUCCESS".equals(resultCode)) {
            //下面为业务逻辑处理:如果支付成功,则修改该用户的付费状态,更新付费时间。
            String[] temp = StringUtils.split(outTradeNo, "|");
            Integer userId = Integer.parseInt(temp[0]);
            UcUser ucUser = new UcUser();
            ucUser.setId(userId);
            ucUser.setIsPay(1);
            ucUser.setPayTime(new Date());
            ucUserManager.updateUserPayment(ucUser);

            retMap.put("return_code", "SUCCESS");
            retMap.put("return_msg", "OK");
            log.info("支付成功!out_trade_no:" + outTradeNo + ", result_code:" + resultCode);
        } else {
            String errCode = map.get("err_code");
            log.error("支付失败!out_trade_no:" + outTradeNo + ",result_code:" + resultCode + ", err_code:" + errCode);
            retMap.put("return_code", returnCode);
            retMap.put("return_msg", resultCode);
        }
    } else {
        String returnMsg = map.get("return_msg");
        retMap.put("return_code", returnCode);
        retMap.put("return_msg", returnMsg);
        log.error("支付通信失败!" + returnMsg);
    }

    ret = PaymentUtil.mapToXml(retMap);

    response.getWriter().print(ret);
}

  回调接口必须通过request.getInputStream()方法获取支付返回的结果参数,最后使用response.getWriter().print()方法将xml形式的处理结果直接输出,返回给微信后台。

  至此,微信扫码支付功能已完整实现,感谢各位的耐心阅读,谢谢!

文章作者: xiaohui249
本文链接: http://javatech.wang/index.php/archives/84/
版本所有 ©转载时必须以链接形式注明作者和原始出处

本文转载自:http://javatech.wang/index.php/archives/84/

共有 人打赏支持
萧十一郎君

萧十一郎君

粉丝 62
博文 34
码字总数 19501
作品 0
昌平
程序员
私信 提问
加载中

评论(4)

萧十一郎君
萧十一郎君

引用来自“仰望星空1111”的评论

楼主,有没有网页版直接掉微信的!

没有做
仰望星空1111
楼主,有没有网页版直接掉微信的!
chenzb
chenzb
PaymentUtil 工具类,能否提供出来那参考下那,谢谢!
lchxosc
lchxosc
感谢提供思路 少走了不少弯路
POS机端应用无法调用微信支付宝支付,该怎么实现订单的支付

我有一个PHP的微信公众号内的商城,主要是做加油的。现在的业务场景是这样的:车主在平台上下了订单加完油产生一个二维码,加油工拿POS机上我们自己开发的应用去扫码核销该订单,核销完以后选...

披着羊皮的狼王
2018/12/04
0
0
JavaWeb项目对接微信扫码支付

项目地址:wxpay github repo 当我接到对接微信支付的开发任务时,我的第一反应是查看官方文档,但是官方文档并不是十分简洁易读(可能是我能力有限),且可能由于开发者习惯不同或业务场景不同...

可乐味儿的白衬衫
2018/05/29
0
0
轻量级支付整合轻松嵌入任何系统 - pay-java-parent

轻量级支付模块集成(微信支付,友店扫码,支付宝,富友,银联, payoneer皮卡 )支付整合,app,扫码,即时到帐刷卡付条码付、支持多种支付类型多支付账户,支付与业务完全剥离,简单几行代码即可实...

egzosn
2017/02/17
0
8
java支付宝和微信的移动支付,扫码支付后台开发

[主要内容] 按照官方提供的SDK开发支付后端 仅限JAVA语言的服务端开发 支付宝移动支付构建移动端需要的参数和签名,支付宝异步通知方法 支付宝网站扫码支付,完成异步通知和同步跳转 微信移动...

互联网-民工
2016/05/30
6
0
微信支付和支付宝支付

[主要内容] 按照官方提供的SDK开发支付后端 仅限JAVA语言的服务端开发 支付宝移动支付构建移动端需要的参数和签名,支付宝异步通知方法 支付宝网站扫码支付,完成异步通知和同步跳转 微信移动...

黔中伯爵
2016/06/04
2
0

没有更多内容

加载失败,请刷新页面

加载更多

Confluence 6 升级中的一些常见问题

升级的时候遇到了问题了吗? 如果你想尝试重新进行升级的话,你需要首先重新恢复老的备份。不要尝试再次对 Confluence 进行升级或者在升级失败后重新启动老的 Confluence。 在升级过程中的一...

honeymoose
37分钟前
0
0
C++随笔(四)Nuget打包

首先把自己编译好的包全部准备到一个文件夹 像这样 接下来新建一个文本文档,后缀名叫.nuspec 填写内容 <?xml version="1.0"?><package xmlns="http://schemas.microsoft.com/packaging/201......

Pulsar-V
今天
2
0
再谈使用开源软件搭建数据分析平台

三年前,我写了这篇博客使用开源软件快速搭建数据分析平台, 当时收到了许多的反馈,有50个点赞和300+的收藏。到现在我还能收到一些关于dataplay2的问题。在过去的三年,开源社区和新技术的发...

naughty
今天
3
0
Python3的日期和时间

python 中处理日期时间数据通常使用datetime和time库 因为这两个库中的一些功能有些重复,所以,首先我们来比较一下这两个库的区别,这可以帮助我们在适当的情况下时候合适的库。 在Python文...

编程老陆
今天
2
0
分布式面试整理

并发和并行 并行是两个任务同时进行,而并发呢,则是一会做一个任务一会又切换做另一个任务。 临界区 临界区用来表示一种公共资源或者说是共享数据,可以被多个线程使用,但是每一次,只能有...

群星纪元
今天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部