微信支付接入方式总共分为四种:公众号支付、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形式的处理结果直接输出,返回给微信后台。
至此,微信扫码支付功能已完整实现,感谢各位的耐心阅读,谢谢!
来源:oschina
链接:https://my.oschina.net/u/1010775/blog/656511