1.下单方法
@ResponseBody
@ApiOperation("微信浏支付/(H5)")
@ApiImplicitParams({
@ApiImplicitParam(name = "productId", value = "产品ID", required = true),
@ApiImplicitParam(name = "client", value = "下单客户端:0=h5、1=pc、2.app", required = true),
@ApiImplicitParam(name = "passagewayId", value = "支付通道ID", required = true)
})
@ApiResponses(value = {
@ApiResponse(code = 500, message = "系统错误"),
@ApiResponse(code = 200, message = "{code:响应码,message:描述,data:参考model类{}}", response = BaseResult.class)})
@GetMapping(value = "wxPayH5")
public BaseResult wxPayH5(HttpServletRequest request, BeanOrder beanOrder) {
try {
Map<String, Object> result = new HashMap();
result.put("success", false);
Long userId = null;
BeanProduct product = productService.findBeanProductId(beanOrder.getProductId());
beanOrder.setAmount(product.getPrice());
if (EmptyUtil.isEmpty(beanOrder.getUserId())) {
userId = accessTokenService.getAppUserId(request);
beanOrder.setUserId(userId);
} else {
userId = beanOrder.getUserId();
}
BigDecimal order_price = beanOrder.getAmount().multiply(new BigDecimal(100));
logger.info("1.发起微信浏支付=========================================================》》productId=" + beanOrder.getProductId() + "=======》》 userId" + userId);
if (EmptyUtil.isEmpty(beanOrder) || EmptyUtil.isEmpty(beanOrder.getAmount())
|| EmptyUtil.isEmpty(beanOrder.getProductId())
|| EmptyUtil.isEmpty(beanOrder.getUserId())
|| EmptyUtil.isEmpty(beanOrder.getClient())
|| EmptyUtil.isEmpty(beanOrder.getPassagewayId())) {
return new BaseResult(ResultEnum.ZC_120000.getCode(), "参数缺失", null);
}
// 价格 注意:价格的单位是分
User user = userService.requiresNewSelectByPrimaryKey(userId);
beanOrder.setPassagewayId(beanOrder.getPassagewayId());
beanOrder.setCount(product.getNumber());
beanOrder.setMobile(user.getMobile());
beanOrder.setAmount(order_price);
beanOrder.setStatus(1);
beanOrder.setOrderType(1);
//下单客户端:0=h5、1=pc、2=app
String client = "MWEB";
if (beanOrder.getClient().intValue() == 1) {
client = "NATIVE";
} else if (beanOrder.getClient().intValue() == 2) {
client = "APP";
}
logger.info("2.拼接统一下单地址参数===================================================》》\n");
Map<String, String> packageParams = AuthUtil.getPackageParams(request);
BaseResult baseResult = beanOrderService.addBeanOrder(beanOrder);
String orderNumber = AuthUtil.getOrderNumber(beanOrder.getOrderId().toString());
beanOrder.setOrderNumber(orderNumber);
BaseResult baseResult1 = beanOrderService.modifyBeanOrder(beanOrder);
if (!ResultEnum.QX_000000.getCode().equals(baseResult.getCode()) ||
!ResultEnum.QX_000000.getCode().equals(baseResult1.getCode()) ) {
return new BaseResult(ResultEnum.ZC_120000.getCode(), "支付失败", null);
}
packageParams.put("out_trade_no", orderNumber); // 商户系统内部的订单号,32个字符
packageParams.put("appid", beanOrder.getClient().intValue() == 2 ? AuthUtil.APP_APPID : AuthUtil.APPID); // 微信分配的公众账号ID(企业号corpid即为此appId)
packageParams.put("total_fee", beanOrder.getAmount().intValue()+""); // 支付金额,单位分
packageParams.put("detail", JSON.toJSONString(beanOrder)); // 商品详情
packageParams.put("notify_url", AuthUtil.NOTIFY_URL + "?orderId=" + beanOrder.getOrderId()); // 此路径是微信服务器调用支付结果通知路径随意写
packageParams.put("trade_type", client); // 支付类型
String sign = WXPayUtil.generateSignature(packageParams, AuthUtil.PATERNERKEY);
packageParams.put("sign", sign); // 签名
String requestXML = WXPayUtil.mapToXml(packageParams); // 将所有参数(map)转xml格式
logger.info("3.发送post请求统一下单接口返回预支付id:prepay_id ======================》》\n" + requestXML);
String resXml = HttpRequest.httpsRequest(AuthUtil.UFDODER_URL, "POST", requestXML);
Map<String, String> map = WXPayUtil.xmlToMap(resXml);
logger.info("4.响应参数 =============================================================》》\n" + resXml);
String urlString = URLEncoder.encode((beanOrder.getClient().intValue() == 2 ? AuthUtil.BACK_URL : AuthUtil.BACK_URL_H5) + packageParams.get("nonce_str"), "GBK");
Map<String, String> retsult = new HashMap<>();
if ("SUCCESS".equals((String) map.get("result_code"))) {
if (beanOrder.getClient().intValue() == 2) {
retsult.put("appid", AuthUtil.APP_APPID);
retsult.put("partnerid", AuthUtil.MCHID); //微信商户账号
retsult.put("prepayid", map.get("prepay_id")); //预付订单id
retsult.put("package", "Sign=WXPay"); //扩展字段
retsult.put("noncestr", map.get("nonce_str")); //自定义不重复的长度不长于32位
retsult.put("timestamp", DateUtils.getCurrentTimeStamp());
String signAgain = WXPayUtil.generateSignature(retsult, AuthUtil.PATERNERKEY);
retsult.put("sign", signAgain); // 签名
logger.info("5.请求结束 =============================================================》》\n" + retsult);
return new BaseResult(ResultEnum.QX_000000.getCode(), retsult);
} else {
String url = beanOrder.getClient() == 1 ? map.get("code_url") : map.get("mweb_url");
String retsult_url = url + "&redirect_url=" + urlString;
retsult.put("url", retsult_url);
retsult.put("orderNumber", packageParams.get("out_trade_no"));
logger.info("5.请求结束 =============================================================》》\n" + retsult);
return new BaseResult(ResultEnum.QX_000000.getCode(), retsult);
}
} else {
return new BaseResult(ResultEnum.ZC_120000.getCode(), "请求异常", null);
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
return new BaseResult(ResultEnum.ZC_120000.getCode());
}
}
2.回调方法
@ResponseBody
@ApiOperation("支付完成的回调函数")
@RequestMapping("/payNotify")
public void payNotify(HttpServletRequest request, HttpServletResponse response) {
try {
String xmlMsg = AuthUtil.readData(request);
Map<String, String> params = WXPayUtil.xmlToMap(xmlMsg);
logger.info("1.支付回调=========================================================》》" + xmlMsg);
BeanOrder beanOrder = beanOrderService.findBeanOrderId(Long.valueOf(params.get("orderId")));
User user = userService.requiresNewSelectByPrimaryKey(beanOrder.getUserId());
beanOrderService.payNotify(params, response, beanOrder, user);
} catch (Exception e) {
logger.error("充值异常", e);
}
}
public void payNotify(Map<String,String> params, HttpServletResponse response,BeanOrder beanOrder,User user) throws BusinessException {
try {
Boolean czflg = true;
logger.info("2.回调参数pay notice====================================================》》 \n"+params);
String resXml = "";
logger.info("3.处理业务开始========================================================》》\n");
if ("SUCCESS".equals((String) params.get("result_code"))) {
logger.info("4.支付成功========================================================》》\n" + params);
try {
synchronized (czflg) {
// 1.订单信息
beanOrder.setLogContent(params.toString());
beanOrder.setOrderNumber(params.get("out_trade_no"));
BaseResult result = beanOrderService.addBeanOrder(beanOrder, user);
logger.info("5.支付结果========================================================》》\n" + result.toString());
}
} catch (Exception e) {
logger.error(e.getMessage(),e);
}
logger.info("6.通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了====》》\n");
resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
} else {
beanOrder.setStatus(5);
beanOrder.setOrderNumber(params.get("out_trade_no"));
beanOrderService.modifyBeanOrder(beanOrder);
resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[充值失败]]></return_msg>" + "</xml> ";
}
BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
out.write(resXml.getBytes());
out.flush();
out.close();
} catch (Exception e) {
logger.error("充值异常:" + e.getMessage(),e);
throw new BusinessException("充值异常");
}
}
3.订单类
package com.fushui.business.kbeans.model;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import net.sf.jsqlparser.statement.select.First;
import org.hibernate.validator.constraints.Length;
import java.util.Date;
import java.math.BigDecimal;
/**
* K豆订单(bean_order)
*
* @author Evan
* @version 1.0.0 2020-02-25
*/
@ApiModel(description = "K豆订单")
public class BeanOrder implements java.io.Serializable {
/** 版本号 */
private static final long serialVersionUID = -5339897937895258326L;
@Length(max=10,message = "最大长度为10",groups = {First.class})
@ApiModelProperty(value = "")
private Long orderId;
@ApiModelProperty(value = "产品Id")
private Long productId;
@ApiModelProperty(value = "购买人id")
private Long userId;
@ApiModelProperty(value = "支付通道ID beans_passageway_set")
private Integer passagewayId;
@ApiModelProperty(value = "订单类型ID order_type")
private Integer orderType;
@ApiModelProperty(value = "下单客户端:0=h5、1=pc")
private Integer client;
@ApiModelProperty(value = "订单类型名称")
private String orderTypeName;
@ApiModelProperty(value = "联系方式")
private String mobile;
@ApiModelProperty(value = "商家订单号")
private String orderNumber;
@ApiModelProperty(value = "返回日志")
private String logContent;
@ApiModelProperty(value = "支付金额/元")
private BigDecimal amount;
@ApiModelProperty(value = "购买数量")
private Integer count;
@ApiModelProperty(value = "订单状态")
private Integer status;
@ApiModelProperty(value = "订单状态")
private String statusName;
@ApiModelProperty(value = "创建时间")
private Date createTime;
@ApiModelProperty(value = "通道名称")
private String passagewayName;
@ApiModelProperty(value = "产品名称")
private String title;
}
4.工具类方法
package com.fushui.common.util.wxpay;
import com.alibaba.fastjson.JSON;
import com.github.wxpay.sdk.WXPayUtil;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Map;
import java.util.TreeMap;
import java.util.UUID;
/**
* @ClassName AuthUtil
* @Description TODO
* @Author Evan
* @Date 2020/3/18 16:18
* @Version 1.0
**/
public class AuthUtil {
public static final String APPID = "******************";//平台ID
public static final String APPSECRET = ""; //平台密钥
public static final String MCHID = "**********"; //商家ID
public static final String PATERNERKEY = "****************"; // 商家密钥
public static String NOTIFY_URL = "https://testm.tanxingk.com/api/wxpay/payNotify"; // 回调地址
public static String NOTIFY_URL_H5 = "https://testm.tanxingk.com/api/wxpay/payNotify"; // 微信支付h5 回调地址
public static String BODY = "弹性K线-K线资讯"; // 商品名称
public static String UFDODER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder"; // 请求地址
public static String ORDERQUERY = "https://api.mch.weixin.qq.com/pay/orderquery"; // 微信支付V2账单查询接口
/**
* @Author Evan
* @Description //TODO 支付基本参数
* @Date 14:43 2020/5/8
* @Param [request]
* @return java.util.Map<java.lang.String,java.lang.String>
**/
public static Map<String, String> getPackageParams(HttpServletRequest request){
Map<String, String> packageParams = new TreeMap();
String ip = AuthUtil.getIp(request);
packageParams.put("appid", AuthUtil.APPID); // 微信分配的公众账号ID(企业号corpid即为此appId)
packageParams.put("mch_id", AuthUtil.MCHID); // 微信支付分配的商户号
packageParams.put("body", AuthUtil.BODY); // 商品描述
packageParams.put("nonce_str", WXPayUtil.generateNonceStr()); // 生成签名的时候需要你自己设置随机字符串
packageParams.put("out_trade_no", UUID.randomUUID().toString().replaceAll("-", "")); // 商户系统内部的订单号,32个字符
packageParams.put("spbill_create_ip",ip); // 必须传正确的用户端IP
packageParams.put("scene_info", "{\"h5_info\": {\"type\":\"Wap\",\"wap_url\": \"https://testm.tanxingk.com/kcoin/buy\",\"wap_name\": \"弹性K线\"}}");
return packageParams;
}
public static String readData(HttpServletRequest request) {
BufferedReader br = null;
try {
StringBuilder result = new StringBuilder();
br = request.getReader();
for (String line; (line = br.readLine()) != null; ) {
if (result.length() > 0) {
result.append("\n");
}
result.append(line);
}
return result.toString();
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (br != null)
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 获取用户真实IP地址,不使用request.getRemoteAddr();的原因是有可能用户使用了代理软件方式避免真实IP地址。
* 可是,如果通过了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP值,究竟哪个才是真正的用户端的真实IP呢?
* 答案是取X-Forwarded-For中第一个非unknown的有效IP字符串
* @param request
* @return
*/
public static String getIp(HttpServletRequest request) {
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.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
if("127.0.0.1".equals(ip)||"0:0:0:0:0:0:0:1".equals(ip)){
//根据网卡取本机配置的IP
InetAddress inet=null;
try {
inet = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
}
ip= inet.getHostAddress();
}
}
return ip;
}
public static String getInterfaceIp(HttpServletRequest request) {
return getIp(request) + ":" + request.getServerPort();
}
}
5.前端代码
async clickSurePay() {
if (this.choosePayId === '') {
this.$showToast('请选择支付方式');
}
console.log(this.choosePayId);
console.log(this.chooseProductId);
// this.$showToast('功能开发中,敬请期待!');
const [_, resp] = await getAsync('/wxpay/wxPayH5', {
productId: this.chooseProductId,
passagewayId: this.choosePayId
});
console.log(resp);
if (resp?.code === '000000') {
const data = resp.data || {};
// 调起微信支付
console.log(data);
location.href = data;
} else {
}
};