微信支付接口模板
一个简单的比较清晰的微信支付demo:https://www.cnblogs.com/wang-yaz/p/8632624.html
大概流程 :
调用接口传参获取二维码url(传入所需订单对象,方便后边业务逻辑进行判断)–》扫描通过扫描成功回传参数进行判断–》根据返回成功参数进行下一步操作(对数据库进行数值的增加或修改)
需要的util类: HttpRequestUtil(主要完成发送post请求) , XMLutil(主要完成将对象转成xml字符串,将xml字符串转化成对象),WeChatPayUtil(主要对回调接口进行操作以及对数据的封装),HttpUtil(主要完成获取用户IP地址的操作)
WeChatPayUtil类
public class WeChatPayUtil {
private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final Random RANDOM = new SecureRandom();
private static WeChatPayConstant.SignType signType;
/**
* 向 对象中 中添加 appid、mch_id、nonce_str、sign_type、sign <br> 该函数适用于商户适用于统一下单等接口,不适用于红包、
代金券接口 @param order @return @throws Exception
*/
public static OrderPO fillRequestData(OrderPO order) throws Exception {/*appid*/
order.setAppid(WeChatPayConstant.APPID);/*商户号*/
order.setMch_id(WeChatPayConstant.MCHID);
order.setNonce_str(generateNonceStr());
if (WeChatPayConstant.SignType.MD5.equals(signType)) order.setSign(WeChatPayConstant.MD5);
else if (WeChatPayConstant.SignType.HMACSHA256.equals(signType))
order.setSign_type(WeChatPayConstant.HMACSHA256);/*生成签名*/
order.setSign(WeChatPayUtil.generateSignature(CollectionUtils.classCastMap(order), WeChatPayConstant.getKEY()));
return order;
}
/**
* 生成签名 @param data 待签名数据 @param key API密钥 @return 签名
*/
public static String generateSignature(final Map<String, String> data, String key) throws Exception {
return generateSignature(data, key, WeChatPayConstant.SignType.MD5);
}
/**
* 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。 @param data 待签名数据 @param key API密钥
@param signType 签名方式 @return 签名
*/
public static String generateSignature(final Map<String, String> data, String key, WeChatPayConstant.SignType signType)
throws Exception {
Set<String> keySet = data.keySet();
String[] keyArray = keySet.toArray(new String[keySet.size()]);
Arrays.sort(keyArray);
StringBuilder sb = new StringBuilder();
for (String k : keyArray) {
if (k.equals(WeChatPayConstant.FIELD_SIGN)) continue;
if (data.get(k).trim().length() > 0) /* 参数值为空,则不参与签名*/
sb.append(k).append("=").append(data.get(k).trim()).append("&");
}
sb.append("key=").append(key);
if (WeChatPayConstant.SignType.MD5.equals(signType)) return MD5(sb.toString()).toUpperCase();
else if (WeChatPayConstant.SignType.HMACSHA256.equals(signType)) return HMACSHA256(sb.toString(), key);
else throw new Exception(String.format("Invalid sign_type: %s", signType));
}
/**
* 判断签名是否正确 @param xmlStr XML格式数据 @param key API密钥 @return 签名是否正确 @throws Exception
*/
public static boolean isSignatureValid(String xmlStr, String key) throws Exception {
Map<String, String> data = XMLUtil.xmlToMap(xmlStr);
if (!data.containsKey(WeChatPayConstant.FIELD_SIGN)) return false;
String sign = data.get(WeChatPayConstant.FIELD_SIGN);
return generateSignature(data, key).equals(sign);
}
/**
* 判断签名是否正确,必须包含sign字段,否则返回false。使用MD5签名。 @param data Map类型数据 @param key API密钥 @return
签名是否正确 @throws Exception
*/
public static boolean isSignatureValid(Map<String, String> data, String key) throws Exception {
return isSignatureValid(data, key, WeChatPayConstant.SignType.MD5);
}
/**
* 判断签名是否正确,必须包含sign字段,否则返回false。 @param data Map类型数据 @param key API密钥 @param
signType 签名方式 @return 签名是否正确 @throws Exception
*/
public static boolean isSignatureValid(Map<String, String> data, String key, WeChatPayConstant.SignType signType)
throws Exception {
if (!data.containsKey(WeChatPayConstant.FIELD_SIGN)) return false;
String sign = data.get(WeChatPayConstant.FIELD_SIGN);
return generateSignature(data, key, signType).equals(sign);
}
/**
* 判断xml数据的sign是否有效,必须包含sign字段,否则返回false。 @param reqData 向wxpay post的请求数据 @return 签名是否
有效 @throws Exception
*/
public static boolean isResponseSignatureValid(Map<String, String> reqData) throws Exception {/* 返回数据的签名方式和请
求中给定的签名方式是一致的*/
return WeChatPayUtil.isSignatureValid(reqData, WeChatPayConstant.getKEY(), WeChatPayConstant.SignType.MD5);
}
/**
* 获取随机字符串 Nonce Str @return String 随机字符串
*/
public static String generateNonceStr() {
char[] nonceChars = new char[32];
for (int index = 0; index < nonceChars.length; ++index)
nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));
return new String(nonceChars);
}
/**
* 生成 MD5 @param data 待处理数据 @return MD5结果
*/
public static String MD5(String data) throws Exception {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] array = md.digest(data.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte item : array) sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
return sb.toString().toUpperCase();
}
/**
* 生成 HMACSHA256 @param data 待处理数据 @param key 密钥 @return 加密结果 @throws Exception
*/
public static String HMACSHA256(String data, String key) throws Exception {
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
sha256_HMAC.init(secret_key);
byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte item : array) sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
return sb.toString().toUpperCase();
}
}
常量类:需要对一些必要参数进行封装
{
public enum SignType {
MD5, HMACSHA256
}
//续费的时长
public static final int RENEWAL_FEE_TIME = 31536000; //此处可以省略
//微信平台ID
public static final String APPID = "XXXXXXXXXXXX";
//商户ID
public static final String MCHID = "XXXXXXXXX";
//商户秘钥
private static final String KEY = "XXXXXXXXXXXXXXXXXXXX";
//微信统一下单接口
public static final String GET_PAY_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
//微信检查订单接口
public static final String CHECK_ORDER = "https://api.mch.weixin.qq.com/pay/orderquery";
//交易金额
private static final Double AMOUNT = 0.01;
public static final String FIELD_SIGN = "sign";
public static final String FAIL = "FAIL";
public static final String MD5 = "MD5";
public static final String HMACSHA256 = "HMAC-SHA256";
//交易状态
/**
* 支付成功
*/
public static final String SUCCESS = "SUCCESS";
/**
* 转入退款
*/
public static final String REFUND = "REFUND";
/**
* 未支付
*/
public static final String NOTPAY = "NOTPAY";
/**
* 已关闭
*/
public static final String CLOSED = "CLOSED";
/**
* 已撤销(付款码支付)
*/
public static final String REVOKED = "REVOKED";
/**
* 用户支付中(付款码支付)
*/
public static final String USERPAYING = "USERPAYING";
/**
* 支付失败(其他原因,如银行返回失败)
*/
public static final String PAYERROR = "PAYERROR";
//商品编号
/**
* 微信公众号服务续费一年
*/
public static final String RENEW_OA_ONE_YEAR = "微信公众号服务续费一年";
public static String getKEY() {
return KEY;
}
public static Double getAMOUNT() {
return AMOUNT;
}
}
具体实现
1 定义创建订单的controller(返回二维码以及将预订单数据插入数据库中,方便之后业务逻辑进行判断)
/* 此处假设业务为将某公众号业务时间增长*/
public ResultInfo createOrder(HttpServletRequest HttpServletRequest, @RequestBody CreateOrderDTO createOrderDTO)
throws Exception {
/*判断传入的公众号id是否为空*/
if (createOrderDTO.getOaId() == null) throw new RException(CustomErrcodeEnum.OA_ID_IS_NULL);
/*返回数据*/
CreateOrderVO createOrderVO = weChatPayService.retuenWeChatPayCodeUrl(HttpServletRequest, createOrderDTO);
if (ObjectUtil.isEmpty(createOrderVO)) throw new RException(CustomErrcodeEnum.ADD_ORDER_ERROR);
else return ResultInfoUtil.success(createOrderVO);
}
/*其中的实现类retuenWechatPayCodeUrl*/ CreateOrderVO中包含二维码url以及订单编号(订单编号可方便后边进行逻辑判断)
public CreateOrderVO retuenWeChatPayCodeUrl(HttpServletRequest request, CreateOrderDTO createOrderDTO) throws
Exception {
/*通过request获取支付订单所需要的信息*/
OrderPO order = CreateOrderPO(request); /**其中createOrderPO为将request的信息存储到OrderPO类中
/*把对象转换成XML*/
String toXmlString = XMLUtil.toXmlString(order);
/*调用微信支付统一下单接口,让微信也生成一个预支付订单*/
String xmlResult = HttpRequestUtil.post(WeChatPayConstant.GET_PAY_URL, toXmlString);
/*把返回的xml字符串转成对象*/
WxOrderResultDTO entity = (WxOrderResultDTO) XMLUtil.xmlStrToBean(xmlResult, WxOrderResultDTO.class);
/*保存订单*/
if (entity.getCode_url() != null) {/*设置id*/
order.setId(StringUtil.getUUID());/*设置续费公众号的id*/
order.setOaId(createOrderDTO.getOaId());/*设置付款结果*/
order.setDoneFlag(false);/*保存订单信息*/
orderService.insert(order);
CreateOrderVO createOrderVO = new CreateOrderVO();
createOrderVO.setCodeUrl(entity.getCode_url());
createOrderVO.setOutTradeNo(order.getOut_trade_no());
return createOrderVO;
} else return null;
}
/*将request中的信息封装到订单类OrderPO中*/
private OrderPO CreateOrderPO(HttpServletRequest request) throws Exception {
double price = WeChatPayConstant.getAMOUNT();
/*生成订单编号*/
int number = (int) ((Math.random() * 9) * 1000);
/*时间*/
String orderNumber = TimeUtil.yyyyMMddHHmmssNoCut.format(new Date()) + number;
/*准备调用接口需要的参数*/
OrderPO order = new OrderPO();
/*商品描述*/
order.setBody(WeChatPayConstant.RENEW_OA_ONE_YEAR);
/*交易类型*/
order.setTrade_type("NATIVE");
/*商户订单号*/
order.setOut_trade_no(orderNumber);
/*支付金额(单位:分)*/
order.setTotal_fee((int) (price * 100));
/*用户ip地址*/
order.setSpbill_create_ip(HttpUtil.getIpAddress(request)); //获取本机ip地址
/*接收支付结果的地址*/
order.setNotify_url(wechatConfigUtil.getCmkfUrl() + "/XXXXXXXX"); //XXXXX为回调接口的接口名
return WeChatPayUtil.fillRequestData(order);
}
定义回调接口
@PostMapping("/name") ------------------------>上方接收支付结果的地址
public ResultInfo wechatPayBack() throws Exception {
WxPayResultDTO wxPayResultDTO = null;
/*从数据流中取出支付结果相关的数据封装到对象当中*/
try {
InputStream inStream = request.getInputStream();
int _buffer_size = 1024;
if (inStream != null) {
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
byte[] tempBytes = new byte[_buffer_size];
int count = -1;
while ((count = inStream.read(tempBytes, 0, _buffer_size)) != -1) outStream.write(tempBytes, 0, count);
tempBytes = null;
outStream.flush();
/*将流转换成字符串*/
String result = new String(outStream.toByteArray(), "UTF-8");
wxPayResultDTO = (WxPayResultDTO) XMLUtil.xmlStrToBean(result, WxPayResultDTO.class);
}
} catch (Exception e) {
e.getStackTrace();
}
/*如果对象不为空*/
if (ObjectUtil.isNotEmpty(wxPayResultDTO))
return ResultInfoUtil.success(weChatPayService.wechatPayBack(wxPayResultDTO));
else throw new RException(CustomErrcodeEnum.CHECK_SIGN_ERROR);
}
/**WxPayResultDTO**/ /* 微信回调固定返回的参数*/
@Getter
@Setter
@ToString
@XmlRootElement(name = "xml")
@XmlAccessorType(XmlAccessType.FIELD)
public class WxPayResultDTO {
//返回状态码
private String return_code;
//业务结果
private String result_code;
//返回信息
private String return_msg;
//公众账号ID
private String appid;
//订单金额
private String total_fee;
//现金支付金额
private String cash_fee;
//签名
private String sign;
//用户标识
private String openid;
//微信支付订单号
private String transaction_id;
//商户订单号
private String out_trade_no;
//商户号
private String mch_id;
//随机字符串
private String nonce_str;
//付款银行
private String bank_type;
//货币种类
private String fee_type;
//是否关注公众账号
private String is_subscribe;
//支付完成时间
private String time_end;
//交易类型
private String trade_type;
}
支付回调检查
此处为方便前端进行支付成功判断,并跳转页面,故此处可写个通过订单号查询预支付订单中的Trade_Status字段是否为success,并返回给前端进行判断