Browse Source

20220428新增微信支付成功回调接口,修改accessToken获取方法

master
zhangye 3 years ago
parent
commit
20d7eafff2
  1. 16
      util/src/main/java/com/ccsens/util/bean/wx/po/WxGetIp.java
  2. 145
      wechatutil/src/main/java/com/ccsens/wechatutil/bean/dto/NoticeResourceDecode.java
  3. 43
      wechatutil/src/main/java/com/ccsens/wechatutil/bean/dto/WxNativePayNotice.java
  4. 22
      wechatutil/src/main/java/com/ccsens/wechatutil/bean/vo/NativePayNoticeVo.java
  5. 133
      wechatutil/src/main/java/com/ccsens/wechatutil/payutil/wxnative/NativeUtils.java
  6. 4
      wechatutil/src/main/java/com/ccsens/wechatutil/util/WxConstant.java
  7. 45
      wechatutil/src/main/java/com/ccsens/wechatutil/wxcommon/WxCommonUtil.java

16
util/src/main/java/com/ccsens/util/bean/wx/po/WxGetIp.java

@ -0,0 +1,16 @@
package com.ccsens.util.bean.wx.po;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.List;
/**
* @author __zHangSan
*/
@Data
public class WxGetIp extends WxBaseEntity{
@JsonProperty("ipList")
private List<String> ip_list;
}

145
wechatutil/src/main/java/com/ccsens/wechatutil/bean/dto/NoticeResourceDecode.java

@ -0,0 +1,145 @@
package com.ccsens.wechatutil.bean.dto;
import lombok.Data;
import java.util.List;
/**
* @author
*/
@Data
public class NoticeResourceDecode {
public enum TRADE_STATE {
/*** 支付成功 */
SUCCESS("SUCCESS","支付成功"),
/*** 转入退款 */
REFUND("REFUND","转入退款"),
/*** 未支付 */
NOTPAY("NOTPAY","未支付"),
/*** 已关闭 */
CLOSED("CLOSED","已关闭"),
/*** 已撤销 */
REVOKED("REVOKED","已撤销"),
/*** 用户支付中 */
USERPAYING("USERPAYING","用户支付中"),
/*** 支付失败 */
PAYERROR("PAYERROR","支付失败");
public String value;
public String phase;
TRADE_STATE(String value,String thePhase){
this.value =value;
this.phase = thePhase;
}
}
/*** 微信支付订单号 微信支付系统生成的订单号 */
private String transaction_id;
/*** 订单金额 */
private AmountBean amount;
/*** 应用ID */
private String mchid;
/***
* 交易状态枚举值
* SUCCESS支付成功
* REFUND转入退款
* NOTPAY未支付
* CLOSED已关闭
* REVOKED已撤销付款码支付
* USERPAYING用户支付中付款码支付
* PAYERROR支付失败(其他原因如银行返回失败)
*/
private String trade_state;
/*** 银行类型,采用字符串类型的银行标识 */
private String bank_type;
/*** 支付完成时间 yyyy-MM-DDTHH:mm:ss+TIMEZONE*/
private String success_time;
/*** 支付者 */
private PayerBean payer;
/*** 商户订单号 商户系统内部订单号 */
private String out_trade_no;
/*** 应用ID */
private String appid;
/*** 交易状态描述*/
private String trade_state_desc;
/***
* 交易类型枚举值
* JSAPI公众号支付
* NATIVE扫码支付
* APPAPP支付
* MICROPAY付款码支付
* MWEBH5支付
* FACEPAY刷脸支付
*/
private String trade_type;
/*** 附加数据 */
private String attach;
/*** 场景信息 */
private SceneInfoBean scene_info;
/*** 优惠功能 */
private List<PromotionDetailBean> promotion_detail;
@Data
public static class AmountBean {
/*** 用户支付金额,单位为分 */
private int payer_total;
/*** 订单总金额,单位为分 */
private int total;
/*** 货币类型 */
private String currency;
/*** 用户支付币种 */
private String payer_currency;
}
@Data
public static class PayerBean {
/*** 用户标识 */
private String openid;
}
@Data
public static class SceneInfoBean {
/*** 终端设备号(门店号或收银设备ID) */
private String device_id;
}
@Data
public static class PromotionDetailBean {
/*** 优惠券面额 */
private int amount;
/*** 微信出资 */
private int wechatpay_contribute;
/*** 券ID */
private String coupon_id;
/*** 优惠范围 */
private String scope;
/*** 商户出资 */
private int merchant_contribute;
/*** 优惠名称 */
private String name;
/*** 优惠类型 */
private String type;
/*** 其他出资 */
private int other_contribute;
/*** 优惠币种 */
private String currency;
/*** 活动ID */
private String stock_id;
/*** 单品列表 */
private List<GoodsDetailBean> goods_detail;
@Data
public static class GoodsDetailBean {
/*** 商品备注 */
private String goods_remark;
/*** 商品数量 */
private int quantity;
/*** 商品优惠金额 */
private int discount_amount;
/*** 商品编码 */
private String goods_id;
/*** 商品单价 */
private int unit_price;
}
}
}

