diff --git a/module-wechat/src/main/java/ink/wgink/module/wechat/enums/PayErrorMsgEnum.java b/module-wechat/src/main/java/ink/wgink/module/wechat/enums/PayErrorMsgEnum.java new file mode 100644 index 00000000..4a76f5a3 --- /dev/null +++ b/module-wechat/src/main/java/ink/wgink/module/wechat/enums/PayErrorMsgEnum.java @@ -0,0 +1,96 @@ +package ink.wgink.module.wechat.enums; + +/** + * @ClassName: PayErrorMsgEnum + * @Description: 支付错误码 + * @Author: wanggeng + * @Date: 2021/8/19 9:21 上午 + * @Version: 1.0 + */ +public enum PayErrorMsgEnum { + + USERPAYING_200(202, "USERPAYING", "用户支付中,需要输入密码", "等待5秒,然后调用被扫订单结果查询API,查询当前订单的不同状态,决定下一步的操作"), + + APPID_MCHID_NOT_MATCH_400(400, "APPID_MCHID_NOT_MATCH", "appid和mch_id不匹配", "请确认appid和mch_id是否匹配"), + PARAM_ERROR_400(400, "PARAM_ERROR", "业务规则限制", "请根据接口返回的详细信息检查请求参数"), + ORDER_CLOSED(400, "ORDER_CLOSED", "订单已关闭", "当前订单已关闭,请重新下单"), + MCH_NOT_EXISTS_400(400, "MCH_NOT_EXISTS", "商户号不存在", "请检查商户号是否正确"), + INVALID_REQUEST_400(400, "INVALID_REQUEST", "无效请求", "请根据接口返回的详细信息检查"), + PHONE_NOT_EXIST_400(400, "PHONE_NOT_EXIST", "手机号不存在", "请检查手机号码是否正确"), + AUTH_CODE_INVALID_400(400, "AUTH_CODE_INVALID", "收银员扫描的不是微信支付的条码", "请扫描微信支付被扫条码/二维码"), + NO_STATEMENT_EXIST_400(400, "NO_STATEMENT_EXIST", "账单文件不存在", "请检查当前商户号是否在指定日期有交易或退款发生"), + STATEMENT_CREATING_400(400, "STATEMENT_CREATING", "账单生成中", "请先检查当前商户号在指定日期内是否有成功的交易或退款,若有,则在T+1日上午8点后再重新下载"), + RESOURCE_ALREADY_EXISTS_400(400, "RESOURCE_ALREADY_EXISTS", "用户已签约该商户,不可重复签约", "请通过查询用户接口获取用户的签约信息"), + ALREADY_EXISTS_400(400, "ALREADY_EXISTS", "资源已存在", "尝试创建的资源已存在,无需重复创建"), + USER_NOT_REGISTERED_400(400, "USER_NOT_REGISTERED", "服务未开通或账号未注册", "该用户尚未注册或开通当前服务,请开通后再试"), + ORDER_PAID_400(400, "ORDER_PAID", "订单已支付", "请确认该订单号是否重复支付,如果是新单,请使用新订单号提交"), + ORDER_REVERSED_400(400, "ORDER_REVERSED", "订单已撤销", "当前订单状态为“订单已撤销”,请提示用户重新支付"), + ORDERCLOSED_400(400, "ORDERCLOSED", "订单已关闭", "商户订单号异常,请重新下单支付"), + ORDERPAID_400(400, "ORDERPAID", "订单已支付", "请确认该订单号是否重复支付,如果是新单,请使用新订单号提交"), + ORDERREVERSED_400(400, "ORDERREVERSED", "订单已撤销", "当前订单状态为“订单已撤销”,请提示用户重新支付"), + + SIGN_ERROR_401(401, "SIGN_ERROR", "签名错误", "请检查签名参数和方法是否都符合签名算法要求"), + + TRADE_ERROR_403(403, "TRADE_ERROR", "交易错误", "因业务原因交易失败,请查看接口返回的详细信息"), + OUT_TRADE_NO_USED_403(403, "OUT_TRADE_NO_USED", "商户订单号重复", "请核实商户订单号是否重复提交"), + ACCOUNT_ERROR_403(403, "ACCOUNT_ERROR", "账号异常", "用户账号异常,无需更多操作"), + RULE_LIMIT_403(403, "RULE_LIMIT", "业务规则限制", "因业务规则限制请求频率,请查看接口返回的详细信息"), + NOT_ENOUGH_403(403, "NOT_ENOUGH", "余额不足", "用户账号余额不足,请用户充值或更换支付卡后再支付"), + NO_AUTH_403(403, "NO_AUTH", "商户无权限", "请商户前往申请此接口相关权限"), + REQUEST_BLOCKED_403(403, "REQUEST_BLOCKED", "请求受阻", "此状态代表退款申请失败,商户可根据具体的错误提示做相应的处理"), + CONTRACT_NOT_EXIST_403(403, "CONTRACT_NOT_EXIST", "签约协议不存在", "请检查签约协议号是否正确,是否已解约"), + CONTRACT_EXISTED_403(403, "CONTRACT_EXISTED", "协议已存在", "已开通自动扣费服务功能,无需重复开通"), + AUTH_CODE_EXPIRE_403(403, "AUTH_CODE_EXPIRE", "用户的条码已经过期", "请收银员提示用户,请用户在微信上刷新条码,然后请收银员重新扫码。 直接将错误展示给收银员"), + USER_ACCOUNT_ABNORMAL_403(403, "USER_ACCOUNT_ABNORMAL", "用户账户异常", "该确认用户账号是否正常,商家可联系微信支付或让用户联系微信支付客服处理"), + CONTRACT_ERROR_403(403, "CONTRACT_ERROR", "当前用户签约状态失效", "请通过查询用户接口核实签约状态"), + CONTRACT_NOT_CONFIRMED_403(403, "CONTRACT_NOT_CONFIRMED", "二级商户未开启手动提现权限", "二级商户号提现权限已关闭,无法发起提现"), + ACCOUNT_NOT_VERIFIED_403(403, "ACCOUNT_NOT_VERIFIED", "二级商户下行打款未成功", "二级商户号结算银行卡信息有误,修改后重试"), + + REFUND_NOT_EXISTS_404(404, "REFUND_NOT_EXISTS", "订单号错误或订单状态不正确", "请检查订单号是否有误以及订单状态是否正确,如:未支付、已支付未退款"), + ORDER_NO_TEXIST_404(404, "ORDER_NO_TEXIST", "订单不存在", "请检查订单是否发起过交易"), + ORDER_NOT_EXIST_404(404, "ORDER_NOT_EXIST", "请求的资源不存在", "请商户检查需要查询的id或者请求URL是否正确"), + USER_NOT_EXIST_404(404, "USER_NOT_EXIST", "用户账户注销", "请检查用户账户是否正确"), + USER_NOT_EXISTS_404(404, "USER_NOT_EXISTS", "openid不正确", "请确认传入的openid是否正确"), + MCH_NOT_EXISTS_404(404, "MCH_NOT_EXISTS", "商户号不存在", "请确认传入的商户号是否正确"), + RESOURCE_NOT_EXISTS_404(404, "RESOURCE_NOT_EXISTS", "查询的资源不存在", "请检查查询资源的对应id是否填写正确"), + NOT_FOUND_404(404, "NOT_FOUND", "请求的资源不存在", "请商户检查需要查询的id或者请求URL是否正确"), + + FREQUENCY_LIMITED_429(429, "FREQUENCY_LIMITED", "频率超限", "请求量不要超过接口调用频率限制"), + RATELIMIT_EXCEEDED_429(429, "RATELIMIT_EXCEEDED", "频率限制", "请降低频率后重试"), + FREQUENCY_LIMIT_EXCEED_429(429, "FREQUENCY_LIMIT_EXCEED", "接口限频", "请降低调用频率"), + + ERROR_500(500, "ERROR", "业务错误", "该错误都会返回具体的错误原因,请根据实际返回做相应处理"), + OPENID_MISMATCH_500(500, "OPENID_MISMATCH", "openid和appid不匹配", "请确认openid和appid是否匹配"), + BANK_ERROR_500(500, "BANK_ERROR", "银行系统异常", "银行系统异常,请用相同参数重新调用"), + INVALID_TRANSACTIONID_500(500, "INVALID_TRANSACTIONID", "订单号非法", "请检查微信支付订单号是否正确"), + BIZ_ERR_NEED_RETRY_500(500, "BIZ_ERR_NEED_RETRY", "退款业务流程错误", "请不要更换商户退款单号,请使用相同参数再次调用API"), + SYSTEM_ERROR_500(500, "SYSTEM_ERROR", "系统错误", "系统异常,请用相同参数重新调用"); + + private int code; + private String errorCode; + private String summary; + private String solution; + + PayErrorMsgEnum(int code, String errorCode, String summary, String solution) { + this.code = code; + this.errorCode = errorCode; + this.summary = summary; + this.solution = solution; + } + + public int getCode() { + return code; + } + + public String getErrorCode() { + return errorCode == null ? "" : errorCode.trim(); + } + + public String getSummary() { + return summary == null ? "" : summary.trim(); + } + + public String getSolution() { + return solution == null ? "" : solution.trim(); + } +} diff --git a/module-wechat/src/main/java/ink/wgink/module/wechat/manager/pay/v3/miniapp/PayMiniappManager.java b/module-wechat/src/main/java/ink/wgink/module/wechat/manager/pay/v3/miniapp/PayMiniappManager.java new file mode 100644 index 00000000..dd3d558b --- /dev/null +++ b/module-wechat/src/main/java/ink/wgink/module/wechat/manager/pay/v3/miniapp/PayMiniappManager.java @@ -0,0 +1,25 @@ +package ink.wgink.module.wechat.manager.pay.v3.miniapp; + +/** + * @ClassName: PayMiniappManager + * @Description: 小程序支付管理 + * @Author: wanggeng + * @Date: 2021/8/19 11:10 上午 + * @Version: 1.0 + */ +public class PayMiniappManager { + + private static final PayMiniappManager payMiniappManager = PayMiniappManagerBuilder.payMiniappManager; + + private PayMiniappManager() { + } + + public PayMiniappManager getInstance() { + return payMiniappManager; + } + + private static class PayMiniappManagerBuilder { + public static PayMiniappManager payMiniappManager = new PayMiniappManager(); + } + +} diff --git a/module-wechat/src/main/java/ink/wgink/module/wechat/pojo/bos/pay/v3/PlaceOrderBO.java b/module-wechat/src/main/java/ink/wgink/module/wechat/pojo/bos/pay/v3/PlaceOrderBO.java new file mode 100644 index 00000000..491e8a20 --- /dev/null +++ b/module-wechat/src/main/java/ink/wgink/module/wechat/pojo/bos/pay/v3/PlaceOrderBO.java @@ -0,0 +1,494 @@ +package ink.wgink.module.wechat.pojo.bos.pay.v3; + +import ink.wgink.annotation.*; + +import java.util.List; + +/** + * @ClassName: PlaceOrderBO + * @Description: 下单 + * @Author: wanggeng + * @Date: 2021/8/19 1:52 下午 + * @Version: 1.0 + */ +public class PlaceOrderBO { + + @CheckEmptyAnnotation(name = "应用ID") + private String appid; + @CheckEmptyAnnotation(name = "直连商户号") + private String mchid; + @CheckEmptyAnnotation(name = "商品描述") + private String description; + @CheckEmptyAnnotation(name = "商户订单号") + private String outTradeNo; + /** + * 交易结束时间 + */ + private String timeExpire; + /** + * 附加数据 + */ + private String attach; + @CheckEmptyAnnotation(name = "通知地址") + private String notifyUrl; + /** + * 订单优惠标记 + */ + private String goodsTag; + @CheckNullAnnotation(name = "订单金额") + @CheckBeanAnnotation + private Amount amount; + @CheckNullAnnotation(name = "支付者") + @CheckBeanAnnotation + private Payer payer; + /** + * 优惠功能 + */ + @CheckBeanAnnotation + private Detail detail; + /** + * 场景信息 + */ + @CheckBeanAnnotation + private SceneInfo sceneInfo; + /** + * 结算信息 + */ + @CheckBeanAnnotation + private SettleInfo settleInfo; + + public String getAppid() { + return appid == null ? "" : appid.trim(); + } + + public void setAppid(String appid) { + this.appid = appid; + } + + public String getMchid() { + return mchid == null ? "" : mchid.trim(); + } + + public void setMchid(String mchid) { + this.mchid = mchid; + } + + public String getDescription() { + return description == null ? "" : description.trim(); + } + + public void setDescription(String description) { + this.description = description; + } + + public String getOutTradeNo() { + return outTradeNo == null ? "" : outTradeNo.trim(); + } + + public void setOutTradeNo(String outTradeNo) { + this.outTradeNo = outTradeNo; + } + + public String getTimeExpire() { + return timeExpire == null ? "" : timeExpire.trim(); + } + + public void setTimeExpire(String timeExpire) { + this.timeExpire = timeExpire; + } + + public String getAttach() { + return attach == null ? "" : attach.trim(); + } + + public void setAttach(String attach) { + this.attach = attach; + } + + public String getNotifyUrl() { + return notifyUrl == null ? "" : notifyUrl.trim(); + } + + public void setNotifyUrl(String notifyUrl) { + this.notifyUrl = notifyUrl; + } + + public String getGoodsTag() { + return goodsTag == null ? "" : goodsTag.trim(); + } + + public void setGoodsTag(String goodsTag) { + this.goodsTag = goodsTag; + } + + public Amount getAmount() { + return amount; + } + + public void setAmount(Amount amount) { + this.amount = amount; + } + + public Payer getPayer() { + return payer; + } + + public void setPayer(Payer payer) { + this.payer = payer; + } + + public Detail getDetail() { + return detail; + } + + public void setDetail(Detail detail) { + this.detail = detail; + } + + public SceneInfo getSceneInfo() { + return sceneInfo; + } + + public void setSceneInfo(SceneInfo sceneInfo) { + this.sceneInfo = sceneInfo; + } + + public SettleInfo getSettleInfo() { + return settleInfo; + } + + public void setSettleInfo(SettleInfo settleInfo) { + this.settleInfo = settleInfo; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("{"); + sb.append("\"appid\":\"") + .append(appid).append('\"'); + sb.append(",\"mchid\":\"") + .append(mchid).append('\"'); + sb.append(",\"description\":\"") + .append(description).append('\"'); + sb.append(",\"outTradeNo\":\"") + .append(outTradeNo).append('\"'); + sb.append(",\"timeExpire\":\"") + .append(timeExpire).append('\"'); + sb.append(",\"attach\":\"") + .append(attach).append('\"'); + sb.append(",\"notifyUrl\":\"") + .append(notifyUrl).append('\"'); + sb.append(",\"goodsTag\":\"") + .append(goodsTag).append('\"'); + sb.append(",\"amount\":") + .append(amount); + sb.append(",\"payer\":") + .append(payer); + sb.append(",\"detail\":") + .append(detail); + sb.append(",\"sceneInfo\":") + .append(sceneInfo); + sb.append(",\"settleInfo\":") + .append(settleInfo); + sb.append('}'); + return sb.toString(); + } + + /** + * 订单金额 + */ + public static class Amount { + @CheckNumberAnnotation(name = "总金额", min = 0) + private Integer total; + /** + * 货币类型 + */ + private String currency; + + public Integer getTotal() { + return total == null ? 0 : total; + } + + public void setTotal(Integer total) { + this.total = total; + } + + public String getCurrency() { + return currency == null ? "" : currency.trim(); + } + + public void setCurrency(String currency) { + this.currency = currency; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("{"); + sb.append("\"total\":") + .append(total); + sb.append(",\"currency\":\"") + .append(currency).append('\"'); + sb.append('}'); + return sb.toString(); + } + } + + /** + * 支付者 + */ + public static class Payer { + @CheckEmptyAnnotation(name = "用户标识") + private String openid; + + public String getOpenid() { + return openid == null ? "" : openid.trim(); + } + + public void setOpenid(String openid) { + this.openid = openid; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("{"); + sb.append("\"openid\":\"") + .append(openid).append('\"'); + sb.append('}'); + return sb.toString(); + } + } + + /** + * 优惠功能 + */ + public static class Detail { + /** + * 订单原价 + */ + private Integer costPrice; + /** + * 商品小票ID + */ + private String invoiceId; + @CheckListBeanAnnotation + private List goodsDetail; + + public Integer getCostPrice() { + return costPrice == null ? 0 : costPrice; + } + + public void setCostPrice(Integer costPrice) { + this.costPrice = costPrice; + } + + public String getInvoiceId() { + return invoiceId == null ? "" : invoiceId.trim(); + } + + public void setInvoiceId(String invoiceId) { + this.invoiceId = invoiceId; + } + + public List getGoodsDetail() { + return goodsDetail; + } + + public void setGoodsDetail(List goodsDetail) { + this.goodsDetail = goodsDetail; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("{"); + sb.append("\"costPrice\":") + .append(costPrice); + sb.append(",\"invoiceId\":\"") + .append(invoiceId).append('\"'); + sb.append(",\"goodsDetail\":") + .append(goodsDetail); + sb.append('}'); + return sb.toString(); + } + + public static class Goods { + @CheckEmptyAnnotation(name = "商户侧商品编码") + private String merchantGoodsId; + /** + * 微信侧商品编码 + */ + private String wechatpayGoodsId; + /** + * 商品名称 + */ + private String goodsName; + @CheckNumberAnnotation(name = "商品数量", min = 0) + private Integer quantity; + @CheckNumberAnnotation(name = "商品单价", min = 0) + private Integer unitPrice; + + public String getMerchantGoodsId() { + return merchantGoodsId == null ? "" : merchantGoodsId.trim(); + } + + public void setMerchantGoodsId(String merchantGoodsId) { + this.merchantGoodsId = merchantGoodsId; + } + + public String getWechatpayGoodsId() { + return wechatpayGoodsId == null ? "" : wechatpayGoodsId.trim(); + } + + public void setWechatpayGoodsId(String wechatpayGoodsId) { + this.wechatpayGoodsId = wechatpayGoodsId; + } + + public String getGoodsName() { + return goodsName == null ? "" : goodsName.trim(); + } + + public void setGoodsName(String goodsName) { + this.goodsName = goodsName; + } + + public Integer getQuantity() { + return quantity == null ? 0 : quantity; + } + + public void setQuantity(Integer quantity) { + this.quantity = quantity; + } + + public Integer getUnitPrice() { + return unitPrice == null ? 0 : unitPrice; + } + + public void setUnitPrice(Integer unitPrice) { + this.unitPrice = unitPrice; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("{"); + sb.append("\"merchantGoodsId\":\"") + .append(merchantGoodsId).append('\"'); + sb.append(",\"wechatpayGoodsId\":\"") + .append(wechatpayGoodsId).append('\"'); + sb.append(",\"goodsName\":\"") + .append(goodsName).append('\"'); + sb.append(",\"quantity\":") + .append(quantity); + sb.append(",\"unitPrice\":") + .append(unitPrice); + sb.append('}'); + return sb.toString(); + } + } + } + + /** + * 场景信息 + */ + public static class SceneInfo { + @CheckEmptyAnnotation(name = "用户终端IP") + private String payerClientIp; + /** + * 商户端设备号 + */ + private String deviceId; + + /** + * 商户门店信息 + */ + public static class StoreInfo { + @CheckEmptyAnnotation(name = "门店编号") + private String id; + /** + * 门店名称 + */ + private String name; + /** + * 地区编码 + */ + private String areaCode; + /** + * 详细地址 + */ + private String address; + + public String getId() { + return id == null ? "" : id.trim(); + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name == null ? "" : name.trim(); + } + + public void setName(String name) { + this.name = name; + } + + public String getAreaCode() { + return areaCode == null ? "" : areaCode.trim(); + } + + public void setAreaCode(String areaCode) { + this.areaCode = areaCode; + } + + public String getAddress() { + return address == null ? "" : address.trim(); + } + + public void setAddress(String address) { + this.address = address; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("{"); + sb.append("\"id\":\"") + .append(id).append('\"'); + sb.append(",\"name\":\"") + .append(name).append('\"'); + sb.append(",\"areaCode\":\"") + .append(areaCode).append('\"'); + sb.append(",\"address\":\"") + .append(address).append('\"'); + sb.append('}'); + return sb.toString(); + } + } + } + + /** + * 结算信息 + */ + public static class SettleInfo { + /** + * 是否指定分账 + */ + private Boolean profitSharing; + + public Boolean getProfitSharing() { + return profitSharing; + } + + public void setProfitSharing(Boolean profitSharing) { + this.profitSharing = profitSharing; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("{"); + sb.append("\"profitSharing\":") + .append(profitSharing); + sb.append('}'); + return sb.toString(); + } + } + +} diff --git a/module-wechat/src/main/java/ink/wgink/module/wechat/service/miniapp/IMiniappUserService.java b/module-wechat/src/main/java/ink/wgink/module/wechat/service/miniapp/IMiniappUserService.java index 4aeb3605..1ab95c51 100644 --- a/module-wechat/src/main/java/ink/wgink/module/wechat/service/miniapp/IMiniappUserService.java +++ b/module-wechat/src/main/java/ink/wgink/module/wechat/service/miniapp/IMiniappUserService.java @@ -100,4 +100,20 @@ public interface IMiniappUserService extends IMiniappUserBaseService { */ MiniappUserPO getPO(String appId, String openId); + /** + * 用户码获取用户列表 + * + * @param userCode 用户码 + * @return + */ + MiniappUserPO getPOByUserCode(String userCode); + + /** + * 用户码获取用户列表 + * + * @param userCodes 用户码列表 + * @return + */ + List listPOByUserCodes(List userCodes); + } diff --git a/module-wechat/src/main/java/ink/wgink/module/wechat/service/miniapp/impl/MiniappUserServiceImpl.java b/module-wechat/src/main/java/ink/wgink/module/wechat/service/miniapp/impl/MiniappUserServiceImpl.java index 2bdbb705..3f7ad38d 100644 --- a/module-wechat/src/main/java/ink/wgink/module/wechat/service/miniapp/impl/MiniappUserServiceImpl.java +++ b/module-wechat/src/main/java/ink/wgink/module/wechat/service/miniapp/impl/MiniappUserServiceImpl.java @@ -159,6 +159,20 @@ public class MiniappUserServiceImpl extends DefaultBaseService implements IMinia return getPO(params); } + @Override + public MiniappUserPO getPOByUserCode(String userCode) { + Map params = getHashMap(2); + params.put("userCode", userCode); + return getPO(params); + } + + @Override + public List listPOByUserCodes(List userCodes) { + Map params = getHashMap(2); + params.put("userCodes", userCodes); + return listPO(params); + } + private void updateUserCode(String userCode, String openId) { Map params = getHashMap(6); params.put("userCode", userCode); diff --git a/module-wechat/src/main/java/ink/wgink/module/wechat/service/official/account/IOfficialAccountUserService.java b/module-wechat/src/main/java/ink/wgink/module/wechat/service/official/account/IOfficialAccountUserService.java index d81af52e..caadbbb1 100644 --- a/module-wechat/src/main/java/ink/wgink/module/wechat/service/official/account/IOfficialAccountUserService.java +++ b/module-wechat/src/main/java/ink/wgink/module/wechat/service/official/account/IOfficialAccountUserService.java @@ -120,9 +120,17 @@ public interface IOfficialAccountUserService extends IOfficialAccountBaseService /** * 用户码获取用户 * - * @param userCode + * @param userCode 用户码 * @return */ OfficialAccountUserPO getPOByUserCode(String userCode); + /** + * 用户码获取用户列表 + * + * @param userCodes 用户码列表 + * @return + */ + List listPOByUserCodes(List userCodes); + } diff --git a/module-wechat/src/main/java/ink/wgink/module/wechat/service/official/account/impl/OfficialAccountUserServiceImpl.java b/module-wechat/src/main/java/ink/wgink/module/wechat/service/official/account/impl/OfficialAccountUserServiceImpl.java index 2990aca9..d5ec41bd 100644 --- a/module-wechat/src/main/java/ink/wgink/module/wechat/service/official/account/impl/OfficialAccountUserServiceImpl.java +++ b/module-wechat/src/main/java/ink/wgink/module/wechat/service/official/account/impl/OfficialAccountUserServiceImpl.java @@ -1,5 +1,7 @@ package ink.wgink.module.wechat.service.official.account.impl; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; import ink.wgink.common.base.DefaultBaseService; import ink.wgink.interfaces.consts.ISystemConstant; import ink.wgink.module.wechat.dao.official.account.IOfficialAccountUserDao; @@ -166,7 +168,10 @@ public class OfficialAccountUserServiceImpl extends DefaultBaseService implement @Override public SuccessResultList> listPage(ListPage page) { - return null; + PageHelper.startPage(page.getPage(), page.getRows()); + List officialAccountUserDTOs = officialAccountUserDao.list(page.getParams()); + PageInfo pageInfo = new PageInfo<>(officialAccountUserDTOs); + return new SuccessResultList<>(officialAccountUserDTOs, pageInfo.getPageNum(), pageInfo.getTotal()); } @Override @@ -182,6 +187,13 @@ public class OfficialAccountUserServiceImpl extends DefaultBaseService implement return getPO(params); } + @Override + public List listPOByUserCodes(List userCodes) { + Map params = getHashMap(2); + params.put("userCodes", userCodes); + return listPO(params); + } + /** * 更新用户码(邀请码) * diff --git a/module-wechat/src/main/java/ink/wgink/module/wechat/service/pay/v3/jsapi/IJsapiService.java b/module-wechat/src/main/java/ink/wgink/module/wechat/service/pay/v3/jsapi/IJsapiService.java new file mode 100644 index 00000000..a18610fa --- /dev/null +++ b/module-wechat/src/main/java/ink/wgink/module/wechat/service/pay/v3/jsapi/IJsapiService.java @@ -0,0 +1,11 @@ +package ink.wgink.module.wechat.service.pay.v3.jsapi; + +/** + * @ClassName: IJsapiService + * @Description: Jsapi业务 + * @Author: wanggeng + * @Date: 2021/8/19 10:42 上午 + * @Version: 1.0 + */ +public interface IJsapiService { +} diff --git a/module-wechat/src/main/java/ink/wgink/module/wechat/service/pay/v3/jsapi/impl/JsapiServiceImpl.java b/module-wechat/src/main/java/ink/wgink/module/wechat/service/pay/v3/jsapi/impl/JsapiServiceImpl.java new file mode 100644 index 00000000..da25bafd --- /dev/null +++ b/module-wechat/src/main/java/ink/wgink/module/wechat/service/pay/v3/jsapi/impl/JsapiServiceImpl.java @@ -0,0 +1,19 @@ +package ink.wgink.module.wechat.service.pay.v3.jsapi.impl; + +import ink.wgink.common.base.DefaultBaseService; +import ink.wgink.module.wechat.service.pay.v3.jsapi.IJsapiService; +import org.springframework.stereotype.Service; + +/** + * @ClassName: JsapiServiceImpl + * @Description: Jsapi业务 + * @Author: wanggeng + * @Date: 2021/8/19 10:42 上午 + * @Version: 1.0 + */ +@Service +public class JsapiServiceImpl extends DefaultBaseService implements IJsapiService { + + + +} diff --git a/module-wechat/src/main/java/ink/wgink/module/wechat/utils/pay/AbstractPayRequest.java b/module-wechat/src/main/java/ink/wgink/module/wechat/utils/pay/AbstractPayRequest.java new file mode 100644 index 00000000..78deef89 --- /dev/null +++ b/module-wechat/src/main/java/ink/wgink/module/wechat/utils/pay/AbstractPayRequest.java @@ -0,0 +1,209 @@ +package ink.wgink.module.wechat.utils.pay; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.core.io.FileSystemResource; +import org.springframework.http.*; +import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; + +import javax.crypto.Cipher; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.io.File; +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.Base64; + +/** + * @ClassName: PayRequestUtil + * @Description: 支付请求 + * @Author: wanggeng + * @Date: 2021/8/18 9:41 下午 + * @Version: 1.0 + */ +public abstract class AbstractPayRequest { + + public static final String PAY_SERIAL = "Wechatpay-Serial"; + private RestTemplate restTemplate; + + public AbstractPayRequest() { + SimpleClientHttpRequestFactory simpleClientHttpRequestFactory = new SimpleClientHttpRequestFactory(); + simpleClientHttpRequestFactory.setConnectTimeout(20 * 1000); + simpleClientHttpRequestFactory.setReadTimeout(60 * 1000); + this.restTemplate = new RestTemplate(simpleClientHttpRequestFactory); + } + + public abstract void response(ResponseEntity responseEntity); + + /** + * GET 请求 + * + * @param url 请求地址 + * @param authorization 授权信息 + * @param serialNumber 公钥证书序列号 + */ + public void get(String url, String authorization, String serialNumber) { + HttpEntity httpEntity = new HttpEntity(getHttpHeaders(authorization, serialNumber)); + ResponseEntity responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class); + response(responseEntity); + } + + /** + * POST 请求 + * + * @param url 请求地址 + * @param authorization 授权信息 + * @param serialNumber 公钥证书序列号 + * @param jsonBody 请求参数 + */ + public void post(String url, String authorization, String serialNumber, String jsonBody) { + HttpEntity httpEntity = new HttpEntity(jsonBody, getHttpHeaders(authorization, serialNumber)); + ResponseEntity responseEntity = restTemplate.exchange(url, HttpMethod.POST, httpEntity, String.class); + response(responseEntity); + } + + /** + * Put 请求 + * + * @param url 请求地址 + * @param authorization 授权信息 + * @param serialNumber 公钥证书序列号 + * @param jsonBody 请求参数 + */ + public void put(String url, String authorization, String serialNumber, String jsonBody) { + HttpEntity httpEntity = new HttpEntity(jsonBody, getHttpHeaders(authorization, serialNumber)); + ResponseEntity responseEntity = restTemplate.exchange(url, HttpMethod.PUT, httpEntity, String.class); + response(responseEntity); + } + + /** + * Delete 请求 + * + * @param url 请求地址 + * @param authorization 授权信息 + * @param serialNumber 公钥证书序列号 + */ + public void delete(String url, String authorization, String serialNumber) { + HttpEntity httpEntity = new HttpEntity(getHttpHeaders(authorization, serialNumber)); + ResponseEntity responseEntity = restTemplate.exchange(url, HttpMethod.DELETE, httpEntity, String.class); + response(responseEntity); + } + + /** + * 上传文件 + * + * @param url 请求地址 + * @param authorization 授权信息 + * @param serialNumber 公钥证书序列号 + * @param jsonData 请求参数 + * @param uploadFile 上传文件 + */ + public void upload(String url, String authorization, String serialNumber, String jsonData, File uploadFile) { + FileSystemResource fileSystemResource = new FileSystemResource(uploadFile); + MultiValueMap form = new LinkedMultiValueMap<>(); + form.add("file", fileSystemResource); + form.add("meta", jsonData); + HttpEntity> httpEntity = new HttpEntity<>(form, getUploadHeaders(authorization, serialNumber)); + ResponseEntity responseEntity = restTemplate.exchange(url, HttpMethod.POST, httpEntity, String.class); + response(responseEntity); + } + + /** + * 请求头 + * + * @param authorization 授权信息 + * @return + */ + private HttpHeaders getHttpBaseHeaders(String authorization) { + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.add("Accept", MediaType.APPLICATION_JSON_UTF8_VALUE); + httpHeaders.add("Authorization", authorization); + return httpHeaders; + } + + /** + * JSON请求头 + * + * @param authorization 授权信息 + * @param serialNumber 公钥证书序列号 + * @return + */ + private HttpHeaders getHttpHeaders(String authorization, String serialNumber) { + HttpHeaders httpHeaders = getHttpBaseHeaders(authorization); + httpHeaders.add("Content-Type", MediaType.APPLICATION_JSON_UTF8_VALUE); + if (!StringUtils.isBlank(serialNumber)) { + httpHeaders.add(PAY_SERIAL, serialNumber); + } + return httpHeaders; + } + + /** + * 上传文件请求头 + * + * @param authorization 授权信息 + * @param serialNumber 公钥证书序列号 + * @return + */ + private HttpHeaders getUploadHeaders(String authorization, String serialNumber) { + HttpHeaders httpHeaders = getHttpBaseHeaders(authorization); + httpHeaders.add("Content-Type", "multipart/form-data;boundary=\"boundary\""); + if (!StringUtils.isBlank(serialNumber)) { + httpHeaders.add(PAY_SERIAL, serialNumber); + } + return httpHeaders; + } + + /** + * Aes 解密 + */ + public static class AesUtil { + static final int KEY_LENGTH_BYTE = 32; + static final int TAG_LENGTH_BIT = 128; + private final byte[] aesKey; + + /** + * @param key APIv3秘钥 + */ + public AesUtil(byte[] key) { + if (key.length != KEY_LENGTH_BYTE) { + throw new IllegalArgumentException("无效的ApiV3Key,长度必须为32个字节"); + } + this.aesKey = key; + } + + /** + * 解密 + * + * @param associatedData 关联数据 + * @param nonce 加密使用的随机串初始化向量 + * @param ciphertext 密文 + * @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); + } + } + } + +} diff --git a/module-wechat/src/main/java/ink/wgink/module/wechat/utils/pay/PayCertificateUtil.java b/module-wechat/src/main/java/ink/wgink/module/wechat/utils/pay/PayCertificateUtil.java new file mode 100644 index 00000000..7d3e34c2 --- /dev/null +++ b/module-wechat/src/main/java/ink/wgink/module/wechat/utils/pay/PayCertificateUtil.java @@ -0,0 +1,53 @@ +package ink.wgink.module.wechat.utils.pay; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.security.cert.*; + +/** + * @ClassName: PayCertificateUtil + * @Description: 证书工具 + * @Author: wanggeng + * @Date: 2021/8/18 8:38 下午 + * @Version: 1.0 + */ +public class PayCertificateUtil { + + /** + * 获取证书 + * + * @param inputStream + * @return + */ + public static X509Certificate getCertificate(InputStream inputStream) { + try { + CertificateFactory cf = CertificateFactory.getInstance("X509"); + X509Certificate cert = (X509Certificate) cf.generateCertificate(inputStream); + cert.checkValidity(); + return cert; + } catch (CertificateExpiredException e) { + throw new RuntimeException("证书已过期", e); + } catch (CertificateNotYetValidException e) { + throw new RuntimeException("证书尚未生效", e); + } catch (CertificateException e) { + throw new RuntimeException("无效的证书", e); + } + } + + public static void main(String[] args) throws FileNotFoundException { + X509Certificate certificate = PayCertificateUtil.getCertificate(new FileInputStream(new File("/Users/wanggeng/tencent/tenlion/cert/1609461201_20210818_cert/apiclient_cert.pem"))); + + System.out.println("输出证书信息:\n" + certificate.toString()); + System.out.println("证书序列号:" + certificate.getSerialNumber().toString(16)); + System.out.println("版本号:" + certificate.getVersion()); + System.out.println("签发者:" + certificate.getIssuerDN()); + System.out.println("有效起始日期:" + certificate.getNotBefore()); + System.out.println("有效终止日期:" + certificate.getNotAfter()); + System.out.println("主体名:" + certificate.getSubjectDN()); + System.out.println("签名算法:" + certificate.getSigAlgName()); + System.out.println("签名:" + certificate.getSignature().toString()); + } + +} diff --git a/module-wechat/src/main/java/ink/wgink/module/wechat/utils/pay/PayPrivateKeyUtil.java b/module-wechat/src/main/java/ink/wgink/module/wechat/utils/pay/PayPrivateKeyUtil.java new file mode 100644 index 00000000..0e4f69bb --- /dev/null +++ b/module-wechat/src/main/java/ink/wgink/module/wechat/utils/pay/PayPrivateKeyUtil.java @@ -0,0 +1,109 @@ +package ink.wgink.module.wechat.utils.pay; + +import ink.wgink.interfaces.consts.ISystemConstant; +import org.bouncycastle.util.encoders.Base64; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.Signature; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; + +/** + * @ClassName: PayPrivateKeyUtil + * @Description: 商户私钥工具 + * @Author: wanggeng + * @Date: 2021/8/18 8:43 下午 + * @Version: 1.0 + */ +public class PayPrivateKeyUtil { + + /** + * 算法 + */ + public static final String KEY_ALGORITHM = "RSA"; + + /** + * 获取商户私钥 + * + * @param keyPath 商户私钥证书路径 + * @return 商户私钥 + * @throws Exception 解析 key 异常 + */ + public static String getPrivateKey(String keyPath) throws Exception { + BufferedReader bufferedReader = new BufferedReader(new FileReader(keyPath)); + StringBuilder apiClientKeySB = new StringBuilder(); + for (String line; (line = bufferedReader.readLine()) != null; ) { + apiClientKeySB.append(line); + } + String originalKey = apiClientKeySB.toString(); + System.out.println(originalKey); + String privateKey = originalKey + .replace("-----BEGIN PRIVATE KEY-----", "") + .replace("-----END PRIVATE KEY-----", "") + .replaceAll("\\s+", ""); + return PayPrivateKeyUtil.getPrivateKeyStr(PayPrivateKeyUtil.loadPrivateKey(privateKey)); + } + + + /** + * 私钥字符串 + * + * @param privateKey + * @return + * @throws UnsupportedEncodingException + */ + public static String getPrivateKeyStr(PrivateKey privateKey) throws UnsupportedEncodingException { + return new String(Base64.encode(privateKey.getEncoded()), ISystemConstant.CHARSET_UTF8); + } + + + /** + * 从字符串中加载私钥 + * + * @param privateKeyStr 私钥 + * @return {@link PrivateKey} + * @throws Exception 异常信息 + */ + public static PrivateKey loadPrivateKey(String privateKeyStr) throws Exception { + try { + byte[] buffer = Base64.decode(privateKeyStr); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + return keyFactory.generatePrivate(keySpec); + } catch (NoSuchAlgorithmException e) { + throw new Exception("无此算法"); + } catch (InvalidKeySpecException e) { + throw new Exception("私钥非法"); + } catch (NullPointerException e) { + throw new Exception("私钥数据为空"); + } + } + + /** + * 私钥签名 + * + * @param data 需要加密的数据 + * @param privateKey 私钥 + * @return 加密后的数据 + * @throws Exception 异常信息 + */ + public static String encryptByPrivateKey(String data, String privateKey) throws Exception { + PKCS8EncodedKeySpec priPkcs8 = new PKCS8EncodedKeySpec(Base64.decode(privateKey)); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + PrivateKey priKey = keyFactory.generatePrivate(priPkcs8); + java.security.Signature signature = Signature.getInstance("SHA256WithRSA"); + + signature.initSign(priKey); + signature.update(data.getBytes(StandardCharsets.UTF_8)); + byte[] signed = signature.sign(); + return new String(Base64.encode(signed), ISystemConstant.CHARSET_UTF8); + } + + +} diff --git a/module-wechat/src/main/java/ink/wgink/module/wechat/utils/pay/PaySignAuthorizationUtil.java b/module-wechat/src/main/java/ink/wgink/module/wechat/utils/pay/PaySignAuthorizationUtil.java new file mode 100644 index 00000000..30fed201 --- /dev/null +++ b/module-wechat/src/main/java/ink/wgink/module/wechat/utils/pay/PaySignAuthorizationUtil.java @@ -0,0 +1,124 @@ +package ink.wgink.module.wechat.utils.pay; + +import ink.wgink.interfaces.consts.ISystemConstant; +import org.springframework.web.bind.annotation.RequestMethod; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.*; + +/** + * @ClassName: PaySignAuthorizationUtil + * @Description: 支付签名和认证工具 + * @Author: wanggeng + * @Date: 2021/8/18 5:45 下午 + * @Version: 1.0 + */ +public class PaySignAuthorizationUtil { + + // 商户号:1609461201 + // APIv3秘钥:_TSKJ_0471_TSkj_0471_tsKJ_0471__ + + /** + * @param requestMethod 请求方法 + * @param url 请求接口 + * @param timestamp 时间戳 + * @param nonceStr 随机字符串 + * @param body 请求主体 + * @return + */ + public static String getSign(RequestMethod requestMethod, String url, long timestamp, String nonceStr, String body) { + return new StringBuilder() + .append(requestMethod.toString()) + .append("\n") + .append(url) + .append("\n") + .append(timestamp) + .append("\n") + .append(nonceStr) + .append("\n") + .append(body) + .append("\n") + .toString(); + } + + /** + * @param method 方法 + * @param urlSuffix 可通过 WxApiType 来获取,URL挂载参数需要自行拼接 + * @param mchId 商户Id + * @param serialNo 商户 API 证书序列号 + * @param keyPath key.pem 证书路径 + * @param body 接口请求参数 + * @param nonceStr 随机字符库 + * @param timestamp 时间戳 + * @param authType 认证类型 + * @return + * @throws Exception + */ + public static String buildAuthorization(RequestMethod method, String urlSuffix, String mchId, + String serialNo, String keyPath, String body, String nonceStr, + long timestamp, String authType) throws Exception { + // 构建签名参数 + String buildSignMessage = getSign(method, urlSuffix, timestamp, nonceStr, body); + // 获取商户私钥 + String key = PayPrivateKeyUtil.getPrivateKey(keyPath); + // 生成签名 + String signature = PayPrivateKeyUtil.encryptByPrivateKey(buildSignMessage, key); + // 根据平台规则生成请求头 authorization + return getAuthorization(mchId, serialNo, nonceStr, String.valueOf(timestamp), signature, authType); + } + + /** + * @param mchId 方法 + * @param serialNo 商户 API 证书序列号 + * @param nonceStr 随机字符库 + * @param timestamp 时间戳 + * @param signature 签名值 + * @param authType 认证类型 + * @return + */ + private static String getAuthorization(String mchId, String serialNo, String nonceStr, String timestamp, String signature, String authType) throws UnsupportedEncodingException { + Map params = new HashMap<>(5); + params.put("mchid", mchId); + params.put("serial_no", serialNo); + params.put("nonce_str", nonceStr); + params.put("timestamp", timestamp); + params.put("signature", signature); + return authType.concat(" ").concat(createLinkString(params, ",", false, true)); + } + + /** + * 链接字符串 + * + * @param params map + * @param connStr 链接值 + * @param encode 是否编码 + * @param quotes 是否加引号 + * @return + */ + private static String createLinkString(Map params, String connStr, boolean encode, boolean quotes) throws UnsupportedEncodingException { + List keys = new ArrayList<>(params.keySet()); + Collections.sort(keys); + StringBuilder content = new StringBuilder(); + for (int i = 0; i < keys.size(); i++) { + String key = keys.get(i); + String value = params.get(key); + // 拼接时,不包括最后一个&字符 + if (i == keys.size() - 1) { + if (quotes) { + content.append(key).append("=").append('"').append(encode ? URLEncoder.encode(value, ISystemConstant.CHARSET_UTF8) : value).append('"'); + } else { + content.append(key).append("=").append(encode ? URLEncoder.encode(value, ISystemConstant.CHARSET_UTF8) : value); + } + } else { + if (quotes) { + content.append(key).append("=").append('"').append(encode ? URLEncoder.encode(value, ISystemConstant.CHARSET_UTF8) : value).append('"').append(connStr); + } else { + content.append(key).append("=").append(encode ? URLEncoder.encode(value, ISystemConstant.CHARSET_UTF8) : value).append(connStr); + } + } + } + return content.toString(); + } + +} diff --git a/module-wechat/src/main/java/ink/wgink/module/wechat/utils/pay/PayVerifyUtil.java b/module-wechat/src/main/java/ink/wgink/module/wechat/utils/pay/PayVerifyUtil.java new file mode 100644 index 00000000..94277f48 --- /dev/null +++ b/module-wechat/src/main/java/ink/wgink/module/wechat/utils/pay/PayVerifyUtil.java @@ -0,0 +1,56 @@ +package ink.wgink.module.wechat.utils.pay; + +import org.bouncycastle.util.encoders.Base64; + +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.security.PublicKey; +import java.security.cert.X509Certificate; + +/** + * @ClassName: PayVerifyUtil + * @Description: 验证工具 + * @Author: wanggeng + * @Date: 2021/8/18 11:20 下午 + * @Version: 1.0 + */ +public class PayVerifyUtil { + + /** + * 验证签名 + * + * @param signature + * @param body + * @param nonce + * @param timestamp + * @param certInputStream + * @return + * @throws Exception + */ + public static boolean verifySignature(String signature, String body, String nonce, String timestamp, InputStream certInputStream) throws Exception { + String buildSignMessage = buildSignMessage(timestamp, nonce, body); + // 获取证书 + X509Certificate certificate = PayCertificateUtil.getCertificate(certInputStream); + PublicKey publicKey = certificate.getPublicKey(); + return checkByPublicKey(buildSignMessage, signature, publicKey); + } + + public static boolean checkByPublicKey(String data, String sign, PublicKey publicKey) throws Exception { + java.security.Signature signature = java.security.Signature.getInstance("SHA256WithRSA"); + signature.initVerify(publicKey); + signature.update(data.getBytes(StandardCharsets.UTF_8)); + return signature.verify(Base64.decode(sign.getBytes(StandardCharsets.UTF_8))); + } + + public static String buildSignMessage(String timestamp, String nonceStr, String body) { + return new StringBuilder() + .append(timestamp) + .append("\n") + .append(nonceStr) + .append("\n") + .append(body) + .append("\n") + .toString(); + } + +} diff --git a/module-wechat/src/main/java/ink/wgink/module/wechat/utils/pay/SensitiveDataUtil.java b/module-wechat/src/main/java/ink/wgink/module/wechat/utils/pay/SensitiveDataUtil.java new file mode 100644 index 00000000..f304b3a3 --- /dev/null +++ b/module-wechat/src/main/java/ink/wgink/module/wechat/utils/pay/SensitiveDataUtil.java @@ -0,0 +1,74 @@ +package ink.wgink.module.wechat.utils.pay; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; +import java.util.Base64; + +/** + * @ClassName: SensitiveDataUtil + * @Description: 敏感信息工具 + * @Author: wanggeng + * @Date: 2021/8/18 11:54 下午 + * @Version: 1.0 + */ +public class SensitiveDataUtil { + + /** + * 加密 + * + * @param message 加密内容 + * @param certificate 证书 + * @return + * @throws IllegalBlockSizeException + * @throws IOException + */ + public static String rsaEncryptOAEP(String message, X509Certificate certificate) throws IllegalBlockSizeException, IOException { + try { + Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding"); + cipher.init(Cipher.ENCRYPT_MODE, certificate.getPublicKey()); + + 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字节"); + } + } + + /** + * 解密 + * + * @param ciphertext 密文 + * @param privateKey 私钥 + * @return + * @throws BadPaddingException + * @throws IOException + */ + public static String rsaDecryptOAEP(String ciphertext, PrivateKey privateKey) throws BadPaddingException, IOException { + try { + Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding"); + cipher.init(Cipher.DECRYPT_MODE, privateKey); + + 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("解密失败"); + } + } + +} diff --git a/module-wechat/src/main/resources/mybatis/mapper/miniapp-user-mapper.xml b/module-wechat/src/main/resources/mybatis/mapper/miniapp-user-mapper.xml index 797546cc..83df6f86 100644 --- a/module-wechat/src/main/resources/mybatis/mapper/miniapp-user-mapper.xml +++ b/module-wechat/src/main/resources/mybatis/mapper/miniapp-user-mapper.xml @@ -121,7 +121,14 @@ user_id IN - #{userIds[${index}]} + #{userIds[${index}]} + + + + AND + user_code IN + + #{userCodes[${index}]} @@ -152,6 +159,10 @@ AND user_id = #{userId} + + AND + user_code = #{userCode} + diff --git a/module-wechat/src/main/resources/mybatis/mapper/official-account-user-mapper.xml b/module-wechat/src/main/resources/mybatis/mapper/official-account-user-mapper.xml index eb8bc125..908e113f 100644 --- a/module-wechat/src/main/resources/mybatis/mapper/official-account-user-mapper.xml +++ b/module-wechat/src/main/resources/mybatis/mapper/official-account-user-mapper.xml @@ -220,6 +220,13 @@ status = #{status} + + AND + user_code IN + + #{userCodes[${index}]} + +