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 {
}
};