43
wechatutil/src/main/java/com/ccsens/wechatutil/bean/dto/WxNativePayNotice.java

@ -0,0 +1,43 @@
package com.ccsens.wechatutil.bean.dto;
import lombok.Data;
/**
* @author
*/
@Data
public class WxNativePayNotice {
/**"消息id"*/
private String id;
/**"通知创建的时间,格式为yyyy-MM-DDTHH:mm:ss+TIMEZONE"*/
private String create_time;
/**"通知的类型 SUCCESS"*/
private String event_type;
/**"通知的资源数据类型 encrypt-resource"*/
private String resource_type;
/**"回调摘要"*/
private String summary;
/**"微信支付回调消息"*/
private NoticeResource resource;
@Data
public static class NoticeResource {
//"对开启结果数据进行加密的加密算法,目前只支持AEAD_AES_256_GCM ")
private String algorithm;
//"Base64编码后的开启/停用结果数据密文")
private String ciphertext;
//"附加数据")
private String associated_data;
//"原始回调类型 为transaction")
private String original_type;
//"加密使用的随机串")
private String nonce;
}
@Data
public static class NoticeResourceDecode {
}
}

22
wechatutil/src/main/java/com/ccsens/wechatutil/bean/vo/NativePayNoticeVo.java

@ -0,0 +1,22 @@
package com.ccsens.wechatutil.bean.vo;
import lombok.Data;
/**
* @author
*/
@Data
public class NativePayNoticeVo {
/**"code"*/
private String code;
/**消息*/
private String message;
public NativePayNoticeVo() {
}
public NativePayNoticeVo(String code, String message) {
this.code = code;
this.message = message;
}
}

133
wechatutil/src/main/java/com/ccsens/wechatutil/payutil/wxnative/NativeUtils.java

