微信小程序实现统一下单
1.依赖的maven配置
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-pay</artifactId>
<version>3.9.0</version>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-miniapp</artifactId>
<version>${weixin4.0.0</version>
</dependency>
2.实体
2.1 微信支付相关参数
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
@Data
@ApiModel(value = "auth.WxPayInfoParam", description = "微信支付相关参数")
public class WxPayInfoParam {
@NotBlank(message = "微信openId不能为空")
@ApiModelProperty(value = "微信openId", name = "openId", required = true)
private String openId;
@Min(value = 0,message = "微信支付金额不能为空")
@ApiModelProperty(value = "支付金额", name = "fee", required = true)
private double fee;
@NotBlank(message = "订单信息")
@ApiModelProperty(value = "订单信息", name = "orderDesc", required = true)
private String orderDesc;
@NotBlank(message = "订单编号不能为空")
@ApiModelProperty(value = "订单编号", name = "orderId", required = true)
private String orderId;
}
import cn.gigahome.common.IBaseDto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel(value = "auth.WxPayInfoDto", description = "微信支付")
public class WxPayInfoDto implements IBaseDto {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "appId", name = "appId")
private String appId;
@ApiModelProperty(value = "nonceStr", name = "nonceStr")
private String nonceStr;
@ApiModelProperty(value = "paySign", name = "paySign")
private String paySign;
@ApiModelProperty(value = "signType", name = "signType")
private String signType;
@ApiModelProperty(value = "prepayId", name = "prepayId")
private String prepayId;
@ApiModelProperty(value = "timeStamp", name = "timeStamp")
private String timeStamp;
}
import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import static io.netty.util.internal.StringUtil.byteToHexString;
public class WxPayUtil {
/**
* xml解析
* @param strxml
* @return
* @throws JDOMException
* @throws IOException
*/
public static Map doXMLParse(String strxml) throws JDOMException, IOException {
strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");
if(null == strxml || "".equals(strxml)) {
return null;
}
Map m = new HashMap();
InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(in);
Element root = doc.getRootElement();
List list = root.getChildren();
Iterator it = list.iterator();
while(it.hasNext()) {
Element e = (Element) it.next();
String k = e.getName();
String v = "";
List children = e.getChildren();
if(children.isEmpty()) {
v = e.getTextNormalize();
} else {
v = getChildrenText(children);
}
m.put(k, v);
}
//关闭流
in.close();
return m;
}
public static String getChildrenText(List children) {
StringBuffer sb = new StringBuffer();
if(!children.isEmpty()) {
Iterator it = children.iterator();
while(it.hasNext()) {
Element e = (Element) it.next();
String name = e.getName();
String value = e.getTextNormalize();
List list = e.getChildren();
sb.append("<" + name + ">");
if(!list.isEmpty()) {
sb.append(getChildrenText(list));
}
sb.append(value);
sb.append("</" + name + ">");
}
}
return sb.toString();
}
/**
* 回调签名验证
* @param map
* @return
*/
public static boolean isTenpaySign(Map<String, String> map) {
String signFromAPIResponse = map.get("sign");
String key = map.get("key");
if (signFromAPIResponse == null || signFromAPIResponse.equals("")) {
System.out.println("API返回的数据签名数据不存在,有可能被第三方篡改!!!");
return false;
}
System.out.println("服务器回包里面的签名是:" + signFromAPIResponse);
//过滤空 设置 TreeMap
SortedMap<String,String> packageParams = new TreeMap();
for (String parameter : map.keySet()) {
String parameterValue = map.get(parameter);
String v = "";
if (null != parameterValue) {
v = parameterValue.trim();
}
packageParams.put(parameter, v);
}
StringBuffer sb = new StringBuffer();
Set es = packageParams.entrySet();
Iterator it = es.iterator();
while(it.hasNext()) {
Map.Entry entry = (Map.Entry)it.next();
String k = (String)entry.getKey();
String v = (String)entry.getValue();
if(!"sign".equals(k) && !"key".equals(k) && null != v && !"".equals(v)) {
sb.append(k + "=" + v + "&");
}
}
System.out.println(sb);
sb.append("key=" +key);
//将API返回的数据根据用签名算法进行计算新的签名,用来跟API返回的签名进行比较
//算出签名
String tobesign = sb.toString();
System.out.println("sb = "+ sb);
String resultSign=stringToMD5(tobesign);
String tenpaySign = signFromAPIResponse.toUpperCase();
return tenpaySign.trim().equals(resultSign.trim().toUpperCase());
}
/**
* md5加密
* @param plainText
* @return
*/
public static String stringToMD5(String plainText) {
byte[] secretBytes = null;
try {
secretBytes = MessageDigest.getInstance("md5").digest(
plainText.getBytes());
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("没有这个md5算法!");
}
String md5code = new BigInteger(1, secretBytes).toString(16);
for (int i = 0; i < 32 - md5code.length(); i++) {
md5code = "0" + md5code;
}
return md5code;
}
/**
* 回调通知
* @param param
* @return
*/
public static String GetMapToXML(Map<String,String> param){
StringBuffer sb = new StringBuffer();
sb.append("<xml>");
for (Map.Entry<String,String> entry : param.entrySet()) {
sb.append("<"+ entry.getKey() +">");
sb.append(entry.getValue());
sb.append("</"+ entry.getKey() +">");
}
sb.append("</xml>");
return sb.toString();
}
/**
* 1 块钱转为 100 分
* 元转分
*
* @param bigDecimal 钱数目
* @return 分
*/
public static int yuanToFee(BigDecimal bigDecimal) {
return bigDecimal.multiply(new BigDecimal(100)).intValue();
}
public static Integer changeY2F(double amount){
return (BigDecimal.valueOf(amount).multiply(new BigDecimal(100))).intValue();
}
/**
* 时间
*
* @return 时间戳
*/
public static String createTimestamp() {
return Long.toString(System.currentTimeMillis() / 1000);
}
private static String byteArrayToHexString(byte b[]) {
StringBuffer resultSb = new StringBuffer();
for (int i = 0; i < b.length; i++)
resultSb.append(byteToHexString(b[i]));
return resultSb.toString();
}
}
import cn.gigahome.auth.dto.WxPayInfoDto;
import cn.gigahome.auth.param.WxPayInfoParam;
import cn.gigahome.common.ResponseEntity;
import cn.gigahome.common.web.Anonymous;
import cn.gigahome.config.wx.WxPayUtil;
import cn.gigahome.crm.entity.BillUtilityEntity;
import cn.gigahome.crm.entity.ParkingOrderEntity;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderResult;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.util.SignUtils;
import io.swagger.annotations.ApiOperation;
import org.jdom2.JDOMException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.net.InetAddress;
import java.util.HashMap;
import java.util.Map;
public class TestController {
@Autowired
private WxPayService wxPayService;
//设置微信公众号或者小程序等的appid
private static final String appId="";
//微信支付商户号
private static final String mchId="";
//微信支付商户密钥
private static final String mchKey="";
@GetMapping("/wxGetPay")
@ApiOperation("微信支付")
public ResponseEntity<WxPayInfoDto> wxGetPay(WxPayInfoParam param) {
WxPayUnifiedOrderResult wxPayUnifiedOrderResult =null;
try {
final WxPayUnifiedOrderRequest wxPayUnifiedOrderRequest = WxPayUnifiedOrderRequest.newBuilder()
//调起支付的人的 openId
.openid(param.getOpenId())
//订单编号,需要后台生成
.outTradeNo(param.getOrderId()).tradeType("JSAPI").productId(param.getOrderId()).detail(param.getOrderDesc())
//订单金额
.totalFee(WxPayUtil.yuanToFee(new BigDecimal(param.getFee())))
//商品描述
.body(param.getOrderDesc())
//获取本地IP
.spbillCreateIp(InetAddress.getLoopbackAddress().getHostAddress())
//回调的 URL 地址
.notifyUrl("http://bjjg.kaisatech.com/api/crm/wxpay/wxPayNotify")
.build();
wxPayUnifiedOrderResult = wxPayService.unifiedOrder(wxPayUnifiedOrderRequest);
} catch (WxPayException e) {
e.printStackTrace();
System.out.println("微信支付调起失败 == " + e.getMessage());
}
//组合参数构建支付
WxPayInfoDto returnPayInfo = new WxPayInfoDto();
Map<String, String> paySignInfo = new HashMap<>(5);
String timeStamp = WxPayUtil.createTimestamp();
String nonceStr = String.valueOf(System.currentTimeMillis());
paySignInfo.put("appId", appId);
paySignInfo.put("nonceStr", nonceStr);
paySignInfo.put("timeStamp", timeStamp);
paySignInfo.put("signType", "MD5");
paySignInfo.put("package", "prepay_id=" + wxPayUnifiedOrderResult.getPrepayId());
String paySign = SignUtils.createSign(paySignInfo, "MD5", mchKey, null);
System.out.println("paySign = " + paySign);
//组合支付参数并返回给前台
returnPayInfo.setAppId(appId);
returnPayInfo.setNonceStr(nonceStr);
returnPayInfo.setPaySign(paySign);
returnPayInfo.setSignType("MD5");
returnPayInfo.setPrepayId(wxPayUnifiedOrderResult.getPrepayId());
returnPayInfo.setTimeStamp(timeStamp);
return ResponseEntity.success(returnPayInfo);
}
@Anonymous
@ResponseBody
@ApiOperation("微信支付回调")
@RequestMapping("/wxPayNotify")
public String payNotify(HttpServletRequest request, HttpServletResponse response) {
try {
System.out.println("微信支付回调");
InputStream inStream = request.getInputStream();
ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = inStream.read(buffer)) != -1) {
outSteam.write(buffer, 0, len);
}
String resultxml = new String(outSteam.toByteArray(), "utf-8");
Map<String, String> params = WxPayUtil.doXMLParse(resultxml);
System.out.println(params);
outSteam.close();
inStream.close();
params.put("key",mchKey);
Map<String,String> return_data = new HashMap<String,String>();
if(WxPayUtil.isTenpaySign(params)) {
System.out.println("验证通过");
// 支付成功
String out_trade_no = params.get("out_trade_no");
if (updateCallbackState(return_data, out_trade_no)) {
return WxPayUtil.GetMapToXML(return_data);
}
} else {
// 支付失败
return_data.put("return_code", "FAIL");
return_data.put("return_msg", "return_code不正确");
return WxPayUtil.GetMapToXML(return_data);
}
} catch (IOException | JDOMException e) {
e.printStackTrace();
}
return null;
}
/**
* 修改支付回调状态
* @param return_data 返回参数
* @param out_trade_no 订单号
* @return
*/
private boolean updateCallbackState(Map<String, String> return_data, String out_trade_no) {
boolean flag= false;
if(out_trade_no.startsWith("sd")){
//通过订单号修改业务逻辑
}else{
// 支付失败
return_data.put("return_code", "FAIL");
return_data.put("return_msg", "return_code不正确");
return true;
}
if(flag){
//回调通知微信
return_data.put("return_code", "SUCCESS");
return_data.put("return_msg", "OK");
System.out.println("通知微信成功");
return true;
}
return false;
}
}