diff --git a/blank.png b/blank.png new file mode 100644 index 0000000..b71b21c Binary files /dev/null and b/blank.png differ diff --git a/pom.xml b/pom.xml index 4aa36da..f38a3dc 100644 --- a/pom.xml +++ b/pom.xml @@ -103,6 +103,49 @@ 2.2 + + + cn.hutool + hutool-all + 5.8.26 + + + + + org.springframework.boot + spring-boot-starter-data-redis + + + + + com.google.code.gson + gson + 2.8.8 + + + + + + com.github.wechatpay-apiv3 + wechatpay-java + 0.2.12 + + + com.github.wechatpay-apiv3 + wechatpay-apache-httpclient + 0.4.9 + + + + com.github.binarywang + wx-java-miniapp-spring-boot-starter + 4.4.0 + + + + + + diff --git a/src/main/java/com/greenorange/promotion/config/RedisConfig.java b/src/main/java/com/greenorange/promotion/config/RedisConfig.java new file mode 100644 index 0000000..74202a9 --- /dev/null +++ b/src/main/java/com/greenorange/promotion/config/RedisConfig.java @@ -0,0 +1,28 @@ +package com.greenorange.promotion.config; + + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +@Configuration +public class RedisConfig { + + @Bean + public RedisTemplate redisTemplate(RedisConnectionFactory factory) { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(factory); + + // 指定kv的序列化方式 + Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class); + redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); + redisTemplate.setKeySerializer(new StringRedisSerializer()); + + return redisTemplate; + } + + +} diff --git a/src/main/java/com/greenorange/promotion/config/WxAccessToken.java b/src/main/java/com/greenorange/promotion/config/WxAccessToken.java new file mode 100644 index 0000000..e63ac43 --- /dev/null +++ b/src/main/java/com/greenorange/promotion/config/WxAccessToken.java @@ -0,0 +1,31 @@ +package com.greenorange.promotion.config; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.context.annotation.Configuration; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 微信小程序获取token响应体 + */ +@Configuration +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WxAccessToken implements Serializable { + + @Schema(description = "获取到的凭证") + private String access_token; + + @Schema(description = "凭证有效时间,单位:秒。目前是7200秒之内的值。") + private String expires_in; + + @Serial + private static final long serialVersionUID = 1L; +} diff --git a/src/main/java/com/greenorange/promotion/controller/wechat/WechatGetQrcodeController.java b/src/main/java/com/greenorange/promotion/controller/wechat/WechatGetQrcodeController.java new file mode 100644 index 0000000..26f3bb8 --- /dev/null +++ b/src/main/java/com/greenorange/promotion/controller/wechat/WechatGetQrcodeController.java @@ -0,0 +1,137 @@ +package com.greenorange.promotion.controller.wechat; + + +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONUtil; +import com.greenorange.promotion.common.BaseResponse; +import com.greenorange.promotion.common.ErrorCode; +import com.greenorange.promotion.common.ResultUtils; +import com.greenorange.promotion.config.WxAccessToken; +import com.greenorange.promotion.service.wechat.WechatGetQrcodeService; +import com.greenorange.promotion.utils.QRCodeUtil; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; + + +@Slf4j +@RestController +@Tag(name = "获取二维码模块") +@RequestMapping("/qrcode") +public class WechatGetQrcodeController { + + + private final static String ACCESS_TOKEN_KEY = "accessToken"; + + + + @Resource + private RedisTemplate redisTemplate; + + + @Resource + private WechatGetQrcodeService wechatGetQrcodeService; + + + + /** + * (小程序端)获取接口调用凭据 + */ + @GetMapping("/get/token") + @Operation(summary = "(小程序端)获取接口调用凭据", description = "参数:无, 权限:所有人, 方法名:getAccessToken") + public BaseResponse getAccessToken() { + String accessToken = (String) redisTemplate.opsForValue().get(ACCESS_TOKEN_KEY); + if (accessToken == null) { + accessToken = wechatGetQrcodeService.getAccessToken().getAccess_token(); + } + WxAccessToken wxAccessToken = WxAccessToken.builder() + .access_token(accessToken) + .expires_in("7200").build(); + return ResultUtils.success(wxAccessToken); + } + + + + + /** + * 微信小程序获取二维码 + */ + @PostMapping("/get/qrcode") + @Operation(summary = "微信小程序获取二维码", description = "参数:无, 权限:所有人, 方法名:getQrcode") + public BaseResponse getQrcode(HttpServletRequest request) throws IOException { + String accessToken = (String) redisTemplate.opsForValue().get(ACCESS_TOKEN_KEY); + if (accessToken == null) { + accessToken = wechatGetQrcodeService.getAccessToken().getAccess_token(); + } + Map param = new HashMap<>(); + param.put("page", "pages/test/test"); + param.put("scene", "a=1"); + param.put("width", 430); // 宽度 + param.put("check_path", false); + param.put("env_version", "develop"); + String url = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + accessToken; + String jsonParams = JSONUtil.toJsonStr(param); + byte[] responseBytes = HttpUtil.createPost(url) + .header("Content-Type", "application/json") + .body(jsonParams) + .execute() + .bodyBytes(); + + + // 将二维码数据转换为 BufferedImage + BufferedImage qrImage = ImageIO.read(new ByteArrayInputStream(responseBytes)); + // 获取用户头像 + String avatarUrl = "https://img.picui.cn/free/2025/04/09/67f5d7bd6b368.jpg"; // 假设这是用户头像的URL + BufferedImage avatarImage = QRCodeUtil.getScaledAvatar(avatarUrl, 188, 188); + // 获取空白图像 + String blankUrl = "https://www.helloimg.com/i/2025/04/07/67f34b0490d07.png"; + // 加载一张空白图片来覆盖logo(假设是透明背景的图片) + BufferedImage blankImage = QRCodeUtil.getScaledAvatar(blankUrl, 196, 196); // 空白图片路径 + // 将头像转换为圆形 + BufferedImage circularAvatar = QRCodeUtil.getCircularImage(avatarImage); + // 将空白头像转换为圆形 + BufferedImage circularBlank = QRCodeUtil.getCircularImage(blankImage); + // 合并二维码和空白图片 + BufferedImage mergedWithBlank = QRCodeUtil.addImages(qrImage, circularBlank, 116, 116); // 偏移量根据需要调整 + // 合并二维码和头像 + BufferedImage resultImage = QRCodeUtil.addImages(mergedWithBlank, circularAvatar, 120, 120); + // 将合成后的图片转换为 Base64 编码 + InputStream resultStream = QRCodeUtil.bufferedImageToInputStream(resultImage); + byte[] resultBytes = resultStream.readAllBytes(); + + + // 生成图片并保存 + try (FileOutputStream fos = new FileOutputStream("qrcode.png")) { + fos.write(resultBytes); // 将二进制数据写入文件 + } catch (IOException e) { + e.printStackTrace(); + return ResultUtils.error(ErrorCode.OPERATION_ERROR, "保存二维码图片失败"); + } + // 将二维码转换为Base64编码 + String base64Image = "data:image/jpeg;base64," + Base64.getEncoder().encodeToString(responseBytes); + return ResultUtils.success(base64Image); + } + + + + + + +} diff --git a/src/main/java/com/greenorange/promotion/controller/wechat/WechatPayoutsController.java b/src/main/java/com/greenorange/promotion/controller/wechat/WechatPayoutsController.java new file mode 100644 index 0000000..7f0ae32 --- /dev/null +++ b/src/main/java/com/greenorange/promotion/controller/wechat/WechatPayoutsController.java @@ -0,0 +1,68 @@ +package com.greenorange.promotion.controller.wechat; + + +import cn.hutool.core.util.RandomUtil; +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONUtil; +import com.greenorange.promotion.common.BaseResponse; +import com.greenorange.promotion.common.ErrorCode; +import com.greenorange.promotion.common.ResultUtils; +import com.greenorange.promotion.utils.OrderNumberUtils; +import com.greenorange.promotion.utils.QRCodeUtil; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; + +@Slf4j +@RestController +@Tag(name = "微信提现模块") +@RequestMapping("/payouts") +public class WechatPayoutsController { + + + + /** + * 微信小程序积分提现到银行卡 + */ + @PostMapping("/points") + @Operation(summary = "微信小程序积分提现到银行卡", description = "参数:无, 权限:所有人, 方法名:getQrcode") + public BaseResponse pointsWithdrawnToBankCard(HttpServletRequest request) throws IOException { + Map param = new HashMap<>(); + param.put("mch_id", "1700326544"); + param.put("partner_trade_no", OrderNumberUtils.generateOrderId()); + param.put("nonce_str", "fbemuj4Xql7CYlQJAoTEPYxvPSNgYT2t"); + param.put("sign", ""); + param.put("enc_bank_no", "6222031207006363442"); + param.put("enc_true_name", "陈新知"); + param.put("bank_code", "1002"); + param.put("amount", 5); + param.put("desc", "提现"); + String url = "https://api.mch.weixin.qq.com/mmpaysptrans/pay_bank"; + String jsonParams = JSONUtil.toJsonStr(param); + String response = HttpUtil.createPost(url) + .header("Content-Type", "application/json") + .body(jsonParams) + .execute() + .body(); + + return ResultUtils.success(true); + } + + + + +} diff --git a/src/main/java/com/greenorange/promotion/service/wechat/WechatGetQrcodeService.java b/src/main/java/com/greenorange/promotion/service/wechat/WechatGetQrcodeService.java new file mode 100644 index 0000000..2c6cceb --- /dev/null +++ b/src/main/java/com/greenorange/promotion/service/wechat/WechatGetQrcodeService.java @@ -0,0 +1,12 @@ +package com.greenorange.promotion.service.wechat; + +import com.greenorange.promotion.config.WxAccessToken; + +public interface WechatGetQrcodeService { + + /** + * 获取微信token + */ + WxAccessToken getAccessToken(); + +} diff --git a/src/main/java/com/greenorange/promotion/service/wechat/impl/WechatGetQrcodeServiceImpl.java b/src/main/java/com/greenorange/promotion/service/wechat/impl/WechatGetQrcodeServiceImpl.java new file mode 100644 index 0000000..33be69c --- /dev/null +++ b/src/main/java/com/greenorange/promotion/service/wechat/impl/WechatGetQrcodeServiceImpl.java @@ -0,0 +1,54 @@ +package com.greenorange.promotion.service.wechat.impl; + + + +import cn.hutool.http.HttpUtil; +import com.google.gson.Gson; +import com.greenorange.promotion.config.WxAccessToken; +import com.greenorange.promotion.service.wechat.WechatGetQrcodeService; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.annotation.Resource; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; + +import java.util.concurrent.TimeUnit; + +@Service +public class WechatGetQrcodeServiceImpl implements WechatGetQrcodeService { + + + private final static String ACCESS_TOKEN_KEY = "accessToken"; + + @Resource + private RedisTemplate redisTemplate; + + + @Schema(description = "小程序 appId") + @Value("${wx.mini.appId}") + private String appId; + + @Schema(description = "小程序 appSecret") + @Value("${wx.mini.appSecret}") + private String appSecret; + + + /** + * 获取接口调用凭据 + */ + @Override + public WxAccessToken getAccessToken() { + String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appId + "&secret=" + appSecret; + String response = null; + try { + response = HttpUtil.get(url); + } catch (Exception e) { + e.printStackTrace(); + } + Gson gson = new Gson(); + WxAccessToken wxAccessToken = gson.fromJson(response, WxAccessToken.class); + String access_token = wxAccessToken.getAccess_token(); + redisTemplate.opsForValue().set(ACCESS_TOKEN_KEY, access_token, 7200, TimeUnit.SECONDS); + return wxAccessToken; + } +} diff --git a/src/main/java/com/greenorange/promotion/utils/OrderNumberUtils.java b/src/main/java/com/greenorange/promotion/utils/OrderNumberUtils.java new file mode 100644 index 0000000..3c3b2ca --- /dev/null +++ b/src/main/java/com/greenorange/promotion/utils/OrderNumberUtils.java @@ -0,0 +1,82 @@ +package com.greenorange.promotion.utils; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +public class OrderNumberUtils { + + + // 定义日期格式化器,精确到秒 + private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyyMMddHHmmss"); + + // 定义序列号最大值 + private static final int MAX_SEQUENCE = 999999; + + // 当前日期,初始化为当天 + private static String currentDate = getCurrentDate(); + + // 自增序列号,线程安全 + private static final AtomicInteger sequence = new AtomicInteger(0); + + // 锁对象,用于保护自增序列号的生成 + private static final Lock lock = new ReentrantLock(); + + + public static String generateOrderId() { + // 获取当前日期 + String today = getCurrentDate(); + + // 如果日期发生变化,重置序列号 + if (!today.equals(currentDate)) { + currentDate = today; + sequence.set(0); // 重置序列号 + } + + // 获取时间戳(精确到秒) + String timestamp = DATE_FORMAT.format(new Date()); + + // 获取4位随机数 + String randomNumber = generateRandomNumber(); + + // 获取6位自增序列号,并确保不超过最大值 + int seq = getNextSequence(); + + // 格式化序列号为6位 + String formattedSequence = String.format("%06d", seq); + + // 拼接生成订单号 + return timestamp + randomNumber + formattedSequence; + } + + // 获取当前日期(格式:yyyyMMdd) + private static String getCurrentDate() { + return new SimpleDateFormat("yyyyMMdd").format(new Date()); + } + + // 生成4位随机数(范围:0000到9999) + private static String generateRandomNumber() { + int random = (int) (Math.random() * 10000); // 生成0到9999之间的随机数 + return String.format("%04d", random); // 格式化为4位 + } + + // 获取下一个自增序列号,使用ReentrantLock来确保线程安全 + private static int getNextSequence() { + lock.lock(); // 获取锁 + try { + int seq = sequence.incrementAndGet(); + if (seq > MAX_SEQUENCE) { + sequence.set(0); // 达到最大值后重置 + seq = sequence.incrementAndGet(); + } + return seq; + } finally { + lock.unlock(); // 确保在最终释放锁 + } + } + + + +} diff --git a/src/main/java/com/greenorange/promotion/utils/QRCodeUtil.java b/src/main/java/com/greenorange/promotion/utils/QRCodeUtil.java new file mode 100644 index 0000000..866362d --- /dev/null +++ b/src/main/java/com/greenorange/promotion/utils/QRCodeUtil.java @@ -0,0 +1,131 @@ +package com.greenorange.promotion.utils; + + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.geom.Ellipse2D; +import java.awt.geom.RoundRectangle2D; +import java.awt.image.BufferedImage; +import java.io.*; +import java.net.URL; + +public class QRCodeUtil { + + + // 将InputStream转换为BufferedImage + public static BufferedImage inputStreamToBufferedImage(InputStream inputStream) throws IOException { + return ImageIO.read(inputStream); + } + + // 获取用户头像并缩放到指定大小 + public static BufferedImage getScaledAvatar(String url, int targetWidth, int targetHeight) throws IOException { + BufferedImage avatarImage = ImageIO.read(new URL(url)); + int width = avatarImage.getWidth(); + int height = avatarImage.getHeight(); + + // 如果头像大于目标大小,进行缩放 + if (width > targetWidth && height > targetHeight) { + width = targetWidth; + height = targetHeight; + } + + return toBufferedImage(avatarImage.getScaledInstance(width, height, Image.SCALE_DEFAULT)); + } + + // 将Image转换为BufferedImage + public static BufferedImage toBufferedImage(Image image) { + if (image instanceof BufferedImage) { + return (BufferedImage) image; + } + BufferedImage bimage = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_RGB); + Graphics g = bimage.createGraphics(); + g.drawImage(image, 0, 0, null); + g.dispose(); + return bimage; + } + +// // 将图片转为圆形 +// public static BufferedImage getCircularImage(BufferedImage img) { +// int width = img.getWidth(); +// int height = img.getHeight(); +// BufferedImage circularImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); +// Graphics2D g = circularImage.createGraphics(); +// Ellipse2D.Double shape = new Ellipse2D.Double(0, 0, width, height); +// g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); +// g.setClip(shape); +// g.drawImage(img, 0, 0, null); +// g.dispose(); +// return circularImage; +// } + + + /** + * 将图片裁剪成圆形,并确保背景透明 + * @param img 原始图片 + * @return 裁剪后的圆形图片,背景为透明 + */ + public static BufferedImage getCircularImage(BufferedImage img) { + int width = img.getWidth(); + int height = img.getHeight(); + + // 创建一个透明背景的 BufferedImage + BufferedImage circularImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + + Graphics2D g = circularImage.createGraphics(); + Ellipse2D.Double shape = new Ellipse2D.Double(0, 0, width, height); + + g.setComposite(AlphaComposite.Src); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g.setColor(Color.WHITE); + g.fill(shape); + g.setComposite(AlphaComposite.SrcAtop); + g.drawImage(img, 0, 0, null); + g.dispose(); + + return circularImage; + } + + + + + // 合并二维码和头像 + public static BufferedImage addImages(BufferedImage qrImage, BufferedImage avatarImage, int x, int y) { + Graphics2D g = qrImage.createGraphics(); + g.drawImage(avatarImage, x, y, avatarImage.getWidth(), avatarImage.getHeight(), null); + g.dispose(); + return qrImage; + } + + // 合并二维码和空白图片 + public static BufferedImage addBlankImage(BufferedImage qrImage, BufferedImage blankImage, int x, int y) { + Graphics2D g = qrImage.createGraphics(); + g.drawImage(blankImage, x, y, blankImage.getWidth(), blankImage.getHeight(), null); + g.dispose(); + return qrImage; + } + + // 将BufferedImage转换为InputStream + public static InputStream bufferedImageToInputStream(BufferedImage image) throws IOException { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + ImageIO.write(image, "png", os); + return new ByteArrayInputStream(os.toByteArray()); + } + + // 添加文字到图片上 + public static BufferedImage addTextToImage(BufferedImage image, String text, Color color, Font font, int x, int y) { + Graphics2D g = image.createGraphics(); + g.setColor(color); + g.setFont(font); + g.drawString(text, x, y); + g.dispose(); + return image; + } + + // 保存图片到文件 + public static void saveImageToFile(BufferedImage image, String path) throws IOException { + File outputfile = new File(path); + ImageIO.write(image, "png", outputfile); + } + + +} diff --git a/src/main/java/com/greenorange/promotion/utils/paybank/AesUtil.java b/src/main/java/com/greenorange/promotion/utils/paybank/AesUtil.java new file mode 100644 index 0000000..f6924f5 --- /dev/null +++ b/src/main/java/com/greenorange/promotion/utils/paybank/AesUtil.java @@ -0,0 +1,64 @@ +package com.greenorange.promotion.utils.paybank; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.Base64; + +import javax.crypto.Cipher; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +public class AesUtil { + static final int KEY_LENGTH_BYTE = 32; + static final int TAG_LENGTH_BIT = 128; + private final byte[] aesKey; + + /** + * 创建解密类 + * + * @param key + */ + public AesUtil(byte[] key) { + if (key.length != KEY_LENGTH_BYTE) { + throw new IllegalArgumentException("无效的ApiV3Key,长度必须为32个字节"); + } + this.aesKey = key; + + } + + /** + * 解密数据 + * + * @param associatedData + * 附加数据包 + * @param nonce + * 加密使用的随机串初始化向量 + * @param ciphertext + * Base64编码后的密文 + * @return + * @throws GeneralSecurityException + * @throws IOException + */ + public String decryptToString(byte[] associatedData, byte[] nonce, String ciphertext) + throws GeneralSecurityException, IOException { + try { + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + + SecretKeySpec key = new SecretKeySpec(aesKey, "AES"); + GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce); + + cipher.init(Cipher.DECRYPT_MODE, key, spec); + cipher.updateAAD(associatedData); + + 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); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/greenorange/promotion/utils/paybank/CompanyWxPayBankBuilder.java b/src/main/java/com/greenorange/promotion/utils/paybank/CompanyWxPayBankBuilder.java new file mode 100644 index 0000000..42f676a --- /dev/null +++ b/src/main/java/com/greenorange/promotion/utils/paybank/CompanyWxPayBankBuilder.java @@ -0,0 +1,240 @@ +package com.greenorange.promotion.utils.paybank; + +import java.util.Map; + +import net.sf.json.JSONObject; + +/** + * v1.0.2 + * + * @author 周工 2020-06-01 + */ +public class CompanyWxPayBankBuilder implements WxPayDataBuilder { + + private static String sendUrl = "https://api.mch.weixin.qq.com/mmpaysptrans/pay_bank"; + + private String certPath, API_KEY, mch_id, nonce_str, partner_trade_no, desc, sign, enc_bank_no, enc_true_name, + bank_code, enc_bank_no_pwd, enc_true_name_pwd; + private String pub_key; + private int amount; + private StringBuffer reStringBuffer; + private StringBuffer signStringBuffer; + private boolean build = false; + + public CompanyWxPayBankBuilder(String certPath, String pub_key) { + nonce_str = WxPayUtil.getRandomStr(20); + int startIndex = pub_key.indexOf("-----BEGIN RSA PUBLIC KEY-----"); + int endIndex = pub_key.indexOf("-----END RSA PUBLIC KEY-----"); + this.pub_key = pub_key; + + if (startIndex >= 0 && endIndex >= 0) { + this.pub_key = pub_key.substring(startIndex + "-----BEGIN RSA PUBLIC KEY-----".length(), endIndex); + } + + this.certPath = certPath; + partner_trade_no = System.currentTimeMillis() + WxPayUtil.getRandomStr(8); + } + + public int getAmount() { + return amount; + } + + public void setAmount(int amount) { + this.amount = amount; + } + + public static String getSendUrl() { + return sendUrl; + } + + public static void setSendUrl(String sendUrl) { + CompanyWxPayBankBuilder.sendUrl = sendUrl; + } + + public String getCertPath() { + return certPath; + } + + public void setCertPath(String certPath) { + this.certPath = certPath; + } + + public String getAPI_KEY() { + return API_KEY; + } + + public void setAPI_KEY(String aPI_KEY) { + API_KEY = aPI_KEY; + } + + public String getmch_id() { + return mch_id; + } + + public void setmch_id(String mch_id) { + this.mch_id = mch_id; + } + + public String getNonce_str() { + return nonce_str; + } + + public void setNonce_str(String nonce_str) { + this.nonce_str = nonce_str; + } + + public String getPartner_trade_no() { + return partner_trade_no; + } + + public void setPartner_trade_no(String partner_trade_no) { + this.partner_trade_no = partner_trade_no; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public String getMch_id() { + return mch_id; + } + + public void setMch_id(String mch_id) { + this.mch_id = mch_id; + } + + public String getEnc_bank_no() { + return enc_bank_no; + } + + public void setEnc_bank_no(String enc_bank_no) { + try { + enc_bank_no_pwd = RSAEncryp.encrypt(enc_bank_no.getBytes("utf-8"), pub_key, 11, + "RSA/ECB/OAEPWITHSHA-1ANDMGF1PADDING"); + } catch (Exception e) { + e.printStackTrace(); + enc_bank_no_pwd = ""; + } + this.enc_bank_no = enc_bank_no; + } + + public String getEnc_true_name() { + return enc_true_name; + } + + public void setEnc_true_name(String enc_true_name) { + try { + enc_true_name_pwd = RSAEncryp.encrypt(enc_true_name.getBytes("utf-8"), pub_key, 11, + "RSA/ECB/OAEPWITHSHA-1ANDMGF1PADDING"); + } catch (Exception e) { + e.printStackTrace(); + enc_true_name_pwd = ""; + } + this.enc_true_name = enc_true_name; + } + + public String getBank_code() { + return bank_code; + } + + public void setBank_code(String bank_code) { + this.bank_code = bank_code; + } + + @Override + public boolean build() throws LackParamExceptions { + build = false; + reStringBuffer = new StringBuffer(); + signStringBuffer = new StringBuffer(); + reStringBuffer.append(""); + appendParam("amount", amount + "", true); + appendParam("bank_code", bank_code + "", true); + appendParam("desc", desc, true); + appendParam("enc_bank_no", enc_bank_no_pwd, true); + appendParam("enc_true_name", enc_true_name_pwd, true); + appendParam("mch_id", mch_id, true); + appendParam("nonce_str", nonce_str, true); + appendParam("partner_trade_no", partner_trade_no, true); + getSign(API_KEY, this.signStringBuffer.toString()); + appendParam("sign", sign, true); + reStringBuffer.append(""); + build = true; + return true; + } + + @SuppressWarnings("null") + @Override + public JSONObject hand() throws LackParamExceptions { + if (!build) { + throw new LackParamExceptions("未build成功,请先确认build成功后再运行"); + } + + if (certPath == null || certPath.length() == 0) { + throw new LackParamExceptions("未设置证书路径"); + } + + if (pub_key == null || pub_key.length() == 0) { + throw new LackParamExceptions("未设置公钥"); + } + + String result = ""; + JSONObject resultJson = new JSONObject(); + + try { + + result = WxPayUtil.doPostDataWithCert(sendUrl, reStringBuffer.toString(), mch_id, certPath); + + Map getResult = WxPayUtil.xmlToMap(result); + if (getResult.get("result_code") == null || !"SUCCESS".equalsIgnoreCase(getResult.get("result_code"))) { + resultJson.put("return_code", "FAIL"); + resultJson.put("return_msg", getResult.get("return_msg")); + resultJson.put("err_code_des", getResult.get("err_code_des")); + resultJson.put("err_code", getResult.get("err_code")); + } else { + resultJson.put("return_code", "SUCCESS"); + resultJson.put("err_code_des", getResult.get("err_code_des")); + resultJson.put("err_code", getResult.get("err_code")); + resultJson.put("return_msg", getResult.get("return_msg")); + resultJson.put("amount", getResult.get("amount")); + resultJson.put("payment_no", getResult.get("payment_no")); + resultJson.put("cmms_amt", getResult.get("cmms_amt")); + resultJson.put("partner_trade_no", getResult.get("partner_trade_no")); + resultJson.put("nonce_str", getResult.get("nonce_str")); + resultJson.put("mch_id", getResult.get("mch_id")); + resultJson.put("mch_appid", getResult.get("mch_appid")); + } + } catch (Exception e) { + e.printStackTrace(); + } + + return resultJson; + } + + private void getSign(String apikey, String value) throws LackParamExceptions { + if (apikey == null) { + throw new LackParamExceptions("商户号API秘钥不能为空"); + } else { + sign = WxPayUtil.MD5(signStringBuffer.toString() + "key=" + apikey); + } + } + + private void appendParam(String key, String value, boolean isneed) throws LackParamExceptions { + + if (value == null || value.length() == 0) { + if (isneed) { + throw new LackParamExceptions("参数" + key + "不能为空"); + } else { + return; + } + } + this.reStringBuffer.append("<" + key + ">"); + this.reStringBuffer.append(""); + this.reStringBuffer.append(""); + this.signStringBuffer.append(key + "=" + value + "&"); + } + +} diff --git a/src/main/java/com/greenorange/promotion/utils/paybank/CompanyWxPayBankQueryBuilder.java b/src/main/java/com/greenorange/promotion/utils/paybank/CompanyWxPayBankQueryBuilder.java new file mode 100644 index 0000000..c25ae58 --- /dev/null +++ b/src/main/java/com/greenorange/promotion/utils/paybank/CompanyWxPayBankQueryBuilder.java @@ -0,0 +1,169 @@ +package com.greenorange.promotion.utils.paybank; + +import java.util.Map; + +import net.sf.json.JSONObject; + +/** + * v1.0.2 + * + * @author 周工 2020-06-01 + */ +public class CompanyWxPayBankQueryBuilder implements WxPayDataBuilder { + private static String sendUrl = "https://api.mch.weixin.qq.com/mmpaysptrans/query_bank"; + + private String certPath, API_KEY, mch_id, nonce_str, partner_trade_no, sign; + private StringBuffer reStringBuffer; + private StringBuffer signStringBuffer; + private boolean build = false; + + public CompanyWxPayBankQueryBuilder(String certPath, String partner_trade_no) { + nonce_str = WxPayUtil.getRandomStr(20); + this.certPath = certPath; + this.partner_trade_no = partner_trade_no; + } + + public static String getSendUrl() { + return sendUrl; + } + + public static void setSendUrl(String sendUrl) { + CompanyWxPayBankQueryBuilder.sendUrl = sendUrl; + } + + public String getCertPath() { + return certPath; + } + + public void setCertPath(String certPath) { + this.certPath = certPath; + } + + public String getAPI_KEY() { + return API_KEY; + } + + public void setAPI_KEY(String aPI_KEY) { + API_KEY = aPI_KEY; + } + + public String getmch_id() { + return mch_id; + } + + public void setmch_id(String mch_id) { + this.mch_id = mch_id; + } + + public String getNonce_str() { + return nonce_str; + } + + public void setNonce_str(String nonce_str) { + this.nonce_str = nonce_str; + } + + public String getPartner_trade_no() { + return partner_trade_no; + } + + public void setPartner_trade_no(String partner_trade_no) { + this.partner_trade_no = partner_trade_no; + } + + public String getMch_id() { + return mch_id; + } + + public void setMch_id(String mch_id) { + this.mch_id = mch_id; + } + + @Override + public boolean build() throws LackParamExceptions { + build = false; + reStringBuffer = new StringBuffer(); + signStringBuffer = new StringBuffer(); + reStringBuffer.append(""); + appendParam("mch_id", mch_id, true); + appendParam("nonce_str", nonce_str, true); + appendParam("partner_trade_no", partner_trade_no, true); + getSign(API_KEY, this.signStringBuffer.toString()); + appendParam("sign", sign, true); + reStringBuffer.append(""); + build = true; + return true; + } + + @SuppressWarnings("null") + @Override + public JSONObject hand() throws LackParamExceptions { + if (!build) { + throw new LackParamExceptions("未build成功,请先确认build成功后再运行"); + } + + if (certPath == null || certPath.length() == 0) { + throw new LackParamExceptions("未设置证书路径"); + } + + String result = ""; + JSONObject resultJson = new JSONObject(); + + try { + + result = WxPayUtil.doPostDataWithCert(sendUrl, reStringBuffer.toString(), mch_id, certPath); + + Map getResult = WxPayUtil.xmlToMap(result); + if (getResult.get("result_code") == null || !"SUCCESS".equalsIgnoreCase(getResult.get("result_code"))) { + resultJson.put("return_code", "FAIL"); + resultJson.put("return_msg", getResult.get("return_msg")); + resultJson.put("err_code_des", getResult.get("err_code_des")); + resultJson.put("err_code", getResult.get("err_code")); + } else { + resultJson.put("return_code", "SUCCESS"); + resultJson.put("err_code_des", getResult.get("err_code_des")); + resultJson.put("err_code", getResult.get("err_code")); + resultJson.put("return_msg", getResult.get("return_msg")); + resultJson.put("amount", getResult.get("amount")); + resultJson.put("payment_no", getResult.get("payment_no")); + resultJson.put("cmms_amt", getResult.get("cmms_amt")); + resultJson.put("partner_trade_no", getResult.get("partner_trade_no")); + resultJson.put("nonce_str", getResult.get("nonce_str")); + resultJson.put("mch_id", getResult.get("mch_id")); + resultJson.put("mch_appid", getResult.get("mch_appid")); + resultJson.put("status", getResult.get("status")); + resultJson.put("create_time", getResult.get("create_time")); + resultJson.put("reason", getResult.get("reason")); + + } + } catch (Exception e) { + e.printStackTrace(); + } + + return resultJson; + } + + private void getSign(String apikey, String value) throws LackParamExceptions { + if (apikey == null) { + throw new LackParamExceptions("商户号API秘钥不能为空"); + } else { + sign = WxPayUtil.MD5(signStringBuffer.toString() + "key=" + apikey); + } + } + + private void appendParam(String key, String value, boolean isneed) throws LackParamExceptions { + + if (value == null || value.length() == 0) { + if (isneed) { + throw new LackParamExceptions("参数" + key + "不能为空"); + } else { + return; + } + } + this.reStringBuffer.append("<" + key + ">"); + this.reStringBuffer.append(""); + this.reStringBuffer.append(""); + this.signStringBuffer.append(key + "=" + value + "&"); + } + +} diff --git a/src/main/java/com/greenorange/promotion/utils/paybank/CompanyWxPayBuilder.java b/src/main/java/com/greenorange/promotion/utils/paybank/CompanyWxPayBuilder.java new file mode 100644 index 0000000..fc57a8f --- /dev/null +++ b/src/main/java/com/greenorange/promotion/utils/paybank/CompanyWxPayBuilder.java @@ -0,0 +1,234 @@ +package com.greenorange.promotion.utils.paybank; + +import java.util.Map; + +import net.sf.json.JSONObject; + +/** + * v1.0.2 + * + * @author 周工 2020-06-01 + */ +public class CompanyWxPayBuilder implements WxPayDataBuilder { + + private static String sendUrl = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers"; + + private String certPath, API_KEY, mch_appid, mchid, nonce_str, partner_trade_no, desc, check_name, spbill_create_ip, + openid, device_info, re_user_name, sign; + + private int amount; + private StringBuffer reStringBuffer; + private StringBuffer signStringBuffer; + private boolean build = false; + + public CompanyWxPayBuilder(String certPath) { + nonce_str = WxPayUtil.getRandomStr(20); + this.certPath = certPath; + // 默认订单时间为半小时内有效 + partner_trade_no = System.currentTimeMillis() + WxPayUtil.getRandomStr(8); + check_name = "NO_CHECK"; + } + + public int getAmount() { + return amount; + } + + public void setAmount(int amount) { + this.amount = amount; + } + + public static String getSendUrl() { + return sendUrl; + } + + public static void setSendUrl(String sendUrl) { + CompanyWxPayBuilder.sendUrl = sendUrl; + } + + public String getCertPath() { + return certPath; + } + + public void setCertPath(String certPath) { + this.certPath = certPath; + } + + public String getAPI_KEY() { + return API_KEY; + } + + public void setAPI_KEY(String aPI_KEY) { + API_KEY = aPI_KEY; + } + + public String getMch_appid() { + return mch_appid; + } + + public void setMch_appid(String mch_appid) { + this.mch_appid = mch_appid; + } + + public String getMchid() { + return mchid; + } + + public void setMchid(String mchid) { + this.mchid = mchid; + } + + public String getNonce_str() { + return nonce_str; + } + + public void setNonce_str(String nonce_str) { + this.nonce_str = nonce_str; + } + + public String getPartner_trade_no() { + return partner_trade_no; + } + + public void setPartner_trade_no(String partner_trade_no) { + this.partner_trade_no = partner_trade_no; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public String getCheck_name() { + return check_name; + } + + public void setCheck_name(String check_name) { + this.check_name = check_name; + } + + public String getSpbill_create_ip() { + return spbill_create_ip; + } + + public void setSpbill_create_ip(String spbill_create_ip) { + this.spbill_create_ip = spbill_create_ip; + } + + public String getOpenid() { + return openid; + } + + public void setOpenid(String openid) { + this.openid = openid; + } + + public String getDevice_info() { + return device_info; + } + + public void setDevice_info(String device_info) { + this.device_info = device_info; + } + + public String getRe_user_name() { + return re_user_name; + } + + public void setRe_user_name(String re_user_name) { + this.re_user_name = re_user_name; + } + + @Override + public boolean build() throws LackParamExceptions { + build = false; + reStringBuffer = new StringBuffer(); + signStringBuffer = new StringBuffer(); + + reStringBuffer.append(""); + appendParam("amount", amount + "", true); + appendParam("check_name", check_name, true); + appendParam("desc", desc, true); + appendParam("device_info", device_info, false); + appendParam("mch_appid", mch_appid, true); + appendParam("mchid", mchid, true); + appendParam("nonce_str", nonce_str, true); + appendParam("openid", openid, true); + appendParam("partner_trade_no", partner_trade_no, true); + appendParam("re_user_name", re_user_name, false); + appendParam("spbill_create_ip", spbill_create_ip, true); + + getSign(API_KEY, this.signStringBuffer.toString()); + + appendParam("sign", sign, true); + reStringBuffer.append(""); + build = true; + return true; + } + + @SuppressWarnings("null") + @Override + public JSONObject hand() throws LackParamExceptions { + if (!build) { + throw new LackParamExceptions("未build成功,请先确认build成功后再运行"); + } + + if (certPath == null || certPath.length() == 0) { + throw new LackParamExceptions("未设置证书路径"); + } + + String result = ""; + JSONObject resultJson = new JSONObject(); + + try { + result = WxPayUtil.doPostDataWithCert(sendUrl, reStringBuffer.toString(), mchid, certPath); + + Map getResult = WxPayUtil.xmlToMap(result); + if (getResult.get("result_code") == null || !"SUCCESS".equalsIgnoreCase(getResult.get("result_code"))) { + resultJson.put("return_code", "FAIL"); + resultJson.put("return_msg", getResult.get("return_msg")); + resultJson.put("err_code_des", getResult.get("err_code_des")); + resultJson.put("err_code", getResult.get("err_code")); + } else { + resultJson.put("return_code", "SUCCESS"); + resultJson.put("return_msg", getResult.get("return_msg")); + resultJson.put("payment_time", getResult.get("payment_time")); + resultJson.put("payment_no", getResult.get("payment_no")); + resultJson.put("partner_trade_no", getResult.get("partner_trade_no")); + resultJson.put("nonce_str", getResult.get("nonce_str")); + resultJson.put("mchid", getResult.get("mchid")); + resultJson.put("mch_appid", getResult.get("mch_appid")); + } + } catch (Exception e) { + e.printStackTrace(); + } + + return resultJson; + } + + private void getSign(String apikey, String value) throws LackParamExceptions { + if (apikey == null) { + throw new LackParamExceptions("商户号API秘钥不能为空"); + } else { + sign = WxPayUtil.MD5(signStringBuffer.toString() + "key=" + apikey); + } + } + + private void appendParam(String key, String value, boolean isneed) throws LackParamExceptions { + + if (value == null) { + if (isneed) { + throw new LackParamExceptions("参数" + key + "不能为空"); + } else { + return; + } + } + this.reStringBuffer.append("<" + key + ">"); + this.reStringBuffer.append(""); + this.reStringBuffer.append(""); + this.signStringBuffer.append(key + "=" + value + "&"); + } + +} diff --git a/src/main/java/com/greenorange/promotion/utils/paybank/EasyPayBuilder.java b/src/main/java/com/greenorange/promotion/utils/paybank/EasyPayBuilder.java new file mode 100644 index 0000000..25947c5 --- /dev/null +++ b/src/main/java/com/greenorange/promotion/utils/paybank/EasyPayBuilder.java @@ -0,0 +1,180 @@ +package com.greenorange.promotion.utils.paybank; + +import java.io.IOException; + +import net.sf.json.JSONObject; + +public class EasyPayBuilder implements WxPayDataBuilder { + + private static String sendUrl = "http://pay2.w-x.net.cn/Pay/PlaceOrder"; + private static String sendUrl2 = "http://api.sczsgc.cn/Pay/PlaceOrder"; + + private String notify_url, return_url, orderid, orderuid, goodsname, key, token; + + private int uid, istype; + + private float price; + + private StringBuffer reStringBuffer; + private StringBuffer signStringBuffer; + private boolean build = false; + + public EasyPayBuilder() { + orderid = System.currentTimeMillis() + WxPayUtil.getRandomStr(8); + orderuid = System.currentTimeMillis() + WxPayUtil.getRandomStr(8); + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + + public String getNotify_url() { + return notify_url; + } + + public void setNotify_url(String notify_url) { + this.notify_url = notify_url; + } + + public String getReturn_url() { + return return_url; + } + + public void setReturn_url(String return_url) { + this.return_url = return_url; + } + + public String getOrderid() { + return orderid; + } + + public void setOrderid(String orderid) { + this.orderid = orderid; + } + + public String getOrderuid() { + return orderuid; + } + + public void setOrderuid(String orderuid) { + this.orderuid = orderuid; + } + + public String getGoodsname() { + return goodsname; + } + + public void setGoodsname(String goodsname) { + this.goodsname = goodsname; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public int getUid() { + return uid; + } + + public void setUid(int uid) { + this.uid = uid; + } + + public int getIstype() { + return istype; + } + + public void setIstype(int istype) { + this.istype = istype; + } + + public float getPrice() { + return price; + } + + public void setPrice(float price) { + this.price = price; + } + + @Override + public boolean build() throws LackParamExceptions { + + build = false; + reStringBuffer = new StringBuffer(); + signStringBuffer = new StringBuffer(); + + appendParam("goodsname", goodsname, true); + appendParam("istype", istype + "", true); + appendParam("notify_url", notify_url, true); + appendParam("orderid", orderid, true); + appendParam("orderuid", orderuid, false); + appendParam("price", price + "", true); + appendParam("return_url", return_url, true); + + if (token == null) { + throw new LackParamExceptions("参数token不能为空"); + } + + signStringBuffer.append(token); + + appendParam("uid", uid + "", true); + + key = WxPayUtil.MD5(signStringBuffer.toString()); + + appendParam("key", key, true); + + build = true; + + System.out.println(reStringBuffer.toString()); + + return build; + } + + @Override + public JSONObject hand() throws LackParamExceptions { + if (!build) { + throw new LackParamExceptions("未build成功,请先确认build成功后再运行"); + } + String result = ""; + try { + // 正常的域名 + result = WxPayUtil.sendHttpRequest(sendUrl, reStringBuffer.toString(), "application/x-www-form-urlencoded", + "utf-8", "POST"); + } catch (IOException e) { + e.printStackTrace(); + // 备用域名 + try { + result = WxPayUtil.sendHttpRequest(sendUrl2, reStringBuffer.toString(), + "application/x-www-form-urlencoded", "utf-8", "POST"); + } catch (IOException e1) { + e1.printStackTrace(); + throw new LackParamExceptions(e1.toString()); + } + } + System.out.println(result); + return null; + } + + private void appendParam(String key, String value, boolean isneed) throws LackParamExceptions { + + if (value == null) { + if (isneed) { + throw new LackParamExceptions("参数" + key + "不能为空"); + } else { + return; + } + } + + this.reStringBuffer.append(key + "=" + value + "&"); + this.signStringBuffer.append(value); + } + +} diff --git a/src/main/java/com/greenorange/promotion/utils/paybank/GetOpenidBuilder.java b/src/main/java/com/greenorange/promotion/utils/paybank/GetOpenidBuilder.java new file mode 100644 index 0000000..3465bcf --- /dev/null +++ b/src/main/java/com/greenorange/promotion/utils/paybank/GetOpenidBuilder.java @@ -0,0 +1,97 @@ +package com.greenorange.promotion.utils.paybank; + +import java.io.IOException; + +import net.sf.json.JSONObject; + +/** + * v1.0.2 + * + * @author 周工 2020-06-01 + */ +public class GetOpenidBuilder implements WxPayDataBuilder { + + private String appid, appsecret, code; + private static String sendUrl = "https://api.weixin.qq.com/sns/oauth2/access_token"; + private StringBuffer reStringBuffer; + private boolean build = false; + + public static String getSendUrl() { + return sendUrl; + } + + public static void setSendUrl(String sendUrl) { + GetOpenidBuilder.sendUrl = sendUrl; + } + + public String getAppid() { + return appid; + } + + public void setAppid(String appid) { + this.appid = appid; + } + + public String getAppsecret() { + return appsecret; + } + + public void setAppsecret(String appsecret) { + this.appsecret = appsecret; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + private void appendParam(String key, String value, boolean isneed) throws LackParamExceptions { + + if (value == null) { + if (isneed) { + throw new LackParamExceptions("参数" + key + "不能为空"); + } else { + return; + } + } + this.reStringBuffer.append(key + "=" + value + "&"); + } + + @Override + public boolean build() throws LackParamExceptions { + // TODO Auto-generated method stub + build = false; + reStringBuffer = new StringBuffer(); + appendParam("appid", appid, true); + appendParam("secret", appsecret, true); + appendParam("code", code, true); + appendParam("grant_type", "authorization_code", true); + reStringBuffer.deleteCharAt(reStringBuffer.length() - 1); + build = true; + + return build; + } + + @Override + public JSONObject hand() throws LackParamExceptions { + if (!build) { + throw new LackParamExceptions("未build成功,请先确认build成功后再运行"); + } + String result = ""; + JSONObject jsonObject = null; + try { + // 正常是的域名 + result = WxPayUtil.sendHttpsRequest(sendUrl, reStringBuffer.toString(), "text/xml", "utf-8", "GET"); + jsonObject = JSONObject.fromObject(result); + } catch (IOException e) { + e.printStackTrace(); + // 备用域名 + throw new LackParamExceptions(e.toString()); + } + return jsonObject; + } + +} diff --git a/src/main/java/com/greenorange/promotion/utils/paybank/GetPublicKeyBuilder.java b/src/main/java/com/greenorange/promotion/utils/paybank/GetPublicKeyBuilder.java new file mode 100644 index 0000000..b49601b --- /dev/null +++ b/src/main/java/com/greenorange/promotion/utils/paybank/GetPublicKeyBuilder.java @@ -0,0 +1,139 @@ +package com.greenorange.promotion.utils.paybank; + +import java.util.Map; + +import net.sf.json.JSONObject; + +/** + * v1.0.2 + * + * @author 周工 2020-06-01 + */ +public class GetPublicKeyBuilder implements WxPayDataBuilder { + + private static String sendUrl = "https://fraud.mch.weixin.qq.com/risk/getpublickey"; + + private String certPath, API_KEY, mch_id, nonce_str, sign; + + private StringBuffer reStringBuffer; + private StringBuffer signStringBuffer; + private boolean build = false; + + public GetPublicKeyBuilder(String certPath) { + nonce_str = WxPayUtil.getRandomStr(20); + this.certPath = certPath; + } + + public static String getSendUrl() { + return sendUrl; + } + + public static void setSendUrl(String sendUrl) { + GetPublicKeyBuilder.sendUrl = sendUrl; + } + + public String getCertPath() { + return certPath; + } + + public void setCertPath(String certPath) { + this.certPath = certPath; + } + + public String getAPI_KEY() { + return API_KEY; + } + + public void setAPI_KEY(String aPI_KEY) { + API_KEY = aPI_KEY; + } + + public String getmch_id() { + return mch_id; + } + + public void setmch_id(String mch_id) { + this.mch_id = mch_id; + } + + public String getNonce_str() { + return nonce_str; + } + + public void setNonce_str(String nonce_str) { + this.nonce_str = nonce_str; + } + + @Override + public boolean build() throws LackParamExceptions { + build = false; + reStringBuffer = new StringBuffer(); + signStringBuffer = new StringBuffer(); + reStringBuffer.append(""); + appendParam("mch_id", mch_id, true); + appendParam("nonce_str", nonce_str, true); + getSign(API_KEY, this.signStringBuffer.toString()); + appendParam("sign", sign, true); + reStringBuffer.append(""); + build = true; + return true; + } + + @SuppressWarnings("null") + @Override + public JSONObject hand() throws LackParamExceptions { + if (!build) { + throw new LackParamExceptions("未build成功,请先确认build成功后再运行"); + } + + if (certPath == null || certPath.length() == 0) { + throw new LackParamExceptions("未设置证书路径"); + } + + String result = ""; + JSONObject resultJson = new JSONObject(); + + try { + result = WxPayUtil.httpClientResultGetPublicKey(sendUrl, reStringBuffer.toString(), mch_id, certPath); + Map getResult = WxPayUtil.xmlToMap(result); + if (getResult.get("result_code") == null || !"SUCCESS".equalsIgnoreCase(getResult.get("result_code"))) { + resultJson.put("return_code", "FAIL"); + resultJson.put("return_msg", getResult.get("return_msg")); + resultJson.put("err_code_des", getResult.get("err_code_des")); + resultJson.put("err_code", getResult.get("err_code")); + } else { + resultJson.put("return_code", "SUCCESS"); + resultJson.put("return_msg", getResult.get("return_msg")); + resultJson.put("mch_id", getResult.get("mch_id")); + resultJson.put("pub_key", getResult.get("pub_key")); + } + } catch (Exception e) { + e.printStackTrace(); + } + + return resultJson; + } + + private void getSign(String apikey, String value) throws LackParamExceptions { + if (apikey == null) { + throw new LackParamExceptions("商户号API秘钥不能为空"); + } else { + sign = WxPayUtil.MD5(signStringBuffer.toString() + "key=" + apikey); + } + } + + private void appendParam(String key, String value, boolean isneed) throws LackParamExceptions { + + if (value == null) { + if (isneed) { + throw new LackParamExceptions("参数" + key + "不能为空"); + } else { + return; + } + } + this.reStringBuffer.append("<" + key + ">"); + this.reStringBuffer.append(""); + this.reStringBuffer.append(""); + this.signStringBuffer.append(key + "=" + value + "&"); + } +} diff --git a/src/main/java/com/greenorange/promotion/utils/paybank/JSAPIWxPayBuilder.java b/src/main/java/com/greenorange/promotion/utils/paybank/JSAPIWxPayBuilder.java new file mode 100644 index 0000000..6923685 --- /dev/null +++ b/src/main/java/com/greenorange/promotion/utils/paybank/JSAPIWxPayBuilder.java @@ -0,0 +1,331 @@ +package com.greenorange.promotion.utils.paybank; + +import java.io.IOException; +import java.util.Map; + +import net.sf.json.JSONObject; + +/** + * v1.0.2 + * + * @author 周工 2020-06-01 + */ +public class JSAPIWxPayBuilder implements WxPayDataBuilder { + + private static String sendUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder"; + private static String sendUrl2 = "https://api2.mch.weixin.qq.com/pay/unifiedorder"; + + private String appid, mch_id, body, out_trade_no, device_info, sign, detail, attach, time_start, time_expire, + goods_tag, product_id, spbill_create_ip, notify_url, limit_pay, openid, receipt, scene_info, API_KEY, + nonce_str; + + private int total_fee; + private StringBuffer reStringBuffer; + private StringBuffer signStringBuffer; + private boolean build = false; + + public JSAPIWxPayBuilder() { + nonce_str = WxPayUtil.getRandomStr(20); + // 默认订单时间为半小时内有效 + time_start = WxPayUtil.FormatDate(System.currentTimeMillis()); + time_expire = WxPayUtil.FormatDate(System.currentTimeMillis() + 30 * 60 * 1000); + + out_trade_no = System.currentTimeMillis() + WxPayUtil.getRandomStr(8); + } + + public static String getSendUrl() { + return sendUrl; + } + + public static void setSendUrl(String sendUrl) { + JSAPIWxPayBuilder.sendUrl = sendUrl; + } + + public static String getSendUrl2() { + return sendUrl2; + } + + public static void setSendUrl2(String sendUrl2) { + JSAPIWxPayBuilder.sendUrl2 = sendUrl2; + } + + public String getAPI_KEY() { + return API_KEY; + } + + public void setAPI_KEY(String aPI_KEY) { + API_KEY = aPI_KEY; + } + + public String getAppid() { + return appid; + } + + public void setAppid(String appid) { + this.appid = appid; + } + + public String getMch_id() { + return mch_id; + } + + public void setMch_id(String mch_id) { + this.mch_id = mch_id; + } + + public String getBody() { + return body; + } + + public void setBody(String body) { + this.body = body; + } + + public String getOut_trade_no() { + return out_trade_no; + } + + public void setOut_trade_no(String out_trade_no) { + this.out_trade_no = out_trade_no; + } + + public String getDevice_info() { + return device_info; + } + + public void setDevice_info(String device_info) { + this.device_info = device_info; + } + + public String getDetail() { + return detail; + } + + public void setDetail(String detail) { + this.detail = detail; + } + + public String getAttach() { + return attach; + } + + public void setAttach(String attach) { + this.attach = attach; + } + + public String getTime_start() { + return time_start; + } + + public void setTime_start(String time_start) { + this.time_start = time_start; + } + + public String getTime_expire() { + return time_expire; + } + + public void setTime_expire(String time_expire) { + this.time_expire = time_expire; + } + + public String getGoods_tag() { + return goods_tag; + } + + public void setGoods_tag(String goods_tag) { + this.goods_tag = goods_tag; + } + + public String getProduct_id() { + return product_id; + } + + public void setProduct_id(String product_id) { + this.product_id = product_id; + } + + public String getSpbill_create_ip() { + return spbill_create_ip; + } + + public void setSpbill_create_ip(String spbill_create_ip) { + this.spbill_create_ip = spbill_create_ip; + } + + public String getNotify_url() { + return notify_url; + } + + public void setNotify_url(String notify_url) { + this.notify_url = notify_url; + } + + public String getLimit_pay() { + return limit_pay; + } + + public void setLimit_pay(String limit_pay) { + this.limit_pay = limit_pay; + } + + public String getOpenid() { + return openid; + } + + public void setOpenid(String openid) { + this.openid = openid; + } + + public String getReceipt() { + return receipt; + } + + public void setReceipt(String receipt) { + this.receipt = receipt; + } + + public String getScene_info() { + return scene_info; + } + + public void setScene_info(String scene_info) { + this.scene_info = scene_info; + } + + public int getTotal_fee() { + return total_fee; + } + + public void setTotal_fee(int total_fee) { + this.total_fee = total_fee; + } + + @Override + public boolean build() throws LackParamExceptions { + build = false; + reStringBuffer = new StringBuffer(); + signStringBuffer = new StringBuffer(); + + reStringBuffer.append(""); + appendParam("appid", appid, true); + appendParam("attach", attach, false); + appendParam("body", body, true); + appendParam("detail", detail, false); + appendParam("device_info", device_info, false); + appendParam("goods_tag", goods_tag, false); + appendParam("limit_pay", limit_pay, false); + appendParam("mch_id", mch_id, true); + appendParam("nonce_str", nonce_str, true); + appendParam("notify_url", notify_url, true); + appendParam("openid", openid, true); + appendParam("out_trade_no", out_trade_no, true); + appendParam("product_id", product_id, false); + appendParam("receipt", receipt, false); + appendParam("scene_info", scene_info, false); + appendParam("sign_type", "MD5", false); + appendParam("spbill_create_ip", spbill_create_ip, true); + appendParam("time_expire", time_expire, false); + appendParam("time_start", time_start, false); + appendParam("total_fee", total_fee + "", true); + appendParam("trade_type", "JSAPI", true); + + getSign(API_KEY, this.signStringBuffer.toString()); + appendParam("sign", sign, true); + reStringBuffer.append(""); + + build = true; + + return build; + } + + private void getSign(String apikey, String value) throws LackParamExceptions { + if (apikey == null) { + throw new LackParamExceptions("商户号操作秘钥不能为空"); + } else { + + sign = WxPayUtil.MD5(signStringBuffer.toString() + "key=" + API_KEY); + } + } + + private void appendParam(String key, String value, boolean isneed) throws LackParamExceptions { + + if (value == null) { + if (isneed) { + throw new LackParamExceptions("参数" + key + "不能为空"); + } else { + return; + } + } + this.reStringBuffer.append("<" + key + ">"); + this.reStringBuffer.append(""); + this.reStringBuffer.append(""); + this.signStringBuffer.append(key + "=" + value + "&"); + } + + private void signAppendParam(String key, String value, boolean isneed) throws LackParamExceptions { + if (value == null) { + if (isneed) { + throw new LackParamExceptions("参数" + key + "不能为空"); + } else { + return; + } + } + this.signStringBuffer.append(key + "=" + value + "&"); + } + + @Override + public JSONObject hand() throws LackParamExceptions { + if (!build) { + throw new LackParamExceptions("未build成功,请先确认build成功后再运行"); + } + String result = ""; + try { + // 正常是的域名 + result = WxPayUtil.sendHttpsRequest(sendUrl, reStringBuffer.toString(), "text/xml", "utf-8", "POST"); + } catch (IOException e) { + e.printStackTrace(); + // 备用域名 + try { + result = WxPayUtil.sendHttpsRequest(sendUrl2, reStringBuffer.toString(), "text/xml", "utf-8", "POST"); + } catch (IOException e1) { + e1.printStackTrace(); + throw new LackParamExceptions(e1.toString()); + } + } + + Map getResult = WxPayUtil.xmlToMap(result); + + JSONObject resultJson = new JSONObject(); + + if (getResult.get("result_code") == null || !"SUCCESS".equalsIgnoreCase(getResult.get("result_code"))) { + resultJson.put("return_code", "FAIL"); + resultJson.put("return_msg", getResult.get("return_msg")); + resultJson.put("err_code", getResult.get("err_code")); + resultJson.put("err_code_des", getResult.get("err_code_des")); + } else { + long curTime = System.currentTimeMillis() / 1000; + // 再次签名 + signStringBuffer = new StringBuffer(); + signAppendParam("appId", appid, true); + signAppendParam("nonceStr", getResult.get("nonce_str"), true); + signAppendParam("package", "prepay_id=" + getResult.get("prepay_id"), true); + signAppendParam("signType", "MD5", true); + signAppendParam("timeStamp", curTime + "", true); + + String sign = WxPayUtil.MD5(signStringBuffer.toString() + "key=" + API_KEY); + + resultJson.put("return_code", "SUCCESS"); + resultJson.put("return_msg", getResult.get("return_msg")); + resultJson.put("err_code", getResult.get("err_code")); + resultJson.put("err_code_des", getResult.get("err_code_des")); + resultJson.put("nonceStr", getResult.get("nonce_str")); + resultJson.put("timeStamp", curTime); + resultJson.put("paySign", sign); + resultJson.put("package", "prepay_id=" + getResult.get("prepay_id")); + } + + return resultJson; + } + +} diff --git a/src/main/java/com/greenorange/promotion/utils/paybank/LackParamExceptions.java b/src/main/java/com/greenorange/promotion/utils/paybank/LackParamExceptions.java new file mode 100644 index 0000000..d610f50 --- /dev/null +++ b/src/main/java/com/greenorange/promotion/utils/paybank/LackParamExceptions.java @@ -0,0 +1,23 @@ +package com.greenorange.promotion.utils.paybank; + +/** + * v1.0.2 + * + * @author 周工 2020-06-01 + */ +public class LackParamExceptions extends Exception { + + /** + * + */ + private static final long serialVersionUID = 3751503410029217326L; + + public LackParamExceptions() { + super(); + } + + public LackParamExceptions(String agr0) { + super(agr0); + } + +} diff --git a/src/main/java/com/greenorange/promotion/utils/paybank/MxObjectLockUtil.java b/src/main/java/com/greenorange/promotion/utils/paybank/MxObjectLockUtil.java new file mode 100644 index 0000000..4b03946 --- /dev/null +++ b/src/main/java/com/greenorange/promotion/utils/paybank/MxObjectLockUtil.java @@ -0,0 +1,56 @@ +package com.greenorange.promotion.utils.paybank; + +import java.util.HashMap; + +/** + * v1.0.2 + * + * @author 周工 2020-06-01 + */ +public class MxObjectLockUtil { + private HashMap planLockHashMap = new HashMap<>(40); + private volatile static MxObjectLockUtil instance; + + private MxObjectLockUtil() throws IllegalAccessException { + if (instance != null) { + throw new IllegalAccessException("该类为单例模式,不可生成额外的对象"); + } + } + + public HashMap getPlanLockHashMap() { + return planLockHashMap; + } + + public synchronized PlanLock getObjectLock(String key) { + if (planLockHashMap.get(key) == null) { + PlanLock planLock = new PlanLock(); + planLockHashMap.put(key, planLock); + return planLock; + } + return planLockHashMap.get(key); + } + + public PlanLock removeLock(String key) { + PlanLock planLock = null; + if (planLockHashMap.get(key) != null) { + planLock = planLockHashMap.remove(key); + } + return planLock; + } + + public static MxObjectLockUtil getInstance() { + if (instance == null) { + synchronized (MxObjectLockUtil.class) { + if (instance == null) { + try { + instance = new MxObjectLockUtil(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + } + } + return instance; + } + +} diff --git a/src/main/java/com/greenorange/promotion/utils/paybank/PlanLock.java b/src/main/java/com/greenorange/promotion/utils/paybank/PlanLock.java new file mode 100644 index 0000000..faf1738 --- /dev/null +++ b/src/main/java/com/greenorange/promotion/utils/paybank/PlanLock.java @@ -0,0 +1,12 @@ +package com.greenorange.promotion.utils.paybank; + +/** + * v1.0.2 + * + * @author 周工 2020-06-01 + */ +public class PlanLock { + public synchronized void execute(Runnable runnable) { + runnable.run(); + } +} diff --git a/src/main/java/com/greenorange/promotion/utils/paybank/RSAEncryp.java b/src/main/java/com/greenorange/promotion/utils/paybank/RSAEncryp.java new file mode 100644 index 0000000..498cba0 --- /dev/null +++ b/src/main/java/com/greenorange/promotion/utils/paybank/RSAEncryp.java @@ -0,0 +1,244 @@ +package com.greenorange.promotion.utils.paybank; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.lang.reflect.Method; +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; + +import javax.crypto.Cipher; + +/** + * v1.0.2 + * + * @author 周工 2020-06-01 + */ +public class RSAEncryp { + + public static byte[] decrypt(byte[] encryptedBytes, PrivateKey privateKey, int keyLength, int reserveSize, + String cipherAlgorithm) throws Exception { + int keyByteSize = keyLength / 8; + int decryptBlockSize = keyByteSize - reserveSize; + int nBlock = encryptedBytes.length / keyByteSize; + ByteArrayOutputStream outbuf = null; + try { + Cipher cipher = Cipher.getInstance(cipherAlgorithm); + cipher.init(Cipher.DECRYPT_MODE, privateKey); + + outbuf = new ByteArrayOutputStream(nBlock * decryptBlockSize); + for (int offset = 0; offset < encryptedBytes.length; offset += keyByteSize) { + int inputLen = encryptedBytes.length - offset; + if (inputLen > keyByteSize) { + inputLen = keyByteSize; + } + byte[] decryptedBlock = cipher.doFinal(encryptedBytes, offset, inputLen); + outbuf.write(decryptedBlock); + } + outbuf.flush(); + return outbuf.toByteArray(); + } catch (Exception e) { + throw new Exception("DEENCRYPT ERROR:", e); + } finally { + try { + if (outbuf != null) { + outbuf.close(); + } + } catch (Exception e) { + outbuf = null; + throw new Exception("CLOSE ByteArrayOutputStream ERROR:", e); + } + } + } + + public static String encrypt(byte[] plainBytes, String pub_Key, int reserveSize, String cipherAlgorithm) + throws Exception { + + PublicKey publicKey = getPublicKey(pub_Key, "RSA"); + byte[] estr = encrypt(plainBytes, publicKey, pub_Key.length(), reserveSize, cipherAlgorithm); + return new String(Base64.getEncoder().encode(estr)); + } + + public static byte[] encrypt(byte[] plainBytes, PublicKey publicKey, int keyLength, int reserveSize, + String cipherAlgorithm) throws Exception { + int keyByteSize = keyLength / 8; + int encryptBlockSize = keyByteSize - reserveSize; + int nBlock = plainBytes.length / encryptBlockSize; + if ((plainBytes.length % encryptBlockSize) != 0) { + nBlock += 1; + } + ByteArrayOutputStream outbuf = null; + try { + Cipher cipher = Cipher.getInstance(cipherAlgorithm); + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + + outbuf = new ByteArrayOutputStream(nBlock * keyByteSize); + for (int offset = 0; offset < plainBytes.length; offset += encryptBlockSize) { + int inputLen = plainBytes.length - offset; + if (inputLen > encryptBlockSize) { + inputLen = encryptBlockSize; + } + byte[] encryptedBlock = cipher.doFinal(plainBytes, offset, inputLen); + outbuf.write(encryptedBlock); + } + outbuf.flush(); + return outbuf.toByteArray(); + } catch (Exception e) { + throw new Exception("ENCRYPT ERROR:", e); + } finally { + try { + if (outbuf != null) { + outbuf.close(); + } + } catch (Exception e) { + outbuf = null; + throw new Exception("CLOSE ByteArrayOutputStream ERROR:", e); + } + } + } + + public static PrivateKey getPriKey(String privateKeyPath, String keyAlgorithm) { + PrivateKey privateKey = null; + InputStream inputStream = null; + try { + if (inputStream == null) { + } + inputStream = new FileInputStream(privateKeyPath); + privateKey = getPrivateKey(inputStream, keyAlgorithm); + } catch (Exception e) { + System.out.println("加载私钥出错!"); + } finally { + if (inputStream != null) { + try { + inputStream.close(); + } catch (Exception e) { + System.out.println("加载私钥,关闭流时出错!"); + } + } + } + return privateKey; + } + + public static PublicKey getPubKey(String publicKeyPath, String keyAlgorithm) { + PublicKey publicKey = null; + InputStream inputStream = null; + try { + inputStream = new FileInputStream(publicKeyPath); + publicKey = getPublicKey(inputStream, keyAlgorithm); + } catch (Exception e) { + System.out.println("加载公钥出错!"); + } finally { + if (inputStream != null) { + try { + inputStream.close(); + } catch (Exception e) { + System.out.println("加载公钥,关闭流时出错!"); + } + } + } + return publicKey; + } + + public static PublicKey getPublicKey(String key, String keyAlgorithm) throws Exception { + try { + org.bouncycastle.asn1.pkcs.RSAPublicKey rsaPublicKey = org.bouncycastle.asn1.pkcs.RSAPublicKey + .getInstance(org.bouncycastle.util.encoders.Base64.decode(key)); + java.security.spec.RSAPublicKeySpec publicKeySpec = new java.security.spec.RSAPublicKeySpec( + rsaPublicKey.getModulus(), rsaPublicKey.getPublicExponent()); + KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm); + PublicKey publicKey = keyFactory.generatePublic(publicKeySpec); + + return publicKey; + } catch (Exception e) { + throw new Exception("READ PUBLIC KEY ERROR:", e); + } finally { + + } + } + + public static PublicKey getPublicKey(InputStream inputStream, String keyAlgorithm) throws Exception { + try { + BufferedReader br = new BufferedReader(new InputStreamReader(inputStream)); + StringBuilder sb = new StringBuilder(); + String readLine = null; + while ((readLine = br.readLine()) != null) { + if (readLine.charAt(0) == '-') { + continue; + } else { + sb.append(readLine); + sb.append('\r'); + } + } + X509EncodedKeySpec pubX509 = new X509EncodedKeySpec(decodeBase64(sb.toString())); + KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm); + PublicKey publicKey = keyFactory.generatePublic(pubX509); + + return publicKey; + } catch (Exception e) { + throw new Exception("READ PUBLIC KEY ERROR:", e); + } finally { + try { + if (inputStream != null) { + inputStream.close(); + } + } catch (IOException e) { + inputStream = null; + throw new Exception("INPUT STREAM CLOSE ERROR:", e); + } + } + } + + public static PrivateKey getPrivateKey(InputStream inputStream, String keyAlgorithm) throws Exception { + try { + BufferedReader br = new BufferedReader(new InputStreamReader(inputStream)); + StringBuilder sb = new StringBuilder(); + String readLine = null; + while ((readLine = br.readLine()) != null) { + if (readLine.charAt(0) == '-') { + continue; + } else { + sb.append(readLine); + sb.append('\r'); + } + } + PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(decodeBase64(sb.toString())); + KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm); + PrivateKey privateKey = keyFactory.generatePrivate(priPKCS8); + return privateKey; + } catch (Exception e) { + throw new Exception("READ PRIVATE KEY ERROR:", e); + } finally { + try { + if (inputStream != null) { + inputStream.close(); + } + } catch (IOException e) { + inputStream = null; + throw new Exception("INPUT STREAM CLOSE ERROR:", e); + } + } + } + + public static String encodeBase64(byte[] input) throws Exception { + Class clazz = Class.forName("com.sun.org.apache.xerces.internal.impl.dv.util.Base64"); + Method mainMethod = clazz.getMethod("encode", byte[].class); + mainMethod.setAccessible(true); + Object retObj = mainMethod.invoke(null, new Object[] { input }); + return (String) retObj; + } + + public static byte[] decodeBase64(String input) throws Exception { + Class clazz = Class.forName("com.sun.org.apache.xerces.internal.impl.dv.util.Base64"); + Method mainMethod = clazz.getMethod("decode", String.class); + mainMethod.setAccessible(true); + Object retObj = mainMethod.invoke(null, input); + return (byte[]) retObj; + } +} \ No newline at end of file diff --git a/src/main/java/com/greenorange/promotion/utils/paybank/SendTempleMsgBuilder.java b/src/main/java/com/greenorange/promotion/utils/paybank/SendTempleMsgBuilder.java new file mode 100644 index 0000000..051f3ba --- /dev/null +++ b/src/main/java/com/greenorange/promotion/utils/paybank/SendTempleMsgBuilder.java @@ -0,0 +1,175 @@ +package com.greenorange.promotion.utils.paybank; + +import java.io.IOException; + +import net.sf.json.JSONObject; + +/** + * v1.0.2 + * + * @author 周工 2020-06-01 + */ +public class SendTempleMsgBuilder implements WxPayDataBuilder { + + private String appid, secret; + private static String sendUrl = "https://api.weixin.qq.com/cgi-bin/token"; + private static String sendUrl2 = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token="; + private String access_token; + private JSONObject sendjson; // 最后发送的数据 + private JSONObject data; // 自定义数据 + private boolean build = false; + private StringBuffer reStringBuffer; + + /** + * + * @param toUserOpenid + * 接收人的openid + * @param template_id + * 发送的模板ID + */ + public SendTempleMsgBuilder(String toUserOpenid, String template_id) { + // TODO Auto-generated constructor stub + sendjson = new JSONObject(); + data = new JSONObject(); + sendjson.put("touser", toUserOpenid); + sendjson.put("template_id", template_id); + } + + public String getAppid() { + return appid; + } + + public void setAppid(String appid) { + this.appid = appid; + } + + public String getSecret() { + return secret; + } + + public void setSecret(String secret) { + this.secret = secret; + } + + public static String getSendUrl() { + return sendUrl; + } + + public static void setSendUrl(String sendUrl) { + SendTempleMsgBuilder.sendUrl = sendUrl; + } + + public String getAccess_token() { + return access_token; + } + + public void setAccess_token(String access_token) { + this.access_token = access_token; + } + + public static String getSendUrl2() { + return sendUrl2; + } + + public static void setSendUrl2(String sendUrl2) { + SendTempleMsgBuilder.sendUrl2 = sendUrl2; + } + + /** + * 用户点击模板消息后跳转小程序 + * + * @param appid + * 跳转小程序的APPID + * @param pagepath + * 跳转的页面 + */ + public void toMiniprogram(String appid, String pagepath) { + JSONObject minidata = new JSONObject(); + minidata.put("appid", appid); + minidata.put("pagepath", pagepath); + sendjson.put("miniprogram", minidata); + } + + /** + * 用户点击模板消息后跳转网页 + * + * @param url + * 网页地址 + */ + public void toUrl(String url) { + sendjson.put("url", url); + } + + /** + * 添加自定义参数 + * + * @param name + * 自定义参数名称 + * @param color + * 十六进制颜色值 + * @param value + * 具体的数据 + */ + public void addCostomData(String name, String color, String value) { + JSONObject infodata = new JSONObject(); + infodata.put("value", value); + infodata.put("color", color); + data.put(name, infodata); + } + + @Override + public boolean build() throws LackParamExceptions { + build = false; + appendParam("appid", appid, true); + appendParam("secret", secret, true); + appendParam("grant_type", "client_credential", true); + reStringBuffer.deleteCharAt(reStringBuffer.length() - 1); + build = true; + return build; + } + + private void appendParam(String key, String value, boolean isneed) throws LackParamExceptions { + if (value == null) { + if (isneed) { + throw new LackParamExceptions("参数" + key + "不能为空"); + } else { + return; + } + } + this.reStringBuffer.append(key + "=" + value + "&"); + } + + @Override + public JSONObject hand() throws LackParamExceptions { + if (!build) { + throw new LackParamExceptions("未build成功,请先确认build成功后再运行"); + } + String result = ""; + JSONObject jsonObject = null; + try { + if (access_token == null) { + // 获取access_token + result = WxPayUtil.sendHttpsRequest(sendUrl, reStringBuffer.toString(), "text/xml", "utf-8", "GET"); + jsonObject = JSONObject.fromObject(result); + if (jsonObject.getString("access_token") == null) { + jsonObject.put("return_code", "FAIL"); + jsonObject.put("return_msg", "获取token失败"); + return jsonObject; + } else { + access_token = jsonObject.getString("access_token"); + } + } + result = WxPayUtil.sendHttpsRequest(sendUrl2 + access_token, sendjson.toString(), "text/xml", "utf-8", + "POST"); + jsonObject = JSONObject.fromObject(result); + + } catch (IOException e) { + e.printStackTrace(); + // 备用域名 + throw new LackParamExceptions(e.toString()); + } + + return jsonObject; + } + +} diff --git a/src/main/java/com/greenorange/promotion/utils/paybank/Test.java b/src/main/java/com/greenorange/promotion/utils/paybank/Test.java new file mode 100644 index 0000000..c09d0a7 --- /dev/null +++ b/src/main/java/com/greenorange/promotion/utils/paybank/Test.java @@ -0,0 +1,253 @@ +package com.greenorange.promotion.utils.paybank; + +import net.sf.json.JSONObject; + +public class Test { + + public static void main(String[] args) { + // 微信预下单 调用示例 + // JSAPIPay(); + // 获取openid 调用示例 + // getOpenid(); + // 企业付款 调用示例 + // compayWxPay(); + // 发送模板消息示例 + // sendTempleMsg(); + // 获取公钥示例 + // getWxPayPublicKey(); + // 付款到银行卡示例 + compayWxPayBank(); + // 付款到银行卡查询示例 + // compayWxPayBankQuery(); + // 微信退款示例 + // WxPayRefund(); + // 微信退款查询示例 + // WxPayRefundQuery(); + // 易支付示例 + // EasyPayExample(); + } + + /** + * 易支付查询示例 + */ + public static void EasyPayExample() { + try { + EasyPayBuilder easyPayBuilder = new EasyPayBuilder(); + + easyPayBuilder.setGoodsname("测试商品"); + easyPayBuilder.setUid(15667); + easyPayBuilder.setIstype(1); + easyPayBuilder.setToken("5556f669bd447c9cc6cd410d0c108aa6"); + easyPayBuilder.setNotify_url(""); + easyPayBuilder.setReturn_url("http://www.baidu.com"); + easyPayBuilder.setPrice(0.5f); + + easyPayBuilder.build();// 验证数据 + System.out.println(easyPayBuilder.hand());// 发送处理 + } catch (LackParamExceptions e) { + e.printStackTrace(); + } + } + + /** + * 微信退款查询示例 + */ + public static void WxPayRefundQuery() { + try { + WxPayRefundQueryBuilder wxPayRefundBuilder = new WxPayRefundQueryBuilder(); + wxPayRefundBuilder.setAppid("小程序或公众号APPID"); + wxPayRefundBuilder.setMch_id("商户号"); + wxPayRefundBuilder.setAPI_KEY("商户号APIKEY"); + // 下面四个任选一个 + wxPayRefundBuilder.setOut_trade_no("商家交易订单号"); + wxPayRefundBuilder.setOut_refund_no("退款商家订单号"); + wxPayRefundBuilder.setTransaction_id("微信交易订单号"); + wxPayRefundBuilder.setRefund_id("退款微信订单号"); + + wxPayRefundBuilder.build();// 验证数据 + System.out.println(wxPayRefundBuilder.hand());// 发送处理 + } catch (LackParamExceptions e) { + e.printStackTrace(); + } + } + + /** + * 微信退款示例 + */ + public static void WxPayRefund() { + try { + WxPayRefundBuilder wxPayRefundBuilder = new WxPayRefundBuilder("证书路径"); + wxPayRefundBuilder.setAppid("小程序或公众号APPID"); + wxPayRefundBuilder.setMch_id("商户号"); + wxPayRefundBuilder.setAPI_KEY("商户号APIKEY"); + wxPayRefundBuilder.setTotal_fee(101);// 该订单总金额 + wxPayRefundBuilder.setRefund_fee(101); // 退款金额 + wxPayRefundBuilder.setRefund_desc("测试"); // 退款描述 + // 任选一个 + wxPayRefundBuilder.setOut_trade_no("商家交易订单号"); + wxPayRefundBuilder.setTransaction_id("微信交易订单号"); + + wxPayRefundBuilder.build();// 验证数据 + System.out.println(wxPayRefundBuilder.hand());// 发送处理 + + } catch (LackParamExceptions e) { + e.printStackTrace(); + } + } + + /** + * 企业付款到银行卡查询示例 + */ + public static void compayWxPayBankQuery() { + + try { + + CompanyWxPayBankQueryBuilder wxPayBankQueryBuilder = new CompanyWxPayBankQueryBuilder("支付证书路径", + "交易订单号(商家,不是微信的)"); + + wxPayBankQueryBuilder.setMch_id("商户号"); + wxPayBankQueryBuilder.setAPI_KEY("APIKEY"); + + wxPayBankQueryBuilder.build();// 验证数据 + System.out.println(wxPayBankQueryBuilder.hand());// 发送处理 + } catch (LackParamExceptions e) { + e.printStackTrace(); + } + } + + /** + * 企业付款到银行卡示例 + */ + public static void compayWxPayBank() { + + try { + // 获取公钥 + String key = getWxPayPublicKey(); + + CompanyWxPayBankBuilder wxPayBankBuilder = new CompanyWxPayBankBuilder("src/main/resources/static/apiclient_cert.p12", key); // key 为微信返回的公钥 + + wxPayBankBuilder.setMch_id("1700326544"); + wxPayBankBuilder.setAPI_KEY("cvsOH6TgbbdNUUqFJyLmWGaIEKoSqANg"); + wxPayBankBuilder.setAmount(2); // 支付金额 + wxPayBankBuilder.setDesc("提现"); + wxPayBankBuilder.setEnc_bank_no("6222031207006363442"); + wxPayBankBuilder.setEnc_true_name("陈新知"); + wxPayBankBuilder.setBank_code("1002"); + + wxPayBankBuilder.build();// 验证数据 + System.out.println(wxPayBankBuilder.hand());// 发送处理 + } catch (LackParamExceptions e) { + e.printStackTrace(); + } + } + + /** + * 获取公钥 + */ + public static String getWxPayPublicKey() { + + try { + + GetPublicKeyBuilder builder = new GetPublicKeyBuilder("src/main/resources/static/apiclient_cert.p12"); + builder.setAPI_KEY("cvsOH6TgbbdNUUqFJyLmWGaIEKoSqANg"); + builder.setmch_id("1700326544"); + + builder.build();// 验证数据 + JSONObject result = builder.hand(); + System.out.println(result);// 发送处理 + return result.getString("pub_key"); + } catch (LackParamExceptions e) { + e.printStackTrace(); + } + return null; + } + + /** + * 微信预下单 调用示例 + */ + public static void JSAPIPay() { + try { + JSAPIWxPayBuilder jsapiWxPayBuilder = new JSAPIWxPayBuilder(); + jsapiWxPayBuilder.setAppid("小程序APPID"); + jsapiWxPayBuilder.setAttach("携带参数"); + jsapiWxPayBuilder.setMch_id("商户号"); + jsapiWxPayBuilder.setAPI_KEY("商户号API秘钥"); + jsapiWxPayBuilder.setBody("商品内容"); + jsapiWxPayBuilder.setTotal_fee(50); // 交易金额 + jsapiWxPayBuilder.setNotify_url("回调地址"); // + jsapiWxPayBuilder.setSpbill_create_ip("发起请求的IP"); // + jsapiWxPayBuilder.setOpenid("OPENID"); + + jsapiWxPayBuilder.build();// 验证数据 + System.out.println(jsapiWxPayBuilder.hand());// 发送处理 + } catch (LackParamExceptions e) { + e.printStackTrace(); + } + } + + /** + * 获取openid 调用示例 + */ + public static void getOpenid() { + try { + GetOpenidBuilder getOpenidBuilder = new GetOpenidBuilder(); + getOpenidBuilder.setAppid("您的小程序Openid"); + getOpenidBuilder.setAppsecret("您的小程序秘钥"); + getOpenidBuilder.setCode("小程序登陆时获取的code"); + getOpenidBuilder.build();// 验证数据 + System.out.println(getOpenidBuilder.hand());// 发送处理 + } catch (LackParamExceptions e) { + e.printStackTrace(); + } + } + + /** + * 企业付款 调用示例 + */ + public static void compayWxPay() { + try { + + CompanyWxPayBuilder compayBuilder = new CompanyWxPayBuilder( + "C:\\Users\\Administrator\\Desktop\\远程\\apiclient_cert.p12"); + compayBuilder.setMch_appid("被付款人使用的小程序appid或公众号appid"); + compayBuilder.setAPI_KEY("您的商户号API操作秘钥"); + compayBuilder.setDesc("付款备注"); + compayBuilder.setMchid("您的商户号"); + compayBuilder.setOpenid("小程序或公众号对应的openid"); + compayBuilder.setSpbill_create_ip("本机ip,不能是 localhost或127.0.0.1"); + compayBuilder.setAmount(200); // 支付金额 + + // compayBuilder.setMch_appid("wxafa0aa182e6570ca"); + // compayBuilder.setAPI_KEY("MvK6aVg00EUxzOyWDOHUWvMTk2PQtdZi"); + // compayBuilder.setDesc("提现"); + // compayBuilder.setMchid("1343097401"); + // compayBuilder.setOpenid("o90eTt95OlE-u8mblsklZNfxpPuw"); + // compayBuilder.setSpbill_create_ip("182.92.119.83"); + // compayBuilder.setAmount(100); // 支付金额 + + compayBuilder.build(); // 验证数据 + System.out.println(compayBuilder.hand()); // 发送处理 + } catch (LackParamExceptions e) { + e.printStackTrace(); + } + } + + /** + * 发送模板消息示例 + */ + public static void sendTempleMsg() { + try { + SendTempleMsgBuilder sendTempleMsgBuilder = new SendTempleMsgBuilder("公众号APPID", "发送模板ID"); + sendTempleMsgBuilder.setAccess_token("access——token"); + sendTempleMsgBuilder.toMiniprogram("用户点击模板消息时跳转小程序的APPID", "跳转路径"); + sendTempleMsgBuilder.toUrl("用户点击模板消息时跳转的网址"); + sendTempleMsgBuilder.addCostomData("自定义数据名称", "十六进制颜色值", "具体数值"); // 添加自定义数据 + // 示例 + sendTempleMsgBuilder.addCostomData("first", "#ff0000", "您有新的故障通知"); + sendTempleMsgBuilder.build(); // 验证数据 + System.out.println(sendTempleMsgBuilder.hand()); // 发送处理 + } catch (LackParamExceptions e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/com/greenorange/promotion/utils/paybank/WeChatAPIV3.java b/src/main/java/com/greenorange/promotion/utils/paybank/WeChatAPIV3.java new file mode 100644 index 0000000..bd62aad --- /dev/null +++ b/src/main/java/com/greenorange/promotion/utils/paybank/WeChatAPIV3.java @@ -0,0 +1,1010 @@ +package com.greenorange.promotion.utils.paybank; + +import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder; +import com.wechat.pay.contrib.apache.httpclient.WechatPayUploadHttpPost; +import com.wechat.pay.contrib.apache.httpclient.auth.*; +import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager; +import com.wechat.pay.contrib.apache.httpclient.util.PemUtil; +import net.sf.json.JSONObject; +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.http.HttpEntity; +import org.apache.http.client.methods.*; +import org.apache.http.client.utils.URIBuilder; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.util.EntityUtils; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.*; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.*; +import java.util.Base64; + +public class WeChatAPIV3 { + + CloseableHttpClient httpClient; + + CloseableHttpClient httpNoSignPayClient; + + String mchId = ""; + String mchSerialNo = ""; + String apiV3Key = ""; + String privateKeyFilePath = ""; + + String privateKey = ""; + String certKeyFilePath = ""; + PrivateKey merchantPrivateKey; + PublicKey merchantPublicKey; + PublicKey pingtaiPublicKey; + String errorHint = ""; + + public String getErrorHint() { + return errorHint; + } + + public void setErrorHint(String errorHint) { + this.errorHint = errorHint; + } + + public boolean isStep() { + return isStep; + } + + long lastUseTime = 0; + + public long getLastUseTime() { + return lastUseTime; + } + + public void setLastUseTime(long lastUseTime) { + this.lastUseTime = lastUseTime; + } + + public String getCertKeyFilePath() { + return certKeyFilePath; + } + + public void setCertKeyFilePath(String certKeyFilePath) { + this.certKeyFilePath = certKeyFilePath; + } + + public String getMchId() { + return mchId; + } + + public void setMchId(String mchId) { + this.mchId = mchId; + } + + public String getMchSerialNo() { + return mchSerialNo; + } + + public void setMchSerialNo(String mchSerialNo) { + this.mchSerialNo = mchSerialNo; + } + + public String getApiV3Key() { + return apiV3Key; + } + + public void setApiV3Key(String apiV3Key) { + this.apiV3Key = apiV3Key; + } + + public String getPrivateKeyFilePath() { + return privateKeyFilePath; + } + + public void setPrivateKeyFilePath(String privateKeyFilePath) { + this.privateKeyFilePath = privateKeyFilePath; + } + + private boolean isStep = false; + + public WeChatAPIV3() { + + } + + public WeChatAPIV3(String mchId, String mchSerialNo, String apiV3Key, String privateKeyFilePath) { + this.mchId = mchId; + this.mchSerialNo = mchSerialNo; + this.apiV3Key = apiV3Key; + this.privateKeyFilePath = privateKeyFilePath; + + } + + public String rsaDecryptOAEP(String ciphertext) throws BadPaddingException, IOException { + + if (!isStep) { + errorHint = "未成功启用Step"; + return null; + } + + if (merchantPrivateKey == null) { + errorHint = "未成功加载私钥"; + return null; + } + + try { + + Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding"); + + cipher.init(Cipher.DECRYPT_MODE, merchantPrivateKey); + + byte[] data = Base64.getDecoder().decode(ciphertext); + return new String(cipher.doFinal(data), "utf-8"); + } catch (NoSuchPaddingException | NoSuchAlgorithmException e) { + throw new RuntimeException("当前Java环境不支持RSA v1.5/OAEP", e); + } catch (InvalidKeyException e) { + throw new IllegalArgumentException("无效的私钥", e); + } catch (BadPaddingException | IllegalBlockSizeException e) { + throw new BadPaddingException("解密失败"); + } + } + + public void loadPTCert(String certKey) throws UnsupportedEncodingException { + pingtaiPublicKey = PemUtil.loadCertificate(new ByteArrayInputStream(certKey.getBytes("utf-8"))).getPublicKey(); + } + + public String rsaEncryptOAEP_PT(String message) throws IllegalBlockSizeException, IOException { + + if (pingtaiPublicKey == null) { + errorHint = "未成功加载公钥"; + return null; + } + + try { + Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding"); + cipher.init(Cipher.ENCRYPT_MODE, pingtaiPublicKey); + + byte[] data = message.getBytes("utf-8"); + byte[] cipherdata = cipher.doFinal(data); + return Base64.getEncoder().encodeToString(cipherdata); + } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { + throw new RuntimeException("当前Java环境不支持RSA v1.5/OAEP", e); + } catch (InvalidKeyException e) { + throw new IllegalArgumentException("无效的证书", e); + } catch (IllegalBlockSizeException | BadPaddingException e) { + throw new IllegalBlockSizeException("加密原串的长度不能超过214字节"); + } + } + + public String signBySHA256WithRSA(String content) { + if (!isStep) { + errorHint = "未成功启用Step"; + return null; + } + + if (merchantPrivateKey == null) { + errorHint = "未成功加载私钥"; + return null; + } + + try { + // PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec( + // org.apache.commons.codec.binary.Base64.decodeBase64(privateKey)); + // PrivateKey priKey = KeyFactory.getInstance("RSA").generatePrivate(priPKCS8); + + Signature signature = Signature.getInstance("SHA256withRSA"); + signature.initSign(merchantPrivateKey); + signature.update(content.getBytes("utf-8")); + + return org.apache.commons.codec.binary.Base64.encodeBase64String(signature.sign()); + } catch (Exception e) { + // 签名失败 + e.printStackTrace(); + return null; + } + } + + public String rsaEncryptOAEP(String message) throws IllegalBlockSizeException, IOException { + + if (!isStep) { + errorHint = "未成功启用Step"; + return null; + } + + if (merchantPublicKey == null) { + errorHint = "未成功加载公钥"; + return null; + } + + try { + Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding"); + cipher.init(Cipher.ENCRYPT_MODE, merchantPublicKey); + + byte[] data = message.getBytes("utf-8"); + byte[] cipherdata = cipher.doFinal(data); + return Base64.getEncoder().encodeToString(cipherdata); + } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { + throw new RuntimeException("当前Java环境不支持RSA v1.5/OAEP", e); + } catch (InvalidKeyException e) { + throw new IllegalArgumentException("无效的证书", e); + } catch (IllegalBlockSizeException | BadPaddingException e) { + throw new IllegalBlockSizeException("加密原串的长度不能超过214字节"); + } + } + + public boolean doCheckParam() { + + return doCheckValue(mchId, mchSerialNo, apiV3Key, privateKeyFilePath, certKeyFilePath); + } + + public boolean doCheckValue(String... item) { + for (String each : item) { + if (each == null || each.length() == 0) { + errorHint = "缺少必要参数"; + return false; + } + } + return true; + } + + public void setup() { + + if (!doCheckParam()) { + isStep = false; + return; + } + + try { + + extracted(); +// extractedNew(); + + isStep = true; + + } catch (Exception e) { + errorHint = errorHint.toString(); + isStep = false; + e.printStackTrace(); + } + + } + + private void extractedNew() throws Exception { + + String certKey = new String(Files.readAllBytes(Paths.get(certKeyFilePath)), "utf-8"); + + merchantPublicKey = PemUtil.loadCertificate(new ByteArrayInputStream(certKey.getBytes("utf-8"))) + .getPublicKey(); + + privateKey = new String(Files.readAllBytes(Paths.get(privateKeyFilePath)), "utf-8"); + + merchantPrivateKey = PemUtil.loadPrivateKey(new ByteArrayInputStream(privateKey.getBytes("utf-8"))); + + + CertificatesManager certificatesManager = CertificatesManager.getInstance(); + // 向证书管理器增加需要自动更新平台证书的商户信息 + certificatesManager.putMerchant(mchId, new WechatPay2Credentials(mchId, + new PrivateKeySigner(mchSerialNo, merchantPrivateKey)), apiV3Key.getBytes(StandardCharsets.UTF_8)); + // ... 若有多个商户号,可继续调用putMerchant添加商户信息 + + // 从证书管理器中获取verifier + Verifier verifier = certificatesManager.getVerifier(mchId); + WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create() + .withMerchant(mchId, mchSerialNo, merchantPrivateKey) + .withValidator(new WechatPay2Validator(verifier)); + // ... 接下来,你仍然可以通过builder设置各种参数,来配置你的HttpClient + + // 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新 + httpClient = builder.build(); + } + + @Deprecated + private void extracted() throws IOException { + privateKey = new String(Files.readAllBytes(Paths.get(privateKeyFilePath)), "utf-8"); + + String certKey = new String(Files.readAllBytes(Paths.get(certKeyFilePath)), "utf-8"); + + // 加载商户私钥(privateKey:私钥字符串) + merchantPrivateKey = PemUtil.loadPrivateKey(new ByteArrayInputStream(privateKey.getBytes("utf-8"))); + + // 加载商户公钥(privateKey:证书字符串) + merchantPublicKey = PemUtil.loadCertificate(new ByteArrayInputStream(certKey.getBytes("utf-8"))) + .getPublicKey(); + + // 加载平台证书(mchId:商户号,mchSerialNo:商户证书序列号,apiV3Key:V3密钥) + AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier( + new WechatPay2Credentials(mchId, new PrivateKeySigner(mchSerialNo, merchantPrivateKey)), + apiV3Key.getBytes("utf-8")); + + + // 初始化httpClient + httpClient = WechatPayHttpClientBuilder.create().withMerchant(mchId, mchSerialNo, merchantPrivateKey) + .withValidator(new WechatPay2Validator(verifier)).build(); + + WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create() + .withMerchant(mchId, mchSerialNo, merchantPrivateKey) + //设置响应对象无需签名 + .withValidator((response) -> true); + + httpNoSignPayClient = builder.build(); + + + } + + + /** + * 查询投诉详情 + * + * @param complaint_id + * 投诉单号 + * @return + * @throws Exception + */ + public String GetComplaintsInfo(String complaint_id) throws Exception { + + if (!isStep) { + errorHint = "未成功启用Step"; + return null; + } + + String result = null; + + // 请求URL + HttpGet httpGet = new HttpGet( + "https://api.mch.weixin.qq.com/v3/merchant-service/complaints-v2/" + complaint_id); + httpGet.setHeader("Accept", "application/json"); + + // 完成签名并执行请求 + CloseableHttpResponse response = httpClient.execute(httpGet); + + try { + int statusCode = response.getStatusLine().getStatusCode(); + if (statusCode == 200) { // 处理成功 + result = EntityUtils.toString(response.getEntity()); + } else if (statusCode == 204) { // 处理成功,无返回Body + result = "{'code':204}"; + } else { + result = EntityUtils.toString(response.getEntity()); + } + } finally { + response.close(); + } + + return result; + } + + /** + * 查询协商历史 + * + * @param complaint_id + * 投诉单号 + * @param offset + * 开始位置 + * @param limit + * 返回数据条数 + * @return + * @throws Exception + */ + public String GetComplaintsHis(String complaint_id, int offset, int limit) throws Exception { + + if (!isStep) { + errorHint = "未成功启用Step"; + return null; + } + + String result = null; + + // 请求URL + HttpGet httpGet = new HttpGet("https://api.mch.weixin.qq.com/v3/merchant-service/complaints-v2/" + complaint_id + + "/negotiation-historys?limit=" + limit + "&offset=" + offset); + httpGet.setHeader("Accept", "application/json"); + // 完成签名并执行请求 + CloseableHttpResponse response = httpClient.execute(httpGet); + + try { + int statusCode = response.getStatusLine().getStatusCode(); + if (statusCode == 200) { // 处理成功 + result = EntityUtils.toString(response.getEntity()); + } else if (statusCode == 204) { // 处理成功,无返回Body + result = "{'code':204}"; + } else { + result = EntityUtils.toString(response.getEntity()); + } + } finally { + response.close(); + } + + return result; + } + + /** + * 查询投诉单列表 + * + * @param offset + * 开始位置 + * @param limit + * 返回数据条数 + * @param begin_date + * 开始日期 yyyy-MM-dd + * @param end_date + * 结束日期 yyyy-MM-dd 时间差最大一个月 + * @return 查询结果 + * @throws Exception + */ + public String GetComplaintsList(int offset, int limit, String begin_date, String end_date) throws Exception { + + if (!isStep) { + errorHint = "未成功启用Step"; + return null; + } + + String result = null; + + // 请求URL + HttpGet httpGet = new HttpGet( + "https://api.mch.weixin.qq.com/v3/merchant-service/complaints-v2?limit=" + limit + "&offset=" + offset + + "&begin_date=" + begin_date + "&end_date=" + end_date + "&complainted_mchid=" + mchId); + httpGet.setHeader("Accept", "application/json"); + + // 完成签名并执行请求 + CloseableHttpResponse response = httpClient.execute(httpGet); + + try { + int statusCode = response.getStatusLine().getStatusCode(); + if (statusCode == 200) { // 处理成功 + result = EntityUtils.toString(response.getEntity()); + } else if (statusCode == 204) { // 处理成功,无返回Body + result = "{'code':204}"; + } else { + result = EntityUtils.toString(response.getEntity()); + } + } finally { + response.close(); + } + + return result; + } + + public void after() throws IOException { + httpClient.close(); + } + + /** + * 查询通知回调地址 + * @param + * @return + * @throws Exception + */ + public String queryComplaintsNotify() throws Exception { + + if (!isStep) { + errorHint = "未成功启用Step"; + return null; + } + + String result = null; + + URIBuilder uriBuilder = new URIBuilder("https://api.mch.weixin.qq.com/v3/merchant-service/complaint-notifications"); + // 请求URL + HttpGet httpGet = new HttpGet(uriBuilder.build()); + httpGet.addHeader("Accept", "application/json"); + + + // 完成签名并执行请求 + CloseableHttpResponse response = httpClient.execute(httpGet); + + try { + int statusCode = response.getStatusLine().getStatusCode(); + if (statusCode == 200) { // 处理成功 + result = EntityUtils.toString(response.getEntity()); + } else if (statusCode == 204) { // 处理成功,无返回Body + result = "{'code':204}"; + } else { + result = EntityUtils.toString(response.getEntity()); + } + } finally { + response.close(); + } + + return result; + } + + /** + * 创建投诉回调通知 + * + * @param url + * 回调地址 + * @return + * @throws Exception + */ + public String CreateComplaintsNotify(String url) throws Exception { + + if (!isStep) { + errorHint = "未成功启用Step"; + return null; + } + + String result = null; + + // 请求URL + HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/merchant-service/complaint-notifications"); + JSONObject dataJSON = new JSONObject(); + dataJSON.put("url", url); + + StringEntity entity = new StringEntity(dataJSON.toString()); + entity.setContentType("application/json"); + httpPost.setEntity(entity); + httpPost.setHeader("Accept", "application/json"); + + // 完成签名并执行请求 + CloseableHttpResponse response = httpClient.execute(httpPost); + + try { + int statusCode = response.getStatusLine().getStatusCode(); + if (statusCode == 200) { // 处理成功 + result = EntityUtils.toString(response.getEntity()); + } else if (statusCode == 204) { // 处理成功,无返回Body + result = "{'code':204}"; + } else { + result = EntityUtils.toString(response.getEntity()); + } + } finally { + response.close(); + } + + return result; + } + + /** + * 更新投诉回调通知 + * + * @param url + * 回调通知 + * @return + * @throws Exception + */ + public String UpdateComplaintsNotify(String url) throws Exception { + + if (!isStep) { + errorHint = "未成功启用Step"; + return null; + } + + String result = null; + + // 请求URL + HttpPut httpPut = new HttpPut("https://api.mch.weixin.qq.com/v3/merchant-service/complaint-notifications"); + JSONObject dataJSON = new JSONObject(); + dataJSON.put("url", url); + + StringEntity entity = new StringEntity(dataJSON.toString()); + entity.setContentType("application/json"); + httpPut.setEntity(entity); + httpPut.setHeader("Accept", "application/json"); + + // 完成签名并执行请求 + CloseableHttpResponse response = httpClient.execute(httpPut); + + try { + int statusCode = response.getStatusLine().getStatusCode(); + if (statusCode == 200) { // 处理成功 + result = EntityUtils.toString(response.getEntity()); + } else if (statusCode == 204) { // 处理成功,无返回Body + result = "{'code':204}"; + } else { + result = EntityUtils.toString(response.getEntity()); + } + } finally { + response.close(); + } + + return result; + + } + + /** + * 删除投诉回调通知地址 + * + * @return + * @throws Exception + */ + public String DelComplaintsNotify() throws Exception { + + if (!isStep) { + errorHint = "未成功启用Step"; + return null; + } + + String result = null; + + // 请求URL + HttpDelete httpDel = new HttpDelete( + "https://api.mch.weixin.qq.com/v3/merchant-service/complaint-notifications"); + httpDel.setHeader("Accept", "application/json"); + + // 完成签名并执行请求 + CloseableHttpResponse response = httpClient.execute(httpDel); + + try { + int statusCode = response.getStatusLine().getStatusCode(); + if (statusCode == 200) { // 处理成功 + result = EntityUtils.toString(response.getEntity()); + } else if (statusCode == 204) { // 处理成功,无返回Body + result = "{'code':204}"; + } else { + result = EntityUtils.toString(response.getEntity()); + } + } finally { + response.close(); + } + + return result; + } + + /** + * 提交回复 + * + * @param complaint_id + * 被投诉单号 + * @param responseJson + * 回复内容 + * @return + * @throws Exception + */ + public String ReplyInfo(String complaint_id, JSONObject responseJson) throws Exception { + + if (!isStep) { + errorHint = "未成功启用Step"; + return null; + } + + String result = null; + + // 请求URL + HttpPost httpPost = new HttpPost( + "https://api.mch.weixin.qq.com/v3/merchant-service/complaints-v2/" + complaint_id + "/response"); + // 请求body参数 + + responseJson.put("complainted_mchid", mchId); + + StringEntity entity = new StringEntity(responseJson.toString(),"UTF-8"); + entity.setContentType("application/json"); + httpPost.setEntity(entity); + httpPost.setHeader("Accept", "application/json"); + + // 完成签名并执行请求 + CloseableHttpResponse response = httpClient.execute(httpPost); + + try { + int statusCode = response.getStatusLine().getStatusCode(); + if (statusCode == 200) { // 处理成功 + result = EntityUtils.toString(response.getEntity()); + } else if (statusCode == 204) { // 处理成功,无返回Body + result = "{'code':204}"; + } else { + result = EntityUtils.toString(response.getEntity()); + } + } finally { + response.close(); + } + + return result; + } + + /** + * 反馈处理完成 + * + * @param complaint_id + * 投诉单号 + * @return + * @throws Exception + */ + public String CompleteComplaints(String complaint_id) throws Exception { + + if (!isStep) { + errorHint = "未成功启用Step"; + return null; + } + + String result = null; + + // 请求URL + HttpPost httpPost = new HttpPost( + "https://api.mch.weixin.qq.com/v3/merchant-service/complaints-v2/" + complaint_id + "/complete"); + JSONObject dataJSON = new JSONObject(); + dataJSON.put("complainted_mchid", mchId); + + StringEntity entity = new StringEntity(dataJSON.toString()); + entity.setContentType("application/json"); + httpPost.setEntity(entity); + httpPost.setHeader("Accept", "application/json"); + + // 完成签名并执行请求 + CloseableHttpResponse response = httpClient.execute(httpPost); + + try { + int statusCode = response.getStatusLine().getStatusCode(); + if (statusCode == 200) { // 处理成功 + result = EntityUtils.toString(response.getEntity()); + } else if (statusCode == 204) { // 处理成功,无返回Body + result = "{'code':204}"; + } else { + result = EntityUtils.toString(response.getEntity()); + } + } finally { + response.close(); + } + + return result; + } + + /** + * 上传图片 + * + * @param filePath + * 图片路径 + * @return + * @throws Exception + */ + public String uploadImg(String filePath) throws Exception { + + if (!isStep) { + errorHint = "未成功启用Step"; + return null; + } + + String result = null; + + URI uri = new URI("https://api.mch.weixin.qq.com/v3/merchant-service/images/upload"); + File file = new File(filePath); + + try (FileInputStream ins1 = new FileInputStream(file)) { + String sha256 = DigestUtils.sha256Hex(ins1); + try (InputStream ins2 = new FileInputStream(file)) { + HttpPost request = new WechatPayUploadHttpPost.Builder(uri).withImage(file.getName(), sha256, ins2) + .build(); + CloseableHttpResponse response = httpClient.execute(request); + try { + int statusCode = response.getStatusLine().getStatusCode(); + if (statusCode == 200) { // 处理成功 + result = EntityUtils.toString(response.getEntity()); + } else if (statusCode == 204) { // 处理成功,无返回Body + result = "{'code':204}"; + } else { + result = EntityUtils.toString(response.getEntity()); + } + } finally { + response.close(); + } + + } + } + + return result; + } + + /** + * 下载图片 + * + * @param media_url + * 图片路径 + * @return + * @throws Exception + */ + public BufferedImage downLoadImg(String media_url, String filePath) throws Exception { + + if (!isStep) { + errorHint = "未成功启用Step"; + return null; + } + + HttpGet httpGet = new HttpGet(media_url); + httpGet.setHeader("Accept", "application/json"); + + CloseableHttpResponse response = httpNoSignPayClient.execute(httpGet); + + BufferedImage result = null; + + try { + HttpEntity responseEntity = response.getEntity(); + InputStream content = responseEntity.getContent(); + result = ImageIO.read(content); + if(filePath != null && filePath.length() >= 0){ + File file = new File(filePath); + if(!file.exists()){ + file.createNewFile(); + } + FileOutputStream fileOutputStream = new FileOutputStream(file); + int bytesWritten = 0; + int byteCount = 0; + byte[] bytes = new byte[1024]; + while ((byteCount = content.read(bytes)) != -1) + { + fileOutputStream.write(bytes, bytesWritten, byteCount); + bytesWritten += byteCount; + } + content.close(); + fileOutputStream.close(); + } + } finally { + response.close(); + } + + return result; + } + + /** + * 发送请求 + * + * @param url + * 发送地址 + * @return + * @throws Exception + */ + public String doSendPostUrl(String url, String sendcontent) throws Exception { + + if (!isStep) { + errorHint = "未成功启用Step"; + return null; + } + + String result = null; + + // 请求URL + HttpPost httpPost = new HttpPost(url); + + StringEntity entity = new StringEntity(sendcontent, "utf-8"); + entity.setContentType("application/json"); + httpPost.setEntity(entity); + httpPost.setHeader("Accept", "application/json"); + + // 完成签名并执行请求 + CloseableHttpResponse response = httpClient.execute(httpPost); + + try { + int statusCode = response.getStatusLine().getStatusCode(); + if (statusCode == 200) { // 处理成功 + result = EntityUtils.toString(response.getEntity()); + } else if (statusCode == 204) { // 处理成功,无返回Body + result = "{'code':204}"; + } else { + result = EntityUtils.toString(response.getEntity()); + } + } finally { + response.close(); + } + + return result; + } + + public String doSendPostUrl(String url, String sendcontent, String Wechatpay_Serial) throws Exception { + + if (!isStep) { + errorHint = "未成功启用Step"; + return null; + } + + String result = null; + + // 请求URL + HttpPost httpPost = new HttpPost(url); + + StringEntity entity = new StringEntity(sendcontent, "utf-8"); + entity.setContentType("application/json"); + httpPost.setEntity(entity); + httpPost.setHeader("Accept", "application/json"); + httpPost.addHeader("Wechatpay-Serial", Wechatpay_Serial); + + // 完成签名并执行请求 + CloseableHttpResponse response = httpClient.execute(httpPost); + + try { + int statusCode = response.getStatusLine().getStatusCode(); + if (statusCode == 200) { // 处理成功 + result = EntityUtils.toString(response.getEntity()); + } else if (statusCode == 204) { // 处理成功,无返回Body + result = "{'code':204}"; + } else { + result = EntityUtils.toString(response.getEntity()); + } + } finally { + response.close(); + } + + return result; + } + + /** + * 发送请求 + * + * @param url + * 发送地址 + * @return + * @throws Exception + */ + public String doSendGETUrl(String url) throws Exception { + + if (!isStep) { + errorHint = "未成功启用Step"; + return null; + } + + String result = null; + + // 请求URL + HttpGet httpGet = new HttpGet(url); + httpGet.setHeader("Accept", "application/json"); + + // 完成签名并执行请求 + CloseableHttpResponse response = httpClient.execute(httpGet); + + try { + int statusCode = response.getStatusLine().getStatusCode(); + if (statusCode == 200) { // 处理成功 + result = EntityUtils.toString(response.getEntity()); + } else if (statusCode == 204) { // 处理成功,无返回Body + result = "{'code':204}"; + } else { + result = EntityUtils.toString(response.getEntity()); + } + } finally { + response.close(); + } + + return result; + } + + /** + * 发送请求 + * + * @param url + * 发送地址 + * @return + * @throws Exception + */ + public String doSendPostImages(String url, String filepath) throws Exception { + + if (!isStep) { + errorHint = "未成功启用Step"; + return null; + } + + String result = null; + + File file = new File(filepath); + URI uri = new URI(url); + + try (FileInputStream ins1 = new FileInputStream(file)) { + String sha256 = DigestUtils.sha256Hex(ins1); + try (InputStream ins2 = new FileInputStream(file)) { + HttpPost request = new WechatPayUploadHttpPost.Builder(uri).withImage(file.getName(), sha256, ins2) + .build(); + CloseableHttpResponse response = httpClient.execute(request); + try { + int statusCode = response.getStatusLine().getStatusCode(); + if (statusCode == 200) { // 处理成功 + result = EntityUtils.toString(response.getEntity()); + } else if (statusCode == 204) { // 处理成功,无返回Body + result = "{'code':204}"; + } else { + result = EntityUtils.toString(response.getEntity()); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + response.close(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } catch (Exception e) { + e.printStackTrace(); + } + + return result; + } + +} + diff --git a/src/main/java/com/greenorange/promotion/utils/paybank/WeChatAPIV3Manager b/src/main/java/com/greenorange/promotion/utils/paybank/WeChatAPIV3Manager new file mode 100644 index 0000000..cec5518 --- /dev/null +++ b/src/main/java/com/greenorange/promotion/utils/paybank/WeChatAPIV3Manager @@ -0,0 +1,68 @@ +package com.mx.util; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +public class WeChatAPIV3Manager { + static volatile WeChatAPIV3Manager weChatAPIV3Manager = null; + + ConcurrentHashMap weChatAPIV3Map = null; + + long lastClearTime = 0l; + + private WeChatAPIV3Manager(){ + weChatAPIV3Map = new ConcurrentHashMap<>(); + lastClearTime = System.currentTimeMillis() / 1000; + } + + public WeChatAPIV3 getWeChatApiV3(String mchid){ + long lCurTime = System.currentTimeMillis() / 1000; + WeChatAPIV3 weChatAPIV3 = weChatAPIV3Map.get(mchid); + if(weChatAPIV3 != null && lCurTime - weChatAPIV3.getLastUseTime() > 3 * 3600){ + weChatAPIV3Map.remove(mchid); + weChatAPIV3 = null; + } + if(weChatAPIV3 != null){ + weChatAPIV3.setLastUseTime(lCurTime); + } + clearNoUserObject(); + return weChatAPIV3; + } + + public void setWeChatAPIV3Map(String mchid,WeChatAPIV3 weChatAPIV3){ + if(weChatAPIV3 != null && weChatAPIV3.isStep()){ + weChatAPIV3.setLastUseTime(System.currentTimeMillis() / 1000); + weChatAPIV3Map.put(mchid,weChatAPIV3); + } + } + + public WeChatAPIV3 remove(String mchid){ + return weChatAPIV3Map.remove(mchid); + } + + private void clearNoUserObject(){ + long lCurTime = System.currentTimeMillis() / 1000; + if(weChatAPIV3Map == null || lCurTime - lastClearTime >= 3 * 3600){ + return; + } + Set> entries = weChatAPIV3Map.entrySet(); + for(Map.Entry entrie:entries){ + if(lCurTime - entrie.getValue().getLastUseTime() >= 3 * 3600){ + weChatAPIV3Map.remove(entrie.getKey()); + } + } + lastClearTime = lCurTime; + } + + public static WeChatAPIV3Manager getInstance(){ + if (weChatAPIV3Manager == null){ + synchronized (WeChatAPIV3Manager.class){ + if (weChatAPIV3Manager == null){ + weChatAPIV3Manager = new WeChatAPIV3Manager(); + } + } + } + return weChatAPIV3Manager; + } +} diff --git a/src/main/java/com/greenorange/promotion/utils/paybank/WxPayDataBuilder.java b/src/main/java/com/greenorange/promotion/utils/paybank/WxPayDataBuilder.java new file mode 100644 index 0000000..d24bbd0 --- /dev/null +++ b/src/main/java/com/greenorange/promotion/utils/paybank/WxPayDataBuilder.java @@ -0,0 +1,14 @@ +package com.greenorange.promotion.utils.paybank; + +import net.sf.json.JSONObject; + +/** + * v1.0.2 + * + * @author 周工 2020-06-01 + */ +interface WxPayDataBuilder { + boolean build() throws LackParamExceptions; + + JSONObject hand() throws LackParamExceptions; +} diff --git a/src/main/java/com/greenorange/promotion/utils/paybank/WxPayRefundBuilder.java b/src/main/java/com/greenorange/promotion/utils/paybank/WxPayRefundBuilder.java new file mode 100644 index 0000000..2da2850 --- /dev/null +++ b/src/main/java/com/greenorange/promotion/utils/paybank/WxPayRefundBuilder.java @@ -0,0 +1,271 @@ +package com.greenorange.promotion.utils.paybank; + +import java.util.Map; + +import net.sf.json.JSONObject; + +/** + * v1.0.2 + * + * @author 周工 2020-06-01 + */ +public class WxPayRefundBuilder implements WxPayDataBuilder { + + private static String sendUrl = "https://api.mch.weixin.qq.com/secapi/pay/refund"; + + private String appid, mch_id, transaction_id, out_refund_no, out_trade_no, refund_fee_type, refund_desc, sign, + API_KEY, nonce_str, notify_url, certPath, refund_account; + + private int total_fee, refund_fee; + private StringBuffer reStringBuffer; + private StringBuffer signStringBuffer; + private boolean build = false; + + public WxPayRefundBuilder(String certPath) { + nonce_str = WxPayUtil.getRandomStr(20); + this.certPath = certPath; + // 默认订单时间为半小时内有效 + out_refund_no = System.currentTimeMillis() + WxPayUtil.getRandomStr(8); + } + + public String getNotify_url() { + return notify_url; + } + + public void setNotify_url(String notify_url) { + this.notify_url = notify_url; + } + + public String getCertPath() { + return certPath; + } + + public void setCertPath(String certPath) { + this.certPath = certPath; + } + + public String getRefund_account() { + return refund_account; + } + + public void setRefund_account(String refund_account) { + this.refund_account = refund_account; + } + + public static String getSendUrl() { + return sendUrl; + } + + public static void setSendUrl(String sendUrl) { + WxPayRefundBuilder.sendUrl = sendUrl; + } + + public String getAppid() { + return appid; + } + + public void setAppid(String appid) { + this.appid = appid; + } + + public String getMch_id() { + return mch_id; + } + + public void setMch_id(String mch_id) { + this.mch_id = mch_id; + } + + public String getTransaction_id() { + return transaction_id; + } + + public void setTransaction_id(String transaction_id) { + this.transaction_id = transaction_id; + } + + public String getOut_refund_no() { + return out_refund_no; + } + + public void setOut_refund_no(String out_refund_no) { + this.out_refund_no = out_refund_no; + } + + public String getOut_trade_no() { + return out_trade_no; + } + + public void setOut_trade_no(String out_trade_no) { + this.out_trade_no = out_trade_no; + } + + public String getRefund_fee_type() { + return refund_fee_type; + } + + public void setRefund_fee_type(String refund_fee_type) { + this.refund_fee_type = refund_fee_type; + } + + public String getRefund_desc() { + return refund_desc; + } + + public void setRefund_desc(String refund_desc) { + this.refund_desc = refund_desc; + } + + public String getSign() { + return sign; + } + + public void setSign(String sign) { + this.sign = sign; + } + + public String getAPI_KEY() { + return API_KEY; + } + + public void setAPI_KEY(String aPI_KEY) { + API_KEY = aPI_KEY; + } + + public String getNonce_str() { + return nonce_str; + } + + public void setNonce_str(String nonce_str) { + this.nonce_str = nonce_str; + } + + public int getTotal_fee() { + return total_fee; + } + + public void setTotal_fee(int total_fee) { + this.total_fee = total_fee; + } + + public StringBuffer getReStringBuffer() { + return reStringBuffer; + } + + public void setReStringBuffer(StringBuffer reStringBuffer) { + this.reStringBuffer = reStringBuffer; + } + + public StringBuffer getSignStringBuffer() { + return signStringBuffer; + } + + public void setSignStringBuffer(StringBuffer signStringBuffer) { + this.signStringBuffer = signStringBuffer; + } + + public boolean isBuild() { + return build; + } + + public void setBuild(boolean build) { + this.build = build; + } + + public int getRefund_fee() { + return refund_fee; + } + + public void setRefund_fee(int refund_fee) { + this.refund_fee = refund_fee; + } + + @Override + public boolean build() throws LackParamExceptions { + build = false; + reStringBuffer = new StringBuffer(); + signStringBuffer = new StringBuffer(); + + reStringBuffer.append(""); + appendParam("appid", appid, true); + appendParam("mch_id", mch_id, true); + appendParam("nonce_str", nonce_str, true); + appendParam("notify_url", notify_url, false); + appendParam("out_refund_no", out_refund_no, true); + appendParam("out_trade_no", out_trade_no, false); + appendParam("refund_account", refund_account, false); + appendParam("refund_desc", refund_desc, false); + appendParam("refund_fee", refund_fee + "", true); + appendParam("refund_fee_type", refund_fee_type, false); + appendParam("sign_type", "MD5", false); + appendParam("total_fee", total_fee + "", true); + appendParam("transaction_id", transaction_id, false); + getSign(API_KEY, this.signStringBuffer.toString()); + appendParam("sign", sign.toUpperCase(), true); + + reStringBuffer.append(""); + + build = true; + + return build; + } + + private void getSign(String apikey, String value) throws LackParamExceptions { + if (apikey == null) { + throw new LackParamExceptions("商户号操作秘钥不能为空"); + } else { + sign = WxPayUtil.MD5(value + "key=" + API_KEY); + } + } + + private void appendParam(String key, String value, boolean isneed) throws LackParamExceptions { + + if (value == null) { + if (isneed) { + throw new LackParamExceptions("参数" + key + "不能为空"); + } else { + return; + } + } + this.reStringBuffer.append("<" + key + ">"); + this.reStringBuffer.append(""); + this.reStringBuffer.append(""); + this.signStringBuffer.append(key + "=" + value + "&"); + } + + @Override + public JSONObject hand() throws LackParamExceptions { + if (!build) { + throw new LackParamExceptions("未build成功,请先确认build成功后再运行"); + } + String result = ""; + JSONObject resultJson = new JSONObject(); + try { + result = WxPayUtil.doPostDataWithCert(sendUrl, reStringBuffer.toString(), mch_id, certPath); + + Map getResult = WxPayUtil.xmlToMap(result); + + if (getResult.get("result_code") == null || !"SUCCESS".equalsIgnoreCase(getResult.get("result_code"))) { + resultJson.put("return_code", "FAIL"); + resultJson.put("return_msg", getResult.get("return_msg")); + resultJson.put("err_code", getResult.get("err_code")); + resultJson.put("err_code_des", getResult.get("err_code_des")); + } else { + resultJson.put("return_code", "SUCCESS"); + resultJson.put("return_msg", getResult.get("return_msg")); + resultJson.put("err_code", getResult.get("err_code")); + resultJson.put("err_code_des", getResult.get("err_code_des")); + resultJson.put("out_refund_no", getResult.get("out_refund_no")); + resultJson.put("out_trade_no", getResult.get("out_trade_no")); + resultJson.put("refund_fee", getResult.get("refund_fee")); + resultJson.put("refund_id", getResult.get("refund_id")); + } + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + return resultJson; + } + +} diff --git a/src/main/java/com/greenorange/promotion/utils/paybank/WxPayRefundQueryBuilder.java b/src/main/java/com/greenorange/promotion/utils/paybank/WxPayRefundQueryBuilder.java new file mode 100644 index 0000000..02ba0db --- /dev/null +++ b/src/main/java/com/greenorange/promotion/utils/paybank/WxPayRefundQueryBuilder.java @@ -0,0 +1,211 @@ +package com.greenorange.promotion.utils.paybank; + +import java.util.Map; + +import net.sf.json.JSONObject; + +/** + * v1.0.2 + * + * @author 周工 2020-06-01 + */ +public class WxPayRefundQueryBuilder implements WxPayDataBuilder { + + private static String sendUrl = "https://api.mch.weixin.qq.com/pay/refundquery"; + + private String appid, mch_id, transaction_id, out_refund_no, out_trade_no, refund_id, sign, API_KEY, nonce_str; + + private StringBuffer reStringBuffer; + private StringBuffer signStringBuffer; + private boolean build = false; + + public WxPayRefundQueryBuilder() { + nonce_str = WxPayUtil.getRandomStr(20); + } + + public StringBuffer getReStringBuffer() { + return reStringBuffer; + } + + public void setReStringBuffer(StringBuffer reStringBuffer) { + this.reStringBuffer = reStringBuffer; + } + + public StringBuffer getSignStringBuffer() { + return signStringBuffer; + } + + public void setSignStringBuffer(StringBuffer signStringBuffer) { + this.signStringBuffer = signStringBuffer; + } + + public static String getSendUrl() { + return sendUrl; + } + + public static void setSendUrl(String sendUrl) { + WxPayRefundQueryBuilder.sendUrl = sendUrl; + } + + public String getAppid() { + return appid; + } + + public void setAppid(String appid) { + this.appid = appid; + } + + public String getMch_id() { + return mch_id; + } + + public void setMch_id(String mch_id) { + this.mch_id = mch_id; + } + + public String getTransaction_id() { + return transaction_id; + } + + public void setTransaction_id(String transaction_id) { + this.transaction_id = transaction_id; + } + + public String getOut_refund_no() { + return out_refund_no; + } + + public void setOut_refund_no(String out_refund_no) { + this.out_refund_no = out_refund_no; + } + + public String getOut_trade_no() { + return out_trade_no; + } + + public void setOut_trade_no(String out_trade_no) { + this.out_trade_no = out_trade_no; + } + + public String getRefund_id() { + return refund_id; + } + + public void setRefund_id(String refund_id) { + this.refund_id = refund_id; + } + + public String getSign() { + return sign; + } + + public void setSign(String sign) { + this.sign = sign; + } + + public String getAPI_KEY() { + return API_KEY; + } + + public void setAPI_KEY(String aPI_KEY) { + API_KEY = aPI_KEY; + } + + public String getNonce_str() { + return nonce_str; + } + + public void setNonce_str(String nonce_str) { + this.nonce_str = nonce_str; + } + + public boolean isBuild() { + return build; + } + + public void setBuild(boolean build) { + this.build = build; + } + + @Override + public boolean build() throws LackParamExceptions { + build = false; + reStringBuffer = new StringBuffer(); + signStringBuffer = new StringBuffer(); + + reStringBuffer.append(""); + appendParam("appid", appid, true); + appendParam("mch_id", mch_id, true); + appendParam("nonce_str", nonce_str, true); + appendParam("out_refund_no", out_refund_no, false); + appendParam("out_trade_no", out_trade_no, false); + appendParam("refund_id", refund_id, false); + appendParam("sign_type", "MD5", false); + appendParam("transaction_id", transaction_id, false); + getSign(API_KEY, this.signStringBuffer.toString()); + appendParam("sign", sign.toUpperCase(), true); + + reStringBuffer.append(""); + + build = true; + + return build; + } + + private void getSign(String apikey, String value) throws LackParamExceptions { + if (apikey == null) { + throw new LackParamExceptions("商户号操作秘钥不能为空"); + } else { + sign = WxPayUtil.MD5(value + "key=" + API_KEY); + } + } + + private void appendParam(String key, String value, boolean isneed) throws LackParamExceptions { + + if (value == null) { + if (isneed) { + throw new LackParamExceptions("参数" + key + "不能为空"); + } else { + return; + } + } + this.reStringBuffer.append("<" + key + ">"); + this.reStringBuffer.append(""); + this.reStringBuffer.append(""); + this.signStringBuffer.append(key + "=" + value + "&"); + } + + @Override + public JSONObject hand() throws LackParamExceptions { + if (!build) { + throw new LackParamExceptions("未build成功,请先确认build成功后再运行"); + } + String result = ""; + JSONObject resultJson = new JSONObject(); + try { + result = WxPayUtil.sendHttpsRequest(sendUrl, reStringBuffer.toString(), "text/xml", "utf-8", "POST"); + + Map getResult = WxPayUtil.xmlToMap(result); + + if (getResult.get("result_code") == null || !"SUCCESS".equalsIgnoreCase(getResult.get("result_code"))) { + resultJson.put("return_code", "FAIL"); + resultJson.put("return_msg", getResult.get("return_msg")); + resultJson.put("err_code", getResult.get("err_code")); + resultJson.put("err_code_des", getResult.get("err_code_des")); + } else { + resultJson.put("return_code", "SUCCESS"); + resultJson.put("return_msg", getResult.get("return_msg")); + resultJson.put("err_code", getResult.get("err_code")); + resultJson.put("err_code_des", getResult.get("err_code_des")); + resultJson.put("out_refund_no", getResult.get("out_refund_no")); + resultJson.put("refund_count", getResult.get("refund_count")); + resultJson.put("transaction_id", getResult.get("transaction_id")); + } + } catch (Exception e) { + e.printStackTrace(); + } + + return resultJson; + } + +} diff --git a/src/main/java/com/greenorange/promotion/utils/paybank/WxPayUtil.java b/src/main/java/com/greenorange/promotion/utils/paybank/WxPayUtil.java new file mode 100644 index 0000000..621918f --- /dev/null +++ b/src/main/java/com/greenorange/promotion/utils/paybank/WxPayUtil.java @@ -0,0 +1,389 @@ +package com.greenorange.promotion.utils.paybank; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.net.HttpURLConnection; +import java.net.URL; +import java.security.KeyStore; +import java.security.MessageDigest; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + + +import net.sf.json.JSONObject; +import net.sf.json.xml.XMLSerializer; +import org.apache.http.HttpEntity; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.conn.ssl.SSLContexts; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + + +/** + * v1.0.2 + * + * @author 周工 2020-06-01 + */ +public class WxPayUtil { + private static SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyyMMddHHmmss"); + + public static String getRandomStr(int length) { + String chars = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + + System.currentTimeMillis();/**/ + int maxPos = chars.length(); + String[] charsMore = chars.split(""); + StringBuffer noceStr = new StringBuffer(); + + for (int i = 0; i < length; i++) { + noceStr.append(charsMore[(int) Math.floor(Math.random() * maxPos)]); + } + return noceStr.toString(); // 随机数 + } + + public static String MD5(String str) { + + try { + MessageDigest messageDigest = MessageDigest.getInstance("MD5"); + byte[] bytes = messageDigest.digest(str.getBytes("utf-8")); + return byteArrayToHexString(bytes); + } catch (Exception e) { + e.printStackTrace(); + } + + return null; + } + + private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", + "e", "f" }; + + private static String byteToHexString(byte b) { + + int n = b; + if (n < 0) { + n += 256; + } + int d1 = n / 16; + int d2 = n % 16; + return hexDigits[d1] + hexDigits[d2]; + } + + public static Map xmlToMap(String xml) { + try { + Map data = new HashMap<>(); + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); + InputStream stream = new ByteArrayInputStream(xml.getBytes("UTF-8")); + org.w3c.dom.Document doc = documentBuilder.parse(stream); + doc.getDocumentElement().normalize(); + NodeList nodeList = doc.getDocumentElement().getChildNodes(); + for (int idx = 0; idx < nodeList.getLength(); ++idx) { + Node node = nodeList.item(idx); + if (node.getNodeType() == Node.ELEMENT_NODE) { + org.w3c.dom.Element element = (org.w3c.dom.Element) node; + data.put(element.getNodeName(), element.getTextContent()); + } + } + stream.close(); + return data; + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + private static String byteArrayToHexString(byte[] digest) { + StringBuffer result = new StringBuffer(); + for (byte b : digest) { + result.append(byteToHexString(b)); + } + return result.toString(); + } + + public static String FormatDate(long timeinmill) { + return sDateFormat.format(new Date(timeinmill)); + } + + public static JSONObject XmlParseJSON(String xml) { + XMLSerializer xmlSerializer = new XMLSerializer(); + String resutStr = xmlSerializer.read(xml).toString(); + JSONObject result = JSONObject.fromObject(resutStr); + + return result; + } + + public static String sendHttpsRequest(String url, String data, String sendType, String contentType, String methed) + throws IOException { + + if ("GET".equals(methed)) { + url = url.indexOf("?") >= 0 ? url + data : url + "?" + data; + } + if (sendType == null) { + sendType = "text/xml"; + } + if (contentType == null) { + contentType = "utf-8"; + } + + URL requestUrl = new URL(url); + HttpsURLConnection connection = (HttpsURLConnection) requestUrl.openConnection(); + connection.setDoInput(true); // 允许输入流,即允许下�? + connection.setDoOutput(true); // 允许输出流,即允许上�? + connection.setUseCaches(false); // 不使用缓�? + connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8"); + + if ("POST".equals(methed)) { + connection.setRequestProperty("Content-type", sendType); + connection.setRequestProperty("Cache-Control", "no-cache"); + connection.setRequestMethod("POST"); + + OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream()); + + out.write(new String(data.getBytes(contentType))); + out.flush(); + out.close(); + } else { + connection.setRequestMethod("GET"); // 使用get请求 + } + + InputStream is = connection.getInputStream(); // 获取输入流,此时才真正建立链�? + + StringBuffer stringBuffer = new StringBuffer(); + InputStreamReader isr = new InputStreamReader(is, "utf-8"); + BufferedReader bufferReader = new BufferedReader(isr); + String inputLine = ""; + + while ((inputLine = bufferReader.readLine()) != null) { + stringBuffer.append(inputLine); + } + + is.close(); + connection.disconnect(); + return stringBuffer.toString(); + } + + public static String sendHttpsRequest(String url, String data, String sendType, String contentType, String methed, + String Authorization) throws IOException { + + if ("GET".equals(methed)) { + url = url.indexOf("?") >= 0 ? url + data : url + "?" + data; + } + if (sendType == null) { + sendType = "text/xml"; + } + if (contentType == null) { + contentType = "utf-8"; + } + + URL requestUrl = new URL(url); + HttpsURLConnection connection = (HttpsURLConnection) requestUrl.openConnection(); + connection.setDoInput(true); // 允许输入流,即允许下�? + connection.setDoOutput(true); // 允许输出流,即允许上�? + connection.setUseCaches(false); // 不使用缓�? + connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8"); + connection.addRequestProperty("Authorization", Authorization); + + if ("POST".equals(methed)) { + // connection.setRequestProperty("Content-type", sendType); + connection.setRequestProperty("Cache-Control", "no-cache"); + connection.setRequestMethod("POST"); + + OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream()); + + out.write(new String(data.getBytes(contentType))); + out.flush(); + out.close(); + } else { + connection.setRequestMethod("GET"); // 使用get请求 + } + + InputStream is = connection.getInputStream(); // 获取输入流,此时才真正建立链�? + + StringBuffer stringBuffer = new StringBuffer(); + InputStreamReader isr = new InputStreamReader(is, "utf-8"); + BufferedReader bufferReader = new BufferedReader(isr); + String inputLine = ""; + + while ((inputLine = bufferReader.readLine()) != null) { + stringBuffer.append(inputLine); + } + + is.close(); + connection.disconnect(); + return stringBuffer.toString(); + } + + public static String sendHttpRequest(String url, String data, String sendType, String contentType, String methed) + throws IOException { + + if ("GET".equals(methed)) { + url = url.indexOf("?") >= 0 ? url + data : url + "?" + data; + } + if (sendType == null) { + sendType = "text/xml"; + } + if (contentType == null) { + contentType = "utf-8"; + } + + URL requestUrl = new URL(url); + HttpURLConnection connection = (HttpURLConnection) requestUrl.openConnection(); + connection.setDoInput(true); // 允许输入流,即允许下�? + connection.setDoOutput(true); // 允许输出流,即允许上�? + connection.setUseCaches(false); // 不使用缓�? + connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8"); + + if ("POST".equals(methed)) { + connection.setRequestProperty("Content-type", sendType); + connection.setRequestProperty("Cache-Control", "no-cache"); + connection.setRequestMethod("POST"); + + OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream()); + + out.write(new String(data.getBytes(contentType))); + out.flush(); + out.close(); + } else { + connection.setRequestMethod("GET"); // 使用get请求 + } + + InputStream is = connection.getInputStream(); // 获取输入流,此时才真正建立链�? + + StringBuffer stringBuffer = new StringBuffer(); + InputStreamReader isr = new InputStreamReader(is, "utf-8"); + BufferedReader bufferReader = new BufferedReader(isr); + String inputLine = ""; + + while ((inputLine = bufferReader.readLine()) != null) { + stringBuffer.append(inputLine); + } + + is.close(); + connection.disconnect(); + return stringBuffer.toString(); + } + + public static String httpClientResultGetPublicKey(String url, String xml, String mch_id, String path) + throws Exception { + + StringBuffer reultBuffer = new StringBuffer(); + + KeyStore keyStore = KeyStore.getInstance("PKCS12"); + + FileInputStream instream = new FileInputStream(new File(path)); + try { + + keyStore.load(instream, mch_id.toCharArray()); + } finally { + instream.close(); + } + + SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, mch_id.toCharArray()).build(); + + SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1", "TLSv1.2" }, null, + SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); + + HttpPost httpPost = new HttpPost(url); + CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build(); + StringEntity myEntity = new StringEntity(xml); + myEntity.setContentType("text/xml;charset=UTF-8"); + myEntity.setContentEncoding("utf-8"); + httpPost.setHeader("Content-Type", "text/xml; charset=UTF-8"); + httpPost.setEntity(myEntity); + + CloseableHttpResponse response = null; + InputStream inputStream = null; + InputStreamReader inputStreamReader = null; + BufferedReader bufferedReader = null; + try { + response = httpclient.execute(httpPost); + HttpEntity entity = response.getEntity(); + if (entity != null) { + inputStream = entity.getContent(); + inputStreamReader = new InputStreamReader(inputStream); + bufferedReader = new BufferedReader(inputStreamReader); + String str = null; + while ((str = bufferedReader.readLine()) != null) { + reultBuffer.append(str); + } + } + } catch (ClientProtocolException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + + httpclient.close(); + response.close(); + bufferedReader.close(); + inputStreamReader.close(); + inputStream.close(); + inputStream = null; + } + + return reultBuffer.toString(); + } + + public static String doPostDataWithCert(String url, String data, String mch_id, String filPath) throws Exception { + KeyStore keyStore = KeyStore.getInstance("PKCS12"); + FileInputStream instream = new FileInputStream(new File(filPath));// P12文件目录 + + try { + keyStore.load(instream, mch_id.toCharArray()); + } finally { + instream.close(); + } + + SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, mch_id.toCharArray())// 这里也是写密码的 + .build(); + + SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1", "TLSv1.2" }, null, + SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); + + CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build(); + + try { + HttpPost httpost = new HttpPost(url); // 设置响应头信息 + httpost.addHeader("Connection", "keep-alive"); + httpost.addHeader("Accept", "*/*"); + httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); + httpost.addHeader("Host", "api.mch.weixin.qq.com"); + httpost.addHeader("X-Requested-With", "XMLHttpRequest"); + httpost.addHeader("Cache-Control", "max-age=0"); + httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) "); + httpost.setEntity(new StringEntity(data, "UTF-8")); + CloseableHttpResponse response = httpclient.execute(httpost); + try { + HttpEntity entity = response.getEntity(); + + String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8"); + EntityUtils.consume(entity); + return jsonStr; + } finally { + response.close(); + } + } finally { + httpclient.close(); + } + } + +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 40063c0..f64c7a2 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,8 +1,5 @@ spring: datasource: - - - driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://8.130.119.119:3306/qingcheng?serverTimezone=Asia/Shanghai username: qingcheng @@ -55,5 +52,33 @@ mybatis-plus: +wx: + mini: + appId: wx3f968a09e31d6bed + appSecret: 0b23498d19665dc323efdd3ed5367041 + + pay: + #应用id(小程序id) + appId: wx61b63e27bddf4ea2 + #商户号 + merchantId: 1700326544 +# #商户API私钥 +# privateKeyPath: apiclient_key.pem + #商户证书序列号 + merchantSerialNumber: 6DC8953AB741D309920DA650B92F837BE38A2757 +# #商户APIv3密钥 +# apiV3Key: fbemuj4Xql7CYlQJAoTEPYxvPSNgYT2t + #通知地址 + notifyUrl: https://winning-mouse-internally.ngrok-free.app + #微信服务器地址 + domain: https://api.mch.weixin.qq.com + #商户APIv2密钥 + apiV2Key: cvsOH6TgbbdNUUqFJyLmWGaIEKoSqANg + #商户API证书 + certificatePath: static/apiclient_cert.p12 + + + + knife4j: enable: true \ No newline at end of file diff --git a/src/main/resources/lib/bcprov-ext-jdk15to18-165.jar b/src/main/resources/lib/bcprov-ext-jdk15to18-165.jar new file mode 100644 index 0000000..6d2c311 Binary files /dev/null and b/src/main/resources/lib/bcprov-ext-jdk15to18-165.jar differ diff --git a/src/main/resources/lib/commons-beanutils.jar b/src/main/resources/lib/commons-beanutils.jar new file mode 100644 index 0000000..b1b89c9 Binary files /dev/null and b/src/main/resources/lib/commons-beanutils.jar differ diff --git a/src/main/resources/lib/commons-codec-1.14.jar b/src/main/resources/lib/commons-codec-1.14.jar new file mode 100644 index 0000000..49ffa9b Binary files /dev/null and b/src/main/resources/lib/commons-codec-1.14.jar differ diff --git a/src/main/resources/lib/commons-collections-3.1.jar b/src/main/resources/lib/commons-collections-3.1.jar new file mode 100644 index 0000000..41e230f Binary files /dev/null and b/src/main/resources/lib/commons-collections-3.1.jar differ diff --git a/src/main/resources/lib/commons-lang-2.1.jar b/src/main/resources/lib/commons-lang-2.1.jar new file mode 100644 index 0000000..87b80ab Binary files /dev/null and b/src/main/resources/lib/commons-lang-2.1.jar differ diff --git a/src/main/resources/lib/commons-logging.jar b/src/main/resources/lib/commons-logging.jar new file mode 100644 index 0000000..8758a96 Binary files /dev/null and b/src/main/resources/lib/commons-logging.jar differ diff --git a/src/main/resources/lib/ezmorph-1.0.6.jar b/src/main/resources/lib/ezmorph-1.0.6.jar new file mode 100644 index 0000000..30fad12 Binary files /dev/null and b/src/main/resources/lib/ezmorph-1.0.6.jar differ diff --git a/src/main/resources/lib/httpclient-4.5.6.jar b/src/main/resources/lib/httpclient-4.5.6.jar new file mode 100644 index 0000000..56231de Binary files /dev/null and b/src/main/resources/lib/httpclient-4.5.6.jar differ diff --git a/src/main/resources/lib/httpcore-4.4.6.jar b/src/main/resources/lib/httpcore-4.4.6.jar new file mode 100644 index 0000000..16ed0d1 Binary files /dev/null and b/src/main/resources/lib/httpcore-4.4.6.jar differ diff --git a/src/main/resources/lib/jackson-annotations-2.9.3.jar b/src/main/resources/lib/jackson-annotations-2.9.3.jar new file mode 100644 index 0000000..b830738 Binary files /dev/null and b/src/main/resources/lib/jackson-annotations-2.9.3.jar differ diff --git a/src/main/resources/lib/jackson-core-2.9.3.jar b/src/main/resources/lib/jackson-core-2.9.3.jar new file mode 100644 index 0000000..40a04ce Binary files /dev/null and b/src/main/resources/lib/jackson-core-2.9.3.jar differ diff --git a/src/main/resources/lib/jackson-databind-2.9.3.jar b/src/main/resources/lib/jackson-databind-2.9.3.jar new file mode 100644 index 0000000..4cfc778 Binary files /dev/null and b/src/main/resources/lib/jackson-databind-2.9.3.jar differ diff --git a/src/main/resources/lib/javax.websocket-api-1.1.jar b/src/main/resources/lib/javax.websocket-api-1.1.jar new file mode 100644 index 0000000..2ad17a8 Binary files /dev/null and b/src/main/resources/lib/javax.websocket-api-1.1.jar differ diff --git a/src/main/resources/lib/json-lib-2.2.3-jdk13.jar b/src/main/resources/lib/json-lib-2.2.3-jdk13.jar new file mode 100644 index 0000000..a6513db Binary files /dev/null and b/src/main/resources/lib/json-lib-2.2.3-jdk13.jar differ diff --git a/src/main/resources/lib/log4j-1.2.17.jar b/src/main/resources/lib/log4j-1.2.17.jar new file mode 100644 index 0000000..1d425cf Binary files /dev/null and b/src/main/resources/lib/log4j-1.2.17.jar differ diff --git a/src/main/resources/lib/okhttp-3.2.0.jar b/src/main/resources/lib/okhttp-3.2.0.jar new file mode 100644 index 0000000..bd9daaf Binary files /dev/null and b/src/main/resources/lib/okhttp-3.2.0.jar differ diff --git a/src/main/resources/lib/slf4j-api-1.7.21.jar b/src/main/resources/lib/slf4j-api-1.7.21.jar new file mode 100644 index 0000000..2a5c33e Binary files /dev/null and b/src/main/resources/lib/slf4j-api-1.7.21.jar differ diff --git a/src/main/resources/lib/slf4j-log4j12-1.7.21.jar b/src/main/resources/lib/slf4j-log4j12-1.7.21.jar new file mode 100644 index 0000000..ff4fddd Binary files /dev/null and b/src/main/resources/lib/slf4j-log4j12-1.7.21.jar differ diff --git a/src/main/resources/lib/wechatpay-apache-httpclient-0.2.2.jar b/src/main/resources/lib/wechatpay-apache-httpclient-0.2.2.jar new file mode 100644 index 0000000..9324cfe Binary files /dev/null and b/src/main/resources/lib/wechatpay-apache-httpclient-0.2.2.jar differ diff --git a/src/main/resources/lib/xom-1.2.5.jar b/src/main/resources/lib/xom-1.2.5.jar new file mode 100644 index 0000000..1f71ab9 Binary files /dev/null and b/src/main/resources/lib/xom-1.2.5.jar differ diff --git a/src/main/resources/static/apiclient_cert.p12 b/src/main/resources/static/apiclient_cert.p12 new file mode 100644 index 0000000..2334989 Binary files /dev/null and b/src/main/resources/static/apiclient_cert.p12 differ