去年开发的小程序支付项目,一直没空梳理
流程文档地址
流程:
->用户在购物车页点击支付
-> 这时调起我们写的接口(用户所携带的商品id,金额等等参数携带到服务器)
-> 这时业务操作可以在这里实现,比如生成订单 -> 商户后台收到用户支付单,(需要填上支付成功回调接口)调用微信支付统一下单接口
-> 发送post请求"统一下单接口"返回预支付id:prepay_id (需要把参与签名的字段名为appid,partnerid,prepayid,noncestr,timestamp,package 返回给前端 然后唤起支付页面)
-> 前端支付成功后会走支付回调接口
demo工具类下载
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1
@Slf4j @Api(tags = "app支付功能") @RestController @RequestMapping("/payment/") public class PaymentController { @Resource private Environment environment; /*调用支付接口*/ @ApiOperation(httpMethod = "POST", value = "调用支付接口") @RequestMapping(value = "prePay", method = RequestMethod.POST) public Map<String, Object> prePay(@RequestBody Pay pay,HttpServletRequest request){ log.info("调用支付接口"); // 返回参数 Map<String, Object> resMap = new HashMap<>(); //获取当前请求ip地址 String ip = request.getHeader("x-forwarded-for"); if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){ ip = request.getHeader("Proxy-Client-IP"); } if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){ ip = request.getHeader("WL-Proxy-Client-IP"); } if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){ ip = request.getRemoteAddr(); } if(ip.indexOf(",")!=-1){ String[] ips = ip.split(","); ip = ips[0].trim(); } try { //业务需求 //前台传输一个openid--每个用户对应小程序都会生成一个独一无二的openid Map<String, Object> paraMap = new HashMap<>(); //查询房屋 String body = "cs"; // 封装11个必需的参数 //小程序ID paraMap.put("appid", environment.getProperty("wx.appId")); //商家ID paraMap.put("mch_id", environment.getProperty("wx.mchId")); //获取随机字符串 Nonce Str paraMap.put("nonce_str", WXPayUtil.generateNonceStr()); //商品名称 paraMap.put("body", body); //订单号 if (pay.getType()==MsgConsts.ZERO_STATUS) { //业务需求 // DecimalFormat df = new DecimalFormat("0.00%"); NumberFormat numberFormat = NumberFormat.getPercentInstance(); Double money = hpOrder.getTotalFee();//租金 numberFormat.setMinimumFractionDigits(2);//保留到小数点后2位,不设置或者设置为0表示不保留小数 //支付金额,单位:分,这边需要转成字符串类型,否则后面的签名会失败 String totalFee= BigDecimal.valueOf(money).multiply(new BigDecimal(100)).setScale(0,BigDecimal.ROUND_HALF_UP) + ""; paraMap.put("total_fee",totalFee); paraMap.put("out_trade_no", hpOrder.getOrderNo()); }else{ Double money = obOrder.getTotalFee(); //支付金额,单位:分,这边需要转成字符串类型,否则后面的签名会失败 String totalFee= BigDecimal.valueOf(money).multiply(new BigDecimal(100)).setScale(0,BigDecimal.ROUND_HALF_UP) + ""; paraMap.put("total_fee",totalFee); paraMap.put("out_trade_no", obOrder.getOrderNo()); } paraMap.put("spbill_create_ip", ip); // 此路径是微信服务器调用支付结果通知路径 paraMap.put("notify_url", "http://payment/callBack"); paraMap.put("trade_type", "JSAPI"); paraMap.put("openid", pay.getOpenid()); String sign = WXPayUtil.generateSignature(paraMap, environment.getProperty("wx.mchKey")); //生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。 paraMap.put("sign", sign); //将所有参数(map)转xml格式 String xml = WXPayUtil.mapToXml(paraMap); log.info("xml=: "+xml); // 统一下单 https://api.mch.weixin.qq.com/pay/unifiedorder String unifiedorder_url = WXPayConstants.UNIFIEDORDER_URL; log.info("统一下单接口unifiedorder_url:"+unifiedorder_url); //发送post请求"统一下单接口"返回预支付id:prepay_id String xmlStr = HttpClientUtil.doPostXml(unifiedorder_url, xml); //以下内容是返回前端页面的json数据 //预支付id String prepay_id = ""; if (xmlStr.indexOf("SUCCESS") != -1) { Map<String, Object> map = WXPayUtil.xmlToMap(xmlStr);//XML格式字符串转换为Map //获取封装好的预支付id prepay_id = map.get("prepay_id").toString(); log.info("prepay_id_1= "+prepay_id); } // 封装所需6个参数调支付页面 Map<String, Object> payMap = new HashMap<String, Object>(); payMap.put("appId", environment.getProperty("wx.appId")); payMap.put("timeStamp", WXPayUtil.getCurrentTimestamp()+"");//获取当前时间戳,单位秒 payMap.put("nonceStr", WXPayUtil.generateNonceStr());//获取随机字符串 Nonce Str payMap.put("signType", "MD5"); payMap.put("package", "prepay_id=" + prepay_id); //生成带有 sign 的 XML 格式字符串 String paySign = WXPayUtil.generateSignature(payMap, environment.getProperty("wx.mchKey")); payMap.put("paySign", paySign); // 封装正常情况返回数据 resMap.put("success",true); resMap.put("payMap",payMap); } catch (Exception e) { //异常删除订单 // 封装异常情况返回数据 log.info("调用统一订单接口错误"); resMap.put("success",false); resMap.put("message","调用统一订单接口错误"); e.printStackTrace(); } return resMap; } /*支付成功回调*/ @ApiOperation(httpMethod = "POST", value = "调用支付成功回调的地址") @RequestMapping(value = "callBack") public Result callBack(HttpServletRequest request, HttpServletResponse response){ Result result = new Result(); log.info("调用微信支付成功回调"); InputStream is = null; String resXml = ""; try { is = request.getInputStream();//获取请求的流信息(这里是微信发的xml格式所有只能使用流来读) String xml = WXPayUtil.inputStream2String(is); Map<String, Object> notifyMap = WXPayUtil.xmlToMap(xml);//将微信发的xml转map if(notifyMap.get("return_code").equals("SUCCESS")){ try { //业务需求 resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> "; BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream()); out.write(resXml.getBytes()); out.flush(); out.close(); System.err.println("返回给微信的值:"+resXml.getBytes()); is.close(); }catch (Exception e){ log.info("订单状态修改失败"); result.setMsg("订单状态修改失败"); } } } catch (Exception e) { e.printStackTrace(); } return result; } }
@Data public class Pay { public String openid; private Long id; private Long customerViewingRecordId; private Long userId; private Integer type; private String ordersNo; private String frontIdCardUrl; private String backIdCardUrl; private String contractUrl; private String idCard; private String contactName; private String phoneNumber; }