@ -11,6 +11,10 @@ import com.google.zxing.common.BitMatrix;
import okhttp3.HttpUrl; import okhttp3.HttpUrl;
import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.RandomStringUtils;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*; import java.io.*;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URL; import java.net.URL;
@ -33,81 +37,11 @@ import java.util.UUID;
public class NativeUtils { public class NativeUtils {
static String schema = "WECHATPAY2-SHA256-RSA2048"; static String schema = "WECHATPAY2-SHA256-RSA2048";
// /**
// * 填写你的商户号
// */
// static String MCH_ID = "1624859575";
// /**
// * 填写证书序列号
// */
// static String SERIAL_NO = "71433832548290D95806FAF490878BB9B2677D4E";
// /**
// * 填写应用APP ID
// */
// static String APP_ID = "wxcb60fcfeaddeb3e3";
//
// /**
// * apiclient_key.pem文件地址 如C:\Users\Administrator\Desktop\wechat\apiclient_key.pem
// */
// static String PATH_PEM = "C:\\Users\\dou\\Desktop\\wxpay\\apiclient_key.pem";
/** /**
* 请求地址 * 请求地址
*/ */
static String NATIVE_URL = "https://api.mch.weixin.qq.com/v3/pay/transactions/native"; static String NATIVE_URL = "https://api.mch.weixin.qq.com/v3/pay/transactions/native";
//
// /**
// * 填写你的返回地址 需要HTTPS返回地址
// */
// static String RETURN_ADDRESS = "https://test.tall.wiki/gateway/defaultwbs/debug";
// public static void main(String[] args) {
// try {
// int money = 100;
// String description = "测试支付";
// HttpUrl httpurl = HttpUrl.parse(NATIVE_URL);
// //签名表头信息 Authorization
// String orderId = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
// String body = OrderData(orderId, money, description);
// String authorization = schema + " " + getToken("POST", httpurl, body, orderId);
// //下单调用的接口,JSON格式
// String codeUrl = nativePostBody(NATIVE_URL, body, authorization);
// System.out.println(codeUrl);
//
// JSONObject jsonObject = JSONObject.parseObject(codeUrl);
// String code_url = jsonObject.getString("code_url");
// if(StrUtil.isNotBlank(code_url)){
// String s = generateQRCode(code_url);
// System.out.println(s);
// }
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
// static String OrderData(String orderId, int money, String description) {
// // 应用ID appid
// // 直连商户号 mchid
// // 商品描述 description
// //商户订单号 out_trade_no
// //通知地址 notify_url
// //订单金额 amount对象: 总金额 total 货币类型 currency
// JSONObject jsonObject = new JSONObject();
// jsonObject.put("mchid", MCH_ID);
// jsonObject.put("out_trade_no", orderId);
// jsonObject.put("appid", APP_ID);
// jsonObject.put("description", description);
// jsonObject.put("notify_url", RETURN_ADDRESS);
// Map<String, Object> map = new HashMap<>();
// map.put("total", money);
// map.put("currency", "CNY");
// jsonObject.put("amount", map);
// return String.valueOf(jsonObject);
//
// }
/** /**
* 获取签名信息所有值 * 获取签名信息所有值
@ -240,26 +174,51 @@ public class NativeUtils {
return results; return results;
} }
public static String generateQRCode(String code_url) throws WriterException, IOException { // public static void main(String[] args) throws Exception {
//生成二维码 // String apiV3 = "Bb507Ek1mr95aAvvv9OMp2i9eWxZSv1G";
//设置二维码的尺寸 // String nonce = "bexg7pfYD50D";
int width = 200; // String ciphertext="i01zjaHjODHIkNkIXal6lKrfxrYtp2IZFe2NeUL/tU7si3p/G52XXqxTRQLuM03D1MTImmzLXocxVOgjNmAT3wb5/2RB6ty0HT2RJzGDnAkR0OxMQseUyiOUNRW3skJtj2/somJ/O1yHI2uktJLd1hitfu2TqoFe18ueBZdFYFHqnSKoayc3qTCKFsNLQ/gArvM0ODG3qCa2x9PypmcpDEx2U0KKdNmn/kLqDjIE6RdW9RpAhG+5wqOw3717sD5uvIg66PvUp7MdGMDiDi1SdcUqRm8x7Fl43uSIq9bAcsJxW4IT877tzkDHNjSY37iOFr9vuFpjMwLnhkZfClCfivh2R9ShrmFVlR1sXikr7rYQQGn0coSQZJ09UVd0UuwSxd5SNiLISkB/MzuLfvFSe2YTFvM/MR4iJy+JLQIEBZc7ZNhv4qrcRUOnjQlWO7G9+iUpFrviItzC7eN1KTkyOBs6QPI28uUDOt/OV548i+0Pf6xmD+rOS+6vvl1WR9F4LFUvCWr3w5uMmejyCRJmWoQY12boiwT+FxsdF0NJnoSheiY+bBZfcDJ/udpo2PBHuryJ";
int hight = 200; // String associated_data = "transaction";
//创建map //
Map<EncodeHintType, Object> hints = new HashMap<>(); // byte[] key = apiV3.getBytes("UTF-8");
hints.put(EncodeHintType.CHARACTER_SET,"UTF-8"); // WxAPIV3AesUtil aesUtil = new WxAPIV3AesUtil(key);
// String decryptToString = aesUtil.decryptToString(associated_data.getBytes("UTF-8"),nonce.getBytes("UTF-8"),ciphertext);
// System.out.println(decryptToString);
// }
/**
* 解密微信ApiV3消息
*/
public static class WxAPIV3AesUtil {
static final int KEY_LENGTH_BYTE = 32;
static final int TAG_LENGTH_BIT = 128;
private final byte[] aesKey;
//创建矩阵对象 调用谷歌提供的 public WxAPIV3AesUtil(byte[] key) {
BitMatrix bitMatrix = new MultiFormatWriter().encode(code_url, BarcodeFormat.QR_CODE, width, hight, hints); if (key.length != KEY_LENGTH_BYTE) {
throw new IllegalArgumentException("无效的ApiV3Key,长度必须为32个字节");
}
this.aesKey = key;
}
//创建二维码生成路径 public String decryptToString(byte[] associatedData, byte[] nonce, String ciphertext)
String filePath = "C:\\Users\\dou\\Desktop\\wxpay\\"; throws GeneralSecurityException, IOException {
String fileName = RandomStringUtils.randomAlphanumeric(10)+ ".jpg"; try {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
Path path = FileSystems.getDefault().getPath(filePath,fileName); SecretKeySpec key = new SecretKeySpec(aesKey, "AES");
GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce);
//将创建的矩阵转换成图片 cipher.init(Cipher.DECRYPT_MODE, key, spec);
MatrixToImageWriter.writeToPath(bitMatrix,"jpg",path); cipher.updateAAD(associatedData);
return filePath + fileName;
return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), "utf-8");
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
throw new IllegalStateException(e);
} catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
throw new IllegalArgumentException(e);
}
}
} }
} }

