微信支付-开发者文档:
https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_1.shtml
生成订单
/**
* 生成订单
*/
@User
@ApiOperation("生成订单")
@PostMapping
public AjaxResult add(@RequestBody WxOrder wxOrder, HttpServletRequest request) {
return wxOrderService.insertWxOrder(wxOrder, request);
}
/**
* 生成订单
*
* @param wxOrder 订单
* @return 结果
*/
public AjaxResult insertWxOrder(WxOrder wxOrder, HttpServletRequest request);
/**
* 生成订单
*
* @param wxOrder 订单
* @return 结果
*/
@Override
public AjaxResult insertWxOrder(WxOrder wxOrder, HttpServletRequest request) {
if (wxOrder == null || StringUtils.isEmpty(wxOrder.getPid())) {
return AjaxResult.error("订单支付数据不能为空,请检查参数再试");
}
WxScenicSpot wxScenicSpot = new WxScenicSpot();
wxScenicSpot.setPid(wxOrder.getPid());
List<WxScenicSpot> wxScenicSpots = wxScenicSpotMapper.selectWxScenicSpotList(wxScenicSpot);
if (CollectionUtils.isEmpty(wxScenicSpots)) {
return AjaxResult.error("未找到支付产品,请检查参数再试");
}
wxOrder.setUserId(JwtUtil.getUser(request.getHeader(ConstantUtil.TOKEN)).getId());
// 订单编号
wxOrder.setOrderNo(CustomCodeUtil.createOrderCode());
// 商品冗余信息
wxOrder.setScenicSpotId(wxScenicSpots.get(0).getId());
wxOrder.setName(wxScenicSpots.get(0).getName());
wxOrder.setAddress(wxScenicSpots.get(0).getAddress());
wxOrder.setOpeningHours(wxScenicSpots.get(0).getOpeningHours());
wxOrder.setPhone(wxScenicSpots.get(0).getPhone());
wxOrder.setImage(wxScenicSpots.get(0).getImage());
// 成人单价
BigDecimal adultPrice = wxScenicSpots.get(0).getAdultPrice();
// 成人票数量
Long adultCount = wxOrder.getAdultCount();
// 成人票总价
BigDecimal adultTotalPrice = new BigDecimal(Arith.mul(adultPrice.doubleValue(), (double)adultCount));
wxOrder.setAdultTotalPrice(adultTotalPrice);
// 儿童单价
BigDecimal childPrice = wxScenicSpots.get(0).getChildPrice();
// 儿童票数量
Long childCount = wxOrder.getChildCount();
// 儿童票总价
BigDecimal childTotalPrice = new BigDecimal(Arith.mul(childPrice.doubleValue(), (double) childCount));
wxOrder.setChildTotalPrice(childTotalPrice);
// 总票价
BigDecimal totalPrice = new BigDecimal(Arith.add(adultTotalPrice.doubleValue(), childTotalPrice.doubleValue()));
if (totalPrice.doubleValue()<0) {
return AjaxResult.error("非法价格,价格不能为负数");
}
wxOrder.setTotalPrice(totalPrice);
// 总票数量
wxOrder.setTotalCount(adultCount+childCount);
// 订单创建时间
wxOrder.setCreateDate(DateUtils.getNowDate());
if (totalPrice.doubleValue()==0) {
wxOrder.setStatus(1);
wxOrder.setPayDate(DateUtils.getNowDate());
wxOrderMapper.insertWxOrder(wxOrder);
return AjaxResult.success("生成免费订单成功");
}
// 获取微信预支付订单号
JSONObject jsonObject = wxPayUtils.getPrepayId(wxOrder);
String prepay_id = (String) jsonObject.get("prepay_id");
// 获取预支付订单号成功
if (StringUtils.isNotEmpty(prepay_id)) {
wxOrder.setPrepayId(prepay_id);
int i = wxOrderMapper.insertWxOrder(wxOrder);
if (i > 0) {
// 返回给前端调起微信支付的必要参数
Map<String, Object> map = wxPayUtils.returnToReceptionJson(wxOrder.getOrderNo(), prepay_id);
return AjaxResult.success(map);
} else {
return AjaxResult.error("生成订单失败");
}
} else {
return AjaxResult.error("生成订单失败");
}
}
微信支付工具类
import cn.hutool.http.HttpRequest;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.ruoyi.wx.api.controller.order.domain.WxOrder;
import com.ruoyi.wx.api.controller.user.domain.WxUser;
import com.ruoyi.wx.api.controller.user.service.IWxUserService;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.*;
import java.util.*;
/**
* 微信支付工具类
*/
@Component
public class WxPayUtils {
@Autowired
private IWxUserService iWxUserService;
// 证书编码
public static final String serial_no = "xxx";
// 商户号
public static final String mchId = "xxx";
// appid
public static final String appId = "xxx";
// 证书私钥
public static final String privateKey = "-----BEGIN PRIVATE KEY-----\n" +
"MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDBJn3bVt9BbLhs\n" +
"+ZjULV7Odh4rtBBd2wc7uZ8RlN/9E8sJfAefowiSTPwOkTYKDlyhpG131a4VKj+N\n" +
"zaTu0Y5zQGyl29ElHVVuPw4mVni7xyqVdJ5VUxyJMKHmQsGpngHS4sF28c5Aihfi\n" +
"Gkydt6BRF8xwFC9u+D0r4PY9dwHTn4jyaFd25CMUxVO+rfjOWFuGa/rEjtQw03KU\n" +
"UVR6np6FmNXdWalQbmbETyUqGjCac6J7hDZ1LIRU4YtqMqqsXhnk3FaoS23MOgaZ\n" +
"RE3nIgpYqGgfXfLOKDt5n4zLALJkV0xmk3VErbheWpzRVFae/JMtsf/8ySgtiOga\n" +
"n8fvShJNAgMBAAECggEAf+P+vbb9yJI2Y2G5QfRwrAAl5gYqOBsI5RD5NGkBDs+G\n" +
"RtdrLNaEnGqBExwvTeVIjHcVTJ2d0MHSpxAdP0xeKA3mYsPz7cFIieESe2wSMTOl\n" +
"DKTVGeYYJPagnJhjJu5KgtpGA34EdVN6kOmdBWlKq6c4ZJXY+n8/8cfZA8XC3d3B\n" +
"favo104podp8cC2IVNMs21OzbjD4V+6VV9X/5BA6x7bDcarlJXSyCehdXgjhjNmT\n" +
"/9gz7MFhOsUlAG7P03easIRcZCQL6z9MebD3xfVYcXnS4L3CcVw1FWEdLXxe/3IG\n" +
"Q8UPTPHhTLFRbURHI00x1jEvcD9qKXYgKeU/N7IY+QKBgQD93r2SotKM5EDxM3L2\n" +
"5UFrmEVnf4bwg5QDyZFpjAJ+/uMSsEX3f1m8Mst82Y4Mybz8XVMNsUJmoY8eJoM+\n" +
"sqRbI5HXySsFEbDqh7jrtBoN7R1rl3jQw4qxqWYR8GQJ6VqAXuEFbrxgE1YFgqw5\n" +
"40HAYbv417h0h5A1rcRKegoW9wKBgQDCxVaCmBzcYYFc2leRy6qH965OEedqbSCV\n" +
"QPprCDBoBM6jrq97ZfWyfmLNSjjFhTSz9h8z5dQcePZrrI3+evlE8V18hUmWN6rM\n" +
"CvPCb+4q8CwiHmLFqVp+m11MR8EOBcWCW/HlioqBKTLglijY53RUjW7No/T+32sf\n" +
"5R1qYq672wKBgQC3dztFN4o169bK+UWCDBgFK9wsecsJEe3r9sWxo09Ce+2aWe2W\n" +
"eWBeU88fARJZR4neT4tv/8Re6y7EuUxsCSoh+0iwy17doPVb6I3JOTUDD3MNiD/1\n" +
"jvsyfZuYJ0QErbGLyAWSqX5VaGPoQ5E3nHauE3OG2E8jV7zuLhAHSr7z7QKBgAF8\n" +
"Z/CPIIk95TLEJ67hEuf+p8HIuS9CreD1ofN3GIdyofD1wDj8yicWd8KBMnWvUnud\n" +
"ARfwRPICqj6gDmVGoug3vzLYAXu36QGtg7aUDAkf0/ZerPo9FIeqv8d5NKvat2sL\n" +
"MIlDyVK68bxs6NreyTBr89B108SuB68ynErXfeXJAoGBAMCoJyWdFAQKVvycHyv4\n" +
"914+v7niQahws23wxHlzvup8/doCS1XVQAoEyQ4iIPDHYmiOCcmzCQnb1CWgMbIl\n" +
"oBY3baP1GWOwEWw/HYKOwHW3fLkYC/xaRi+b5IxyEzR7hlCm/A6gCnCUYqIdU8gd\n" +
"jYE4kGB2O/5vdbITdSMai47o\n" +
"-----END PRIVATE KEY-----";
/**
* 获取微信预支付订单号
* @return
*/
public JSONObject getPrepayId(WxOrder wxOrder){
WxUser wxUser = iWxUserService.selectWxUserById(wxOrder.getUserId());
JSONObject jsonObject = new JSONObject();
JSONObject amountJsonObject = new JSONObject();
JSONObject payerJsonObject = new JSONObject();
amountJsonObject.put("total",wxOrder.getTotalPrice());
payerJsonObject.put("openid",wxUser.getOpenid());
// 应用ID
jsonObject.put("appid",appId);
// 商户号
jsonObject.put("mchid",mchId);
// 商品描述
jsonObject.put("description",wxOrder.getName());
// 商户订单号
jsonObject.put("out_trade_no",wxOrder.getOrderNo());
// 通知地址
jsonObject.put("notify_url","https://zc.xxx.com/wx/api/pay/notifyUrl");
// 订单金额信息
jsonObject.put("amount",amountJsonObject);
// 支付者信息
jsonObject.put("payer",payerJsonObject);
String body = jsonObject.toString();
System.out.println(body);
String authorization = signStr("POST", "/v3/pay/transactions/jsapi", body);
String result = HttpRequest.post("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi")
.body(body)
.header("Content-Type","application/json")
.header("Accept","application/json")
.header("Authorization","WECHATPAY2-SHA256-RSA2048 " + authorization)//头信息,多个头信息多次调用此方法即可
.timeout(10000)//超时,毫秒
.execute().body();
JSONObject jsonObject1 = JSONUtil.parseObj(result);
String prepay_id = jsonObject1 == null ? "":(String) jsonObject1.get("prepay_id");
JSONObject resultJsonObject = new JSONObject();
resultJsonObject.put("prepay_id",prepay_id);
resultJsonObject.put("chatPrice",wxOrder.getTotalPrice());
return resultJsonObject;
}
/**
* 成功获取微信预支付订单号后, 返回给前端调起微信支付的必要参数
* @param orderNo 商户订单号
* @param prepay_id 预支付订单号
* @return
*/
public Map<String, Object> returnToReceptionJson(String orderNo, String prepay_id){
HashMap<String, Object> map = new HashMap<>();
long timeStamp = System.currentTimeMillis() / 1000;
String uuid = UUID.randomUUID().toString().replace("-", "");
String paySign = getPaySign(timeStamp, uuid, prepay_id);
map.put("orderId", orderNo);
map.put("timeStamp", timeStamp);
map.put("nonceStr", uuid);
map.put("package", "prepay_id=" + prepay_id);
map.put("signType", "RSA");
map.put("paySign", paySign);
return map;
}
public String getPaySign(long timestamp, String randString, String prepay_id){
String data = appId + "\n" +
timestamp + "\n" +
randString + "\n" +
"prepay_id=" + prepay_id + "\n";
String signature = null;
try {
signature = sign(data.getBytes("utf-8"));
} catch (SignatureException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return signature;
}
public String signStr2(String method, String url){
String nonceStr = UUID.randomUUID().toString();
long timestamp = System.currentTimeMillis() / 1000;
String message = buildMessage2(method, url, timestamp, nonceStr);
String signature = null;
try {
signature = sign(message.getBytes("utf-8"));
} catch (SignatureException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
System.out.println("签名后:[" + signature + "]");
return "mchid=\"" + mchId + "\","
+ "serial_no=\"" + serial_no + "\","
+ "nonce_str=\"" + nonceStr + "\","
+ "timestamp=\"" + timestamp + "\","
+ "signature=\"" + signature + "\"";
}
public String buildMessage2(String method, String url, long timestamp, String nonceStr) {
String str = method + "\n"
+ url + "\n"
+ timestamp + "\n"
+ nonceStr + "\n\n";
System.out.println("签名数据[" + str + "]");
return str;
}
public String signStr(String method, String url, String body){
String nonceStr = UUID.randomUUID().toString();
long timestamp = System.currentTimeMillis() / 1000;
String message = buildMessage(method, url, timestamp, nonceStr, body);
System.out.println("message:[" + message + "]");
String signature = null;
try {
signature = sign(message.getBytes("utf-8"));
} catch (SignatureException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
System.out.println("signature=[" + signature + "]");
return "mchid=\"" + mchId + "\","
+ "nonce_str=\"" + nonceStr + "\","
+ "timestamp=\"" + timestamp + "\","
+ "serial_no=\"" + serial_no + "\","
+ "signature=\"" + signature + "\"";
}
public String buildMessage(String method, String url, long timestamp, String nonceStr, String body) {
String str = method + "\n"
+ url + "\n"
+ timestamp + "\n"
+ nonceStr + "\n"
+ body + "\n";
return str;
}
public String sign(byte[] message) throws SignatureException {
Signature sign = null;
try {
sign = Signature.getInstance("SHA256withRSA");
PrivateKey privateKey = getPrivateKey();
sign.initSign(privateKey);
sign.update(message);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (SignatureException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return Base64.getEncoder().encodeToString(sign.sign());
}
/**
* 获取私钥。
*/
public static PrivateKey getPrivateKey() throws IOException {
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(
new ByteArrayInputStream(privateKey.getBytes("utf-8")));
return merchantPrivateKey;
}
// xml解析
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;
}
/**
* 获取子结点的xml
*
* @param children
* @return String
*/
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();
}
}
支付接口
import cn.hutool.json.JSONObject;
import com.ruoyi.wx.api.controller.pay.service.IWxPayService;
import com.ruoyi.wx.response.ResponseResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Api(value = "微信小程序支付接口",tags = {"微信小程序支付接口"})
@RestController
@RequestMapping("/wx/api/pay")
public class WxPayController {
@Autowired
private IWxPayService wxPayService;
/**
* 查询订单支付状态
* @param orderNo 商户订单号
* @return
*/
@ApiOperation("查询订单支付状态")
@GetMapping("/transactions")
public ResponseResult<Object> transactions(@RequestParam("orderNo") String orderNo){
return wxPayService.transactions(orderNo);
}
/**
* 支付通知(回调)
* @param request
* @param response
* @return
* @throws Exception
*/
@ApiOperation("支付通知(回调)")
@RequestMapping(value = "/notifyUrl", produces = "application/json;charset=UTF-8")
@ResponseBody
public JSONObject payNotifyUrl(HttpServletRequest request, HttpServletResponse response) throws Exception {
return wxPayService.payNotifyUrl(request,response);
}
}
import cn.hutool.json.JSONObject;
import com.ruoyi.wx.response.ResponseResult;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public interface IWxPayService {
ResponseResult<Object> transactions(String orderNo);
JSONObject payNotifyUrl(HttpServletRequest request, HttpServletResponse response)throws IOException;
}
import cn.hutool.http.HttpRequest;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.ruoyi.wx.api.controller.order.domain.WxOrder;
import com.ruoyi.wx.api.controller.order.mapper.WxOrderMapper;
import com.ruoyi.wx.api.controller.pay.service.IWxPayService;
import com.ruoyi.wx.response.ResponseResult;
import com.ruoyi.wx.utils.WxPayUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@Service
public class WxPayServiceImpl implements IWxPayService {
// 格式化时间日期
private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@Autowired
private WxPayUtils wxPayUtils;
@Autowired
private WxOrderMapper wxOrderMapper;
@Override
public ResponseResult<Object> transactions(String orderId) {
WxOrder wxOrder = wxOrderMapper.apiSelectWxOrderByOrderId(orderId);
String authorization = wxPayUtils.signStr2("GET", "/v3/pay/transactions/out-trade-no/" + orderId + "?mchid="+WxPayUtils.mchId);
String result = HttpRequest.get("https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/" + orderId + "?mchid="+WxPayUtils.mchId)
.header("Content-Type","application/json")
.header("Accept","application/json")
.header("Authorization","WECHATPAY2-SHA256-RSA2048 " + authorization)//头信息,多个头信息多次调用此方法即可
.timeout(10000)//超时,毫秒
.execute().body();
JSONObject jsonObject = JSONUtil.parseObj(result);
String trade_state = (String) jsonObject.get("trade_state");
// 支付成功
if ("SUCCESS".equals(trade_state)) {
String success_time = (String) jsonObject.get("success_time");
Date payDate = null;
try {
payDate = simpleDateFormat.parse(success_time.replace("T", " ").replace("+08:00", ""));
} catch (ParseException e) {
e.printStackTrace();
}
wxOrder.setStatus(1);
wxOrder.setPayDate(payDate);
wxOrderMapper.updateWxOrder(wxOrder);
} else {
// 支付失败
}
return ResponseResult.createBySuccess(jsonObject);
}
@Override
public JSONObject payNotifyUrl(HttpServletRequest request, HttpServletResponse response) throws IOException {
BufferedReader reader = null;
reader = request.getReader();
String line = "";
String xmlString = null;
StringBuffer inputString = new StringBuffer();
while ((line = reader.readLine()) != null) {
inputString.append(line);
}
xmlString = inputString.toString();
request.getReader().close();
log.info("----接收到的数据如下:---" + xmlString);
Map<String, Object> map = new HashMap<String, Object>();
String summary = "";
String resource = "";
String create_time = "";
Map<String, Object> value = getValue(xmlString);
map = value;
summary = (String) map.get("summary");
resource = (String) map.get("resource");
create_time = (String) map.get("create_time");
log.info("回调接口 summary == 》" + summary);
log.info("回调接口 resource == 》" + resource);
log.info("回调接口 create_time == 》" + create_time);
JSONObject jsonObject = new JSONObject();
jsonObject.put("code","SUCCESS");
jsonObject.put("message","成功");
return jsonObject;
}
/**
* 处理支付回调的方法
* @param param
* @return
*/
public static Map<String, Object> getValue(String param) {
Map<String, Object> map = new HashMap<>();
String str = "";
String key = "";
Object value = "";
char[] charList = param.toCharArray();
boolean valueBegin = false;
for (int i = 0; i < charList.length; i++) {
char c = charList[i];
if (c == '{') {
if (valueBegin == true) {
value = getValue(param.substring(i, param.length()));
i = param.indexOf('}', i) + 1;
map.put(key, value);
}
} else if (c == '=') {
valueBegin = true;
key = str;
str = "";
} else if (c == ',') {
valueBegin = false;
value = str;
str = "";
map.put(key, value);
} else if (c == '}') {
if (str != "") {
value = str;
}
map.put(key, value);
return map;
} else if (c != ' ') {
str += c;
}
}
return map;
}
}