1.下單回調方法
@ResponseBody
@ApiOperation("微信瀏支付/(H5)")
@ApiImplicitParams({
@ApiImplicitParam(name = "productId", value = "產品ID",required=true),
@ApiImplicitParam(name = "client", value = "下單客戶端:0=h5、1=pc",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) {
Map<String, Object> result = new HashMap();
result.put("success", false);
try {
Long userId = accessTokenService.getAppUserId(request);
logger.info("1.發起微信瀏支付=========================================================》》productId="+ beanOrder.getProductId() + "=======》》 userId" + userId);
// 價格 注意:價格的單位是分
beanOrder.setAmount(new BigDecimal("1"));
String order_price = beanOrder.getAmount().multiply(new BigDecimal(100)).toString().split("\\.")[0];
beanOrder.setUserId(userId);
// 下單客戶端:0=h5、1=pc (掃碼支付)
beanOrder.setClient(1);
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);
}
logger.info("2.拼接統一下單地址參數===================================================》》\n ");
Map<String, String> packageParams = AuthUtil.getPackageParams(request);
packageParams.put("total_fee", "1"); // 支付金額,單位分
packageParams.put("detail", JSON.toJSONString(beanOrder)); // 商品詳情
packageParams.put("notify_url", AuthUtil.NOTIFY_URL_H5 + "?productId="+beanOrder.getProductId()+"&userId="+userId); // 此路徑是微信服務器調用支付結果通知路徑隨意寫
packageParams.put("trade_type", beanOrder.getClient() == 1 ? "NATIVE" : "MWEB"); // 支付類型
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("https://testm.tanxingk.com/kcoin/buy", "GBK");
String url = beanOrder.getClient() == 1 ? map.get("code_url") : map.get("mweb_url");
String retsult = url + "&redirect_url=" + urlString;
logger.info("5.請求結束 =============================================================》》\n" + retsult);
return new BaseResult(ResultEnum.QX_000000.getCode(),retsult);
} catch (Exception e) {
logger.error(e.getMessage(),e);
return new BaseResult(ResultEnum.ZC_120000.getCode());
}
}
@ResponseBody
@ApiOperation("支付完成的回調函數")
@ApiImplicitParams({
})
@ApiResponses(value = {
@ApiResponse(code = 500, message = "系統錯誤"),
@ApiResponse(code = 200, message = "{code:響應碼,message:描述,data:參考model類{}}", response = BeanOrder.class)})
@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 = new BeanOrder();
beanOrder.setUserId(Long.valueOf(params.get("userId")));
beanOrder.setProductId(Long.valueOf(params.get("productId")));
BeanProduct product = productService.findBeanProductId(beanOrder.getProductId());
User user = userService.requiresNewSelectByPrimaryKey(beanOrder.getUserId());
beanOrder.setAmount(product.getPrice());
beanOrder.setOrderType(1);
beanOrder.setMobile(user.getMobile());
beanOrder.setPassagewayId(1);
beanOrder.setCount(product.getNumber());
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("nonce_str"));
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 {
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("充值異常");
}
} catch (Exception e) {
logger.error("充值異常",e);
}
}
2.訂單類
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;
}
3.工具類方法
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();
}
}
4.前端代碼
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 {
}
};