4
wechatutil/src/main/java/com/ccsens/wechatutil/util/WxConstant.java

@ -16,8 +16,8 @@ public class WxConstant {
/*** 默认小程序 */ /*** 默认小程序 */
public static final String ANYRING = "anyring"; public static final String ANYRING = "anyring";
/*** 获取微信服务器IP地址 */
public static final String URL_GET_WX_IP = "https://api.weixin.qq.com/cgi-bin/get_api_domain_ip?access_token=%1$s";
/*** 公众号获取accessToken */ /*** 公众号获取accessToken */
public static final String URL_GET_OAUTH2_ACCESS_TOKEN = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%1$s&secret=%2$s&code=%3$s&grant_type=authorization_code"; public static final String URL_GET_OAUTH2_ACCESS_TOKEN = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%1$s&secret=%2$s&code=%3$s&grant_type=authorization_code";
/*** 公众号通过模板发送订阅消息 */ /*** 公众号通过模板发送订阅消息 */

45
wechatutil/src/main/java/com/ccsens/wechatutil/wxcommon/WxCommonUtil.java

@ -1,5 +1,6 @@
package com.ccsens.wechatutil.wxcommon; package com.ccsens.wechatutil.wxcommon;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest; import cn.hutool.http.HttpRequest;
import com.ccsens.util.JacksonUtil; import com.ccsens.util.JacksonUtil;
@ -7,6 +8,7 @@ import com.ccsens.util.RedisUtil;
import com.ccsens.util.WebConstant; import com.ccsens.util.WebConstant;
import com.ccsens.util.bean.wx.po.WxAccessToken; import com.ccsens.util.bean.wx.po.WxAccessToken;
import com.ccsens.util.bean.wx.po.WxApiTicket; import com.ccsens.util.bean.wx.po.WxApiTicket;
import com.ccsens.util.bean.wx.po.WxGetIp;
import com.ccsens.util.exception.BaseException; import com.ccsens.util.exception.BaseException;
import com.ccsens.wechatutil.util.WxCodeError; import com.ccsens.wechatutil.util.WxCodeError;
import com.ccsens.wechatutil.util.WxConstant; import com.ccsens.wechatutil.util.WxConstant;
@ -40,7 +42,20 @@ public class WxCommonUtil {
public static String getAccessToken(String appId, String secret) throws BaseException { public static String getAccessToken(String appId, String secret) throws BaseException {
log.info("获取accessToken,appid:{}", appId.substring(appId.length() - 4)); log.info("获取accessToken,appid:{}", appId.substring(appId.length() - 4));
Object obj = util.redisUtil.get(WxConstant.ACCESS_TOKEN + appId); Object obj = util.redisUtil.get(WxConstant.ACCESS_TOKEN + appId);
if (obj == null || StrUtil.isBlank((String) obj)) { //使用redis内的token获取微信的服务器id,验证是否正确
boolean flag = false;
if (obj != null && StrUtil.isNotBlank((String) obj)) {
WxGetIp wxGetIp = null;
try {
wxGetIp = getWxIp((String) obj);
}catch (Exception e){
log.error("accessToken不正确,需要重新获取");
}
if(ObjectUtil.isNotNull(wxGetIp)){
flag = true;
}
}
if (!flag) {
WxAccessToken wxAccessToken; WxAccessToken wxAccessToken;
String url = String.format(WxConstant.URL_GET_ACCESS_TOKEN, appId, secret); String url = String.format(WxConstant.URL_GET_ACCESS_TOKEN, appId, secret);
String response = HttpRequest.get(url).execute().body(); String response = HttpRequest.get(url).execute().body();
@ -67,14 +82,33 @@ public class WxCommonUtil {
return (String) obj; return (String) obj;
} }
/**
* 获取微信服务器IP地址
*/
public static WxGetIp getWxIp(String accessToken) throws BaseException {
log.info("获取微信服务器IP地址");
WxGetIp wxGetIp;
String url = String.format(WxConstant.URL_GET_WX_IP, accessToken);
String response = HttpRequest.get(url).execute().body();
log.info("获取微信服务器IP地址返回: {}", response);
try {
if (StrUtil.isEmpty(response) || null == (wxGetIp = JacksonUtil.jsonToBean(response, WxGetIp.class))) {
return null;
}
} catch (IOException e) {
return null;
}
if (null != wxGetIp.getErrcode()) {
return null;
}
return wxGetIp;
}
/** /**
* 通过accessToken获取临时票据ticket * 通过accessToken获取临时票据ticket
*/ */
public static String getTicketByAccessToken(String accessToken) throws BaseException { public static String getTicketByAccessToken(String accessToken) throws BaseException {
log.info("获取ticket"); log.info("获取ticket");
// Object obj = util.redisUtil.get(WxConstant.ACCESS_TOKEN + appId);
// if (obj == null || StrUtil.isBlank((String) obj)) {
WxApiTicket wxApiTicket; WxApiTicket wxApiTicket;
String url = String.format(WxConstant.URL_GET_API_TICKET, accessToken); String url = String.format(WxConstant.URL_GET_API_TICKET, accessToken);
String response = HttpRequest.get(url).execute().body(); String response = HttpRequest.get(url).execute().body();
@ -93,11 +127,6 @@ public class WxCommonUtil {
if (StrUtil.isEmpty(wxApiTicket.getTicket())) { if (StrUtil.isEmpty(wxApiTicket.getTicket())) {
throw new BaseException(WxCodeError.API_TICKET_ERROR); throw new BaseException(WxCodeError.API_TICKET_ERROR);
} }
// util.redisUtil.set(WebConstant.Wx.ACCESS_TOKEN + appId, wxAccessToken.getAccessToken(), WebConstant.Wx.EXPIRE_TIME);
// log.info("存储access_token:{}", wxAccessToken.getAccessToken());
// return wxAccessToken.getAccessToken();
// }
// log.info("读取reids的token:{}", obj);
return wxApiTicket.getTicket(); return wxApiTicket.getTicket();
} }

Loading…
Cancel
Save