在这个二维码风起云涌的时代,在线支付已经成为潮流,没事扫一扫,打赏一下我也不介意。
酝酿
谈坑之前先聊一聊模式一的大体流程,模式一的适用场景一般为自助售卖机或者固定价格的商品的线下交易居多。
当然我能想象到的线上交易,比如,对于固定价格的商品进行支付,由商户交易回调中设置短信或者邮件激活码之类的做验证。
感觉模式一更像是一个无状态的支付,二维码中的信息可用的只有product_id而已,对于用户-订单来说并没有任何关联。
如果小伙伴们有类似使用场景,还望告知。
生成二维码
首先根据商品ID以及其他信息,由商户后台生成二维码。
参数列表
部分代码
/**
* 二维码生成器(扫码支付模式一)
* 创建者 小柒2012
* 创建时间 2017年8月2日
*/
public class qrCodeUtil {
public static void main(String[] args) {
SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
//封装通用参数
ConfigUtil.commonParams(packageParams);
packageParams.put("product_id", "20170731");//真实商品ID
packageParams.put("time_stamp", PayCommonUtil.getCurrTime());
//生成签名
String sign = PayCommonUtil.createSign("UTF-8", packageParams, ConfigUtil.API_KEY);
//组装二维码信息(注意全角和半角:的区别 狗日的腾讯)
StringBuffer qrCode = new StringBuffer();
qrCode.append("weixin://wxpay/bizpayurl?");
qrCode.append("appid="+ConfigUtil.APP_ID);
qrCode.append("&mch_id="+ConfigUtil.MCH_ID);
qrCode.append("&nonce_str="+packageParams.get("nonce_str"));
qrCode.append("&product_id=20170731");
qrCode.append("&time_stamp="+packageParams.get("time_stamp"));
qrCode.append("&sign="+sign);
//生成二维码
ZxingUtils.getQRCodeImge(qrCode.toString(), 256, "D:\\weixn.png");
}
}
回调设置
配置回调地址
公众平台微信支付公众号支付授权目录、扫码支付回调URL配置入口已于8月1日迁移至商户平台(pay.weixin.qq.com)。迁移后,原有配置数据不会受影响,你可在商户平台查看和配置。带来的不便敬请谅解。
回调方法
简单说一下,回调方法中具体的逻辑,如下:
- 读取xml参数
- 解析xml成map
- 校验签名是否正确
- 统一下单
- 验证下单是否成功
- 通知微信下单成功
- 最终用户授权支付
- 当然后面还有一系列的交易逻辑(非此方法)
/**
* 模式一支付回调URL(生成二维码见 qrCodeUtil)
* 商户支付回调URL设置指引:进入公众平台-->微信支付-->开发配置-->扫码支付-->修改
* @Author 科帮网
* @param request
* @param response
* @throws Exception void
* @Date 2017年8月3日
* 更新日志
* 2017年8月3日 科帮网 首次创建
*
*/
@SuppressWarnings({ "unchecked", "rawtypes"})
@RequestMapping(value = "bizpayurl")
public void bizpayurl(HttpServletRequest request, HttpServletResponse response) throws Exception {
logger.info("模式一支付回调URL");
//读取参数
InputStream inputStream = request.getInputStream();
StringBuffer sb = new StringBuffer();
String s;
BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
while ((s = in.readLine()) != null) {
sb.append(s);
}
in.close();
inputStream.close();
//解析xml成map
Map<String, String> map = XMLUtil.doXMLParse(sb.toString());
//过滤空 设置 TreeMap
SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
Iterator it = map.keySet().iterator();
while (it.hasNext()) {
String parameter = (String) it.next();
String parameterValue = map.get(parameter);
String v = "";
if (null != parameterValue) {
v = parameterValue.trim();
}
packageParams.put(parameter, v);
}
//判断签名是否正确
if (PayCommonUtil.isTenpaySign("UTF-8", packageParams, ConfigUtil.API_KEY)) {
//统一下单
SortedMap<Object, Object> params = new TreeMap<Object, Object>();
ConfigUtil.commonParams(params);
//随即生成一个 入库 走业务逻辑
String out_trade_no=Long.toString(System.currentTimeMillis());
params.put("body", "模式一扫码支付");// 商品描述
params.put("out_trade_no", out_trade_no);// 商户订单号
params.put("total_fee", "100");// 总金额
params.put("spbill_create_ip", "192.168.1.66");// 发起人IP地址
params.put("notify_url", notify_url);// 回调地址
params.put("trade_type", "NATIVE");// 交易类型
String paramsSign = PayCommonUtil.createSign("UTF-8", params, ConfigUtil.API_KEY);
params.put("sign", paramsSign);// 签名
String requestXML = PayCommonUtil.getRequestXml(params);
String resXml = HttpUtil.postData(ConfigUtil.UNIFIED_ORDER_URL, requestXML);
Map<String, String> payResult = XMLUtil.doXMLParse(resXml);
String returnCode = (String) payResult.get("return_code");
if("SUCCESS".equals(returnCode)){
String resultCode = (String) payResult.get("result_code");
if("SUCCESS".equals(resultCode)){
logger.info("(订单号:{}生成微信支付码成功)",out_trade_no);
String prepay_id = payResult.get("prepay_id");
SortedMap<Object, Object> prepayParams = new TreeMap<Object, Object>();
ConfigUtil.commonParams(params);
prepayParams.put("prepay_id", prepay_id);
prepayParams.put("return_code", "SUCCESS");
prepayParams.put("result_code", "SUCCESS");
String prepaySign = PayCommonUtil.createSign("UTF-8", prepayParams, ConfigUtil.API_KEY);
prepayParams.put("sign", prepaySign);
String prepayXml = PayCommonUtil.getRequestXml(prepayParams);
//通知微信 预下单成功
BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
out.write(prepayXml.getBytes());
out.flush();
out.close();
}else{
String errCodeDes = (String) map.get("err_code_des");
logger.info("(订单号:{}生成微信支付码(系统)失败[{}])",out_trade_no,errCodeDes);
}
}else{
String returnMsg = (String) map.get("return_msg");
logger.info("(订单号:{} 生成微信支付码(通信)失败[{}])",out_trade_no,returnMsg);
}
}else{
logger.info("签名错误");
}
}
部署项目,启动,扫码,如下:
挖坑
其实如果你做过扫码支付模式二,在处理模式一的一些流程上还是比较顺利的。
无论是签名认证,统一下单还是二维码生成基础组间已经都具备了。
唯一dog ri的腾讯,官方文档给的回调URL中冒号是中文全角,导致扫描二维码直接显示回调